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); }); }