AI_UIAutomation/scripts/relay.ts

107 lines
3.8 KiB
TypeScript

/**
* 继电器命令行调试工具 —— 参数化触发按键/配对/原始命令,方便接线调试。
*
* 用法(端口/波特率走 .env 的 SERIAL_PORT/SERIAL_BAUD):
* npx ts-node scripts/relay.ts probe # 查版本(只读握手)
* npx ts-node scripts/relay.ts status # 读 64 路状态
* npx ts-node scripts/relay.ts pair remote # 进配对(按 config 默认)
* npx ts-node scripts/relay.ts pair meter --hold 2500 # 进配对,覆盖时长
* npx ts-node scripts/relay.ts press remote convex --hold 800 # 点按命名键
* npx ts-node scripts/relay.ts pulse 34,35 --hold 3000 # 同时脉冲指定通道
* npx ts-node scripts/relay.ts on 34 / off 34 # 单路通/断
* npx ts-node scripts/relay.ts raw 55C8210155 # 发原始 16 进制帧
*/
import * as dotenv from 'dotenv';
import * as path from 'path';
dotenv.config({ path: path.resolve(__dirname, '../.env') });
import { SerialController } from '../utils/common/serial_controller';
import { triggerRelayAction, pressButton } from '../utils/common/relay.helper';
function flag(name: string): string | undefined {
const i = process.argv.indexOf(`--${name}`);
return i >= 0 ? process.argv[i + 1] : undefined;
}
/** 取位置参数(剔除 --flag 及其紧随的值)。 */
function positionals(): string[] {
const argv = process.argv.slice(2);
const out: string[] = [];
for (let i = 0; i < argv.length; i++) {
if (argv[i].startsWith('--')) {
i++; // 跳过该 flag 的值
continue;
}
out.push(argv[i]);
}
return out;
}
async function main() {
const [cmd, a, b] = positionals();
const hold = flag('hold') ? parseInt(flag('hold') as string, 10) : undefined;
switch (cmd) {
case 'probe': {
const c = new SerialController();
await c.open();
console.log(await c.queryVersion());
await c.close();
break;
}
case 'status': {
const c = new SerialController();
await c.open();
console.log('status:', (await c.readStatus()).toString('hex').toUpperCase());
await c.close();
break;
}
case 'pair': {
if (!a) throw new Error('用法: pair <device> [--hold ms]');
await triggerRelayAction(a, 'pairing', hold ? { holdMs: hold } : {});
break;
}
case 'press': {
if (!a || !b) throw new Error('用法: press <device> <button> [--hold ms]');
await pressButton(a, b, hold ?? 500);
break;
}
case 'pulse': {
if (!a) throw new Error('用法: pulse <ch1,ch2,...> [--hold ms]');
const chs = a.split(',').map((s) => parseInt(s, 10));
const c = new SerialController();
await c.open();
await c.pulseChannels(chs, hold ?? 500);
console.log(`脉冲通道[${chs.join(',')}] ${hold ?? 500}ms 完成`);
await c.close();
break;
}
case 'on':
case 'off': {
if (!a) throw new Error(`用法: ${cmd} <ch>`);
const c = new SerialController();
await c.open();
const resp = cmd === 'on' ? await c.relayOn(parseInt(a, 10)) : await c.relayOff(parseInt(a, 10));
console.log(`${cmd} ${a}:`, resp.toString('hex').toUpperCase());
await c.close();
break;
}
case 'raw': {
if (!a) throw new Error('用法: raw <hex,如 55C8210155>');
const bytes = (a.match(/.{1,2}/g) || []).map((h) => parseInt(h, 16));
const c = new SerialController();
await c.open();
console.log('resp:', (await c.send(bytes)).toString('hex').toUpperCase());
await c.close();
break;
}
default:
console.log('命令: probe | status | pair <dev> | press <dev> <btn> | pulse <chs> | on <ch> | off <ch> | raw <hex> [--hold ms]');
}
}
main().catch((e) => {
console.error('错误:', e.message);
process.exit(1);
});