50 lines
2.1 KiB
TypeScript
50 lines
2.1 KiB
TypeScript
/**
|
||
* 继电器开发板按钮点击次数报告 —— 统计每块板、每个通道(=设备按键)的累计点击,
|
||
* 对照继电器约 20 万次寿命,显示占比/剩余/告警。多块板按串口序列号自动分别统计。
|
||
* npx ts-node scripts/relay-press-report.ts
|
||
* 数据来源:test-plan/relay-press-counts.json(由 SerialController 每次通道吸合时累加)。
|
||
*/
|
||
import * as fs from 'fs';
|
||
import * as path from 'path';
|
||
import { RELAY_MAP } from '../config/relay.config';
|
||
|
||
const FILE = path.resolve(__dirname, '../test-plan/relay-press-counts.json');
|
||
const LIFE = 200000;
|
||
|
||
// 反查:通道号 → "设备.按键"(取 android 通道;两端不同时也显示 ios)
|
||
const chLabel: Record<number, string> = {};
|
||
for (const [dev, cfg] of Object.entries(RELAY_MAP)) {
|
||
for (const [btn, spec] of Object.entries(cfg.buttons)) {
|
||
const chs = typeof spec === 'number' ? [spec] : [spec.android, spec.ios].filter((x): x is number => x != null);
|
||
for (const ch of chs) chLabel[ch] = `${dev}.${btn}`;
|
||
}
|
||
}
|
||
|
||
const data: Record<string, Record<string, number>> = fs.existsSync(FILE)
|
||
? JSON.parse(fs.readFileSync(FILE, 'utf-8')) : {};
|
||
|
||
if (!Object.keys(data).length) {
|
||
console.log('(暂无点击计数;跑过添加/继电器操作后才会有。文件:' + FILE + ')');
|
||
process.exit(0);
|
||
}
|
||
|
||
for (const [board, chs] of Object.entries(data)) {
|
||
console.log(`\n=== 开发板 ${board} ===`);
|
||
console.log('通道'.padEnd(6) + '设备.按键'.padEnd(22) + '点击次数'.padEnd(12) + '寿命占比'.padEnd(10) + '剩余');
|
||
const entries = Object.entries(chs).map(([ch, c]) => [Number(ch), c] as [number, number]).sort((a, b) => b[1] - a[1]);
|
||
let total = 0;
|
||
for (const [ch, cnt] of entries) {
|
||
total += cnt;
|
||
const pct = (cnt / LIFE * 100).toFixed(2) + '%';
|
||
const warn = cnt >= LIFE * 0.9 ? ' ⚠️已超90%寿命' : cnt >= LIFE * 0.8 ? ' ⚠接近寿命' : '';
|
||
console.log(
|
||
`ch${ch}`.padEnd(6) +
|
||
(chLabel[ch] || '-').padEnd(22) +
|
||
String(cnt).padEnd(12) +
|
||
pct.padEnd(10) +
|
||
String(LIFE - cnt) + warn
|
||
);
|
||
}
|
||
console.log(`板内累计点击: ${total}`);
|
||
}
|