device-manager.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { ipcMain, BrowserWindow } from 'electron';
  2. import { exec } from 'child_process';
  3. import { promisify } from 'util';
  4. import { getCachedAdbPath, loadConfig } from '../config.js';
  5. const execAsync = promisify(exec);
  6. // 获取主窗口引用(用于实时推送设备发现事件)
  7. function getMainWindow() {
  8. const windows = BrowserWindow.getAllWindows();
  9. return windows.length > 0 ? windows[0] : null;
  10. }
  11. // 获取 ADB 设备列表(每次都重新扫描网络)
  12. export async function getADBDevices() {
  13. try {
  14. // 每次都重新扫描网络设备,不记录已连接的设备
  15. return await scanNetworkDevices(null);
  16. } catch (error) {
  17. console.error('获取设备列表失败:', error);
  18. return [];
  19. }
  20. }
  21. // 网络扫描:按顺序扫描 192.168.0、192.168.1、192.168.2 三个网段,尝试连接设备(实时推送结果)
  22. export async function scanNetworkDevices(event) {
  23. const adbPath = getCachedAdbPath();
  24. const port = 5555;
  25. const maxConcurrent = 50; // 限制并发数,避免过载
  26. const connectTimeout = 1500; // 连接超时时间(毫秒)
  27. const foundDevices = new Set(); // 使用 Set 避免重复
  28. // 定义要扫描的网段(按顺序:0.0 -> 1.0 -> 2.0)
  29. const networkSegments = ['192.168.0', '192.168.1', '192.168.2'];
  30. // 推送设备发现的辅助函数
  31. const pushDevice = (ipPort) => {
  32. if (!foundDevices.has(ipPort)) {
  33. foundDevices.add(ipPort);
  34. const device = {
  35. id: ipPort,
  36. status: 'device'
  37. };
  38. // 实时发送发现的设备
  39. if (event && event.sender) {
  40. event.sender.send('device-found', device);
  41. } else {
  42. const mainWindow = getMainWindow();
  43. if (mainWindow) {
  44. mainWindow.webContents.send('device-found', device);
  45. }
  46. }
  47. console.log('发现设备:', ipPort);
  48. }
  49. };
  50. // 扫描单个 IP 地址的函数
  51. const scanIP = async (ip) => {
  52. const ipPort = `${ip}:${port}`;
  53. try {
  54. // 先尝试连接设备
  55. await execAsync(`${adbPath} connect ${ipPort}`, {
  56. timeout: connectTimeout,
  57. maxBuffer: 1024 * 1024
  58. });
  59. // 连接后稍等片刻,让设备注册到 ADB 服务器
  60. await new Promise(resolve => setTimeout(resolve, 500));
  61. } catch (error) {
  62. // 连接失败是正常的,继续检查设备列表
  63. }
  64. // 调用 adb devices 命令获取所有设备列表,检查是否包含端口 5555 的设备
  65. try {
  66. const { stdout } = await execAsync(`${adbPath} devices`, {
  67. timeout: 2000,
  68. maxBuffer: 1024 * 1024
  69. });
  70. // 检查输出中是否包含该 IP:PORT(匹配端口 5555)
  71. // 使用正则表达式匹配 IP:5555 格式
  72. const devicePattern = new RegExp(`${ip.replace(/\./g, '\\.')}:${port}\\s+device`, 'i');
  73. if (devicePattern.test(stdout)) {
  74. // 发现设备,立即推送显示(不等待整批完成)
  75. pushDevice(ipPort);
  76. return ipPort;
  77. }
  78. // 如果没有匹配到,说明没有设备
  79. } catch (checkError) {
  80. // 检查失败,忽略
  81. }
  82. return null;
  83. };
  84. // 按顺序扫描每个网段
  85. for (const baseIP of networkSegments) {
  86. console.log(`开始扫描网段: ${baseIP}.x`);
  87. // 生成当前网段的 IP 地址列表
  88. const ipList = [];
  89. for (let i = 1; i <= 255; i++) {
  90. ipList.push(`${baseIP}.${i}`);
  91. }
  92. // 分批并发扫描当前网段,扫描到一个显示一个
  93. for (let i = 0; i < ipList.length; i += maxConcurrent) {
  94. const batch = ipList.slice(i, i + maxConcurrent);
  95. const promises = batch.map(ip => scanIP(ip));
  96. // 并发执行当前批次,但不等待所有完成才推送
  97. // 每个 promise 一旦发现设备就会立即推送
  98. await Promise.all(promises);
  99. }
  100. console.log(`完成扫描网段: ${baseIP}.x`);
  101. }
  102. // 返回所有发现的设备
  103. return Array.from(foundDevices).map(ipPort => ({
  104. id: ipPort,
  105. status: 'device'
  106. }));
  107. }
  108. // 连接 ADB 设备
  109. export async function connectDevice(ipPort) {
  110. try {
  111. const adbPath = getCachedAdbPath();
  112. await execAsync(`${adbPath} connect ${ipPort}`);
  113. return { success: true };
  114. } catch (error) {
  115. console.error('连接设备失败:', error);
  116. return { success: false, error: error.message };
  117. }
  118. }
  119. // 注册 IPC 处理器
  120. export function registerIpcHandlers() {
  121. // IPC 处理程序:获取 ADB 路径配置
  122. ipcMain.handle('get-adb-path-config', async () => {
  123. const config = loadConfig();
  124. return config ? config['adb-path'] : null;
  125. });
  126. // IPC 处理程序:获取 ADB 设备列表
  127. ipcMain.handle('get-adb-devices', async () => {
  128. return await getADBDevices();
  129. });
  130. // IPC 处理程序:扫描网络设备(支持实时推送)
  131. ipcMain.handle('scan-adb-devices', async (event) => {
  132. try {
  133. const devices = await scanNetworkDevices(event);
  134. return devices;
  135. } catch (error) {
  136. console.error('网络扫描失败:', error);
  137. return [];
  138. }
  139. });
  140. // IPC 处理程序:连接 ADB 设备
  141. ipcMain.handle('connect-adb-device', async (event, ipPort) => {
  142. return await connectDevice(ipPort);
  143. });
  144. }