#!/usr/bin/env node /** * run-process.js * 接收两个参数:ip 数组 (JSON)、脚本名 * 异步根据每个 ip 执行脚本;执行前先 adb connect 该设备,确保连接成功再跑流程。 * * 调用示例:node run-process.js '["192.168.2.5","192.168.2.6"]' 'RedNoteAIThumbsUp' */ const path = require('path') const fs = require('fs') const { execSync } = require('child_process') const projectRoot = path.resolve(path.join(__dirname, '..')) const config = require(path.join(projectRoot, 'configs', 'config.js')) const adbPath = config.adbPath?.path ? path.resolve(projectRoot, config.adbPath.path) : path.join(projectRoot, 'lib', 'scrcpy-adb', 'adb.exe') const ADB_PORT = 5555 const ipListJson = process.argv[2] const scriptName = process.argv[3] if (!scriptName) { process.stderr.write('run-process: missing scriptName\n') process.exit(1) } const folderPath = path.resolve(path.join(__dirname, '..', 'static', 'process', scriptName)) function writeLog(folderPath, message) { const logFile = path.join(folderPath, 'log.txt') if (!fs.existsSync(folderPath)) fs.mkdirSync(folderPath, { recursive: true }) const ts = new Date().toISOString().replace('T', ' ').slice(0, 19) fs.appendFileSync(logFile, `[${ts}] ${message}\n`) } try { writeLog(folderPath, `[run-process] start scriptName=${scriptName} folderPath=${folderPath}`) } catch (e) { process.stderr.write(`run-process: writeLog failed ${e.message}\n`) } let ipList try { ipList = JSON.parse(ipListJson || '[]') } catch (e) { writeLog(folderPath, `[错误] 解析 IP 列表失败: ${e.message}`) process.exit(1) } let shouldStop = false let actions try { const { parseWorkflow } = require('./ef-compiler/ef-compiler.js') actions = parseWorkflow(JSON.parse(fs.readFileSync(path.join(folderPath, 'process.json'), 'utf8'))).actions } catch (e) { writeLog(folderPath, `[错误] 加载 process.json 失败: ${e.message}`) process.exit(1) } const { executeActionSequence } = require('./ef-compiler/ef-compiler.js') const resolution = { width: 1080, height: 1920 } /** 对指定 IP 执行 adb connect,确保设备在列表中后再跑流程 */ function ensureDeviceConnected(ip, port) { const out = execSync(`"${adbPath}" connect ${ip}:${port}`, { encoding: 'utf-8' }).trim() return out.includes('connected') || out.includes('already connected') } /** 启动执行:遍历 ip 列表并异步执行脚本;任一台失败则停止全部并返回失败设备 IP */ async function start() { if (!ipList || ipList.length === 0) { writeLog(folderPath, '[run-process] 无设备,退出') process.stdout.write(JSON.stringify({ success: true, results: [] }) + '\n') process.exit(0) } writeLog(folderPath, `[run-process] 开始执行 ${ipList.length} 台设备: ${ipList.join(', ')}`) let failedIp = null const runOne = async (ip) => { if (shouldStop) return { ip, success: false, stopped: true } const connected = ensureDeviceConnected(ip, ADB_PORT) if (!connected) { writeLog(folderPath, `[run-process] ${ip}:${ADB_PORT} 连接未就绪,跳过`) return { ip, success: false } } const result = await executeActionSequence(actions, `${ip}:${ADB_PORT}`, folderPath, resolution, 1000, null, () => shouldStop) if (!result.success) { if (!failedIp) { failedIp = ip; shouldStop = true } } return { ip, success: result.success } } const results = await Promise.all(ipList.map(ip => runOne(ip))) const output = failedIp ? { success: false, failedIp, results } : { success: true, results } process.stdout.write(JSON.stringify(output) + '\n') process.exit(failedIp ? 1 : 0) } /** 停止执行(SIGTERM/SIGINT 时调用) */ function stop() { shouldStop = true } process.on('SIGTERM', () => { stop(); process.exit(130) }) process.on('SIGINT', () => { stop(); process.exit(130) }) start()