| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import { ipcMain, BrowserWindow } from 'electron';
- import { exec } from 'child_process';
- import { promisify } from 'util';
- import path from 'path';
- import { fileURLToPath } from 'url';
- import { getCachedAdbPath, loadConfig } from '../config.js';
- const __filename = fileURLToPath(import.meta.url);
- const __dirname = path.dirname(__filename);
- const execAsync = promisify(exec);
- // 获取主窗口引用(用于实时推送设备发现事件)
- function getMainWindow() {
- const windows = BrowserWindow.getAllWindows();
- return windows.length > 0 ? windows[0] : null;
- }
- // 获取 ADB 设备列表(每次都重新扫描网络)
- export async function getADBDevices() {
- try {
- // 每次都重新扫描网络设备,不记录已连接的设备
- return await scanNetworkDevices(null);
- } catch (error) {
- return [];
- }
- }
- // 网络扫描:按顺序扫描 192.168.0、192.168.1、192.168.2 三个网段,尝试连接设备(实时推送结果)
- export async function scanNetworkDevices(event) {
- const adbPath = getCachedAdbPath();
- const port = 5555;
- const maxConcurrent = 50; // 限制并发数,避免过载
- const connectTimeout = 1500; // 连接超时时间(毫秒)
-
- const foundDevices = new Set(); // 使用 Set 避免重复
-
- // 定义要扫描的网段(按顺序:0.0 -> 1.0 -> 2.0)
- const networkSegments = ['192.168.0', '192.168.1', '192.168.2'];
-
- // 推送设备发现的辅助函数
- const pushDevice = (ipPort) => {
- if (!foundDevices.has(ipPort)) {
- foundDevices.add(ipPort);
- const device = {
- id: ipPort,
- status: 'device'
- };
- // 实时发送发现的设备
- if (event && event.sender) {
- event.sender.send('device-found', device);
- } else {
- const mainWindow = getMainWindow();
- if (mainWindow) {
- mainWindow.webContents.send('device-found', device);
- }
- }
- }
- };
-
- // 扫描单个 IP 地址的函数
- const scanIP = async (ip) => {
- const ipPort = `${ip}:${port}`;
- try {
- // 先尝试连接设备
- await execAsync(`${adbPath} connect ${ipPort}`, {
- timeout: connectTimeout,
- maxBuffer: 1024 * 1024
- });
-
- // 连接后稍等片刻,让设备注册到 ADB 服务器
- await new Promise(resolve => setTimeout(resolve, 500));
- } catch (error) {
- // 连接失败是正常的,继续检查设备列表
- }
-
- // 调用 adb devices 命令获取所有设备列表,检查是否包含端口 5555 的设备
- try {
- const { stdout } = await execAsync(`${adbPath} devices`, {
- timeout: 2000,
- maxBuffer: 1024 * 1024
- });
-
- // 检查输出中是否包含该 IP:PORT(匹配端口 5555)
- // 使用正则表达式匹配 IP:5555 格式
- const devicePattern = new RegExp(`${ip.replace(/\./g, '\\.')}:${port}\\s+device`, 'i');
- if (devicePattern.test(stdout)) {
- // 发现设备,立即推送显示(不等待整批完成)
- pushDevice(ipPort);
- return ipPort;
- }
- // 如果没有匹配到,说明没有设备
- } catch (checkError) {
- // 检查失败,忽略
- }
- return null;
- };
-
- // 按顺序扫描每个网段
- for (const baseIP of networkSegments) {
- // 生成当前网段的 IP 地址列表
- const ipList = [];
- for (let i = 1; i <= 255; i++) {
- ipList.push(`${baseIP}.${i}`);
- }
-
- // 分批并发扫描当前网段,扫描到一个显示一个
- for (let i = 0; i < ipList.length; i += maxConcurrent) {
- const batch = ipList.slice(i, i + maxConcurrent);
- const promises = batch.map(ip => scanIP(ip));
-
- // 并发执行当前批次,但不等待所有完成才推送
- // 每个 promise 一旦发现设备就会立即推送
- await Promise.all(promises);
- }
- }
-
- // 返回所有发现的设备
- return Array.from(foundDevices).map(ipPort => ({
- id: ipPort,
- status: 'device'
- }));
- }
- // 连接 ADB 设备
- export async function connectDevice(ipPort) {
- try {
- const adbPath = getCachedAdbPath();
-
- // 尝试连接设备
- try {
- await execAsync(`${adbPath} connect ${ipPort}`, {
- timeout: 3000,
- maxBuffer: 1024 * 1024
- });
- } catch (connectError) {
- // 连接命令失败
- return { success: false, error: connectError.message || '连接失败' };
- }
-
- // 连接后稍等片刻,让设备注册到 ADB 服务器
- await new Promise(resolve => setTimeout(resolve, 1000));
-
- // 验证设备是否真的连接成功(检查设备列表)
- try {
- const { stdout } = await execAsync(`${adbPath} devices`, {
- timeout: 2000,
- maxBuffer: 1024 * 1024
- });
-
- // 检查输出中是否包含该设备且状态为 'device'
- // 匹配格式:IP:PORT device
- const escapedIpPort = ipPort.replace(/\./g, '\\.').replace(/:/g, '\\:');
- const devicePattern = new RegExp(`${escapedIpPort}\\s+device`, 'i');
-
- if (devicePattern.test(stdout)) {
- // 设备确实连接成功
- return { success: true };
- } else {
- // 设备不在列表中,连接失败
- return {
- success: false,
- error: `无法连接到设备 ${ipPort},设备未出现在ADB设备列表中`
- };
- }
- } catch (checkError) {
- // 检查设备列表失败,但连接命令已执行,返回不确定状态
- return {
- success: false,
- error: `无法验证设备连接状态:${checkError.message || '未知错误'}`
- };
- }
- } catch (error) {
- return { success: false, error: error.message || '连接失败' };
- }
- }
- // 注册 IPC 处理器
- export function registerIpcHandlers() {
- // IPC 处理程序:获取 ADB 路径配置(返回实际使用的 ADB 路径)
- ipcMain.handle('get-adb-path-config', async () => {
- // 返回实际使用的 ADB 路径(项目目录下的 adb-tools/adb.exe)
- const adbPath = getCachedAdbPath();
- // 如果是项目目录下的 adb,返回其目录路径;否则返回 null(表示使用系统路径)
- const projectRoot = path.join(__dirname, '..', '..');
- const localAdbPath = path.join(projectRoot, 'adb-tools', 'adb.exe');
- if (adbPath === localAdbPath || adbPath.includes('adb-tools')) {
- return path.join(projectRoot, 'adb-tools');
- }
- // 向后兼容:如果配置文件存在,返回配置的路径
- const config = loadConfig();
- if (config && config['adb-path']) {
- return config['adb-path'];
- }
- return null;
- });
- // IPC 处理程序:获取 ADB 设备列表
- ipcMain.handle('get-adb-devices', async () => {
- return await getADBDevices();
- });
- // IPC 处理程序:扫描网络设备(支持实时推送)
- ipcMain.handle('scan-adb-devices', async (event) => {
- try {
- const devices = await scanNetworkDevices(event);
- return devices;
- } catch (error) {
- return [];
- }
- });
- // IPC 处理程序:连接 ADB 设备
- ipcMain.handle('connect-adb-device', async (event, ipPort) => {
- return await connectDevice(ipPort);
- });
- }
|