run-process.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #!/usr/bin/env node
  2. /**
  3. * run-process.js
  4. * 接收两个参数:ip 数组 (JSON)、脚本名
  5. * 异步根据每个 ip 执行脚本;执行前先 adb connect 该设备,确保连接成功再跑流程。
  6. *
  7. * 调用示例:node run-process.js '["192.168.2.5","192.168.2.6"]' 'RedNoteAIThumbsUp'
  8. */
  9. const path = require('path')
  10. const fs = require('fs')
  11. const { execSync } = require('child_process')
  12. const projectRoot = path.resolve(path.join(__dirname, '..'))
  13. const staticRoot = process.env.STATIC_ROOT ? path.resolve(process.env.STATIC_ROOT) : path.join(projectRoot, 'static')
  14. const config = require(path.join(projectRoot, 'configs', 'config.js'))
  15. const adbPath = config.adbPath?.path
  16. ? path.resolve(projectRoot, config.adbPath.path)
  17. : path.join(projectRoot, 'lib', 'scrcpy-adb', 'adb.exe')
  18. const ADB_PORT = 5555
  19. const ipListJson = process.argv[2]
  20. const scriptName = process.argv[3]
  21. const folderPath = path.resolve(path.join(staticRoot, 'process', scriptName))
  22. const logFilePath = path.join(folderPath, 'log.txt')
  23. const ipList = JSON.parse(ipListJson)
  24. let shouldStop = false
  25. function appendLog(text) {
  26. try {
  27. fs.appendFileSync(logFilePath, text, 'utf8')
  28. } catch (e) {}
  29. }
  30. function logLine(msg) {
  31. const line = `[${new Date().toISOString()}] ${msg}\n`
  32. process.stdout.write(line)
  33. appendLog(line)
  34. }
  35. const { parseWorkflow } = require('./ef-compiler/ef-compiler.js')
  36. const actions = parseWorkflow(JSON.parse(fs.readFileSync(path.join(folderPath, 'process.json'), 'utf8'))).actions
  37. const { executeActionSequence } = require('./ef-compiler/ef-compiler.js')
  38. const resolution = { width: 1080, height: 1920 }
  39. function sleep(ms) {
  40. return new Promise((resolve) => setTimeout(resolve, ms))
  41. }
  42. /** 对指定 IP 执行 adb connect,与 bat-tool/adb-connect-test 行为一致:可重试、短延迟,确保设备就绪 */
  43. async function ensureDeviceConnected(ip, port, logLineFn) {
  44. const deviceId = `${ip}:${port}`
  45. const maxTries = 3
  46. const delayMs = 2000
  47. for (let i = 0; i < maxTries; i++) {
  48. try {
  49. const out = execSync(`"${adbPath}" connect ${deviceId}`, { encoding: 'utf-8' }).trim()
  50. if (out.includes('connected') || out.includes('already connected')) return true
  51. } catch (e) {
  52. // execSync 抛错(如 adb 未找到)时不再重试
  53. if (logLineFn) logLineFn(`Connect attempt ${i + 1}/${maxTries}: ${(e.stderr || e.message || '').trim() || 'failed'}`)
  54. }
  55. if (i < maxTries - 1) {
  56. if (logLineFn) logLineFn(`Wait ${delayMs / 1000}s, retry connect ${deviceId} ...`)
  57. await sleep(delayMs)
  58. }
  59. }
  60. return false
  61. }
  62. /** 启动执行:遍历 ip 列表并异步执行脚本;任一台失败则停止全部并返回失败设备 IP */
  63. async function start() {
  64. appendLog(`\n========== Run ${new Date().toISOString()} ==========\n`)
  65. logLine(`Process "${scriptName}" start, devices: ${ipList.length}`)
  66. let failedIp = null
  67. const runOne = async (ip) => {
  68. if (shouldStop) return { ip, success: false, stopped: true }
  69. const deviceId = ip.includes(':') ? ip : `${ip}:${ADB_PORT}`
  70. const [deviceIp, devicePort] = deviceId.includes(':') ? deviceId.split(':') : [ip, ADB_PORT]
  71. logLine(`Connecting ${deviceId} ...`)
  72. const connected = await ensureDeviceConnected(deviceIp, devicePort, logLine)
  73. if (!connected) {
  74. logLine(`Failed to connect ${deviceId}`)
  75. return { ip, success: false }
  76. }
  77. logLine(`Running on ${deviceId}`)
  78. const result = await executeActionSequence(actions, deviceId, folderPath, resolution, 1000, null, () => shouldStop)
  79. if (!result.success) {
  80. if (!failedIp) { failedIp = ip; shouldStop = true }
  81. }
  82. return { ip, success: result.success }
  83. }
  84. const results = await Promise.all(ipList.map(ip => runOne(ip)))
  85. const output = failedIp
  86. ? { success: false, failedIp, results }
  87. : { success: true, results }
  88. logLine(failedIp ? `Process finished with failed device: ${failedIp}` : 'Process finished successfully.')
  89. const resultLine = JSON.stringify(output) + '\n'
  90. process.stdout.write(resultLine)
  91. appendLog(resultLine)
  92. process.exit(failedIp ? 1 : 0)
  93. }
  94. /** 停止执行(SIGTERM/SIGINT 时调用) */
  95. function stop() {
  96. shouldStop = true
  97. }
  98. process.on('SIGTERM', () => { stop(); process.exit(130) })
  99. process.on('SIGINT', () => { stop(); process.exit(130) })
  100. start()