ti-pote/apps/robot-client/scripts/verify-protocol.ts
2026-04-08 18:37:08 +02:00

65 lines
2.4 KiB
TypeScript

// One-off verification script used during development to ensure the
// TS protocol codec round-trips correctly and matches the C++ side.
// Not intended to ship — kept because vitest couldn't run in the
// sandbox where this was bootstrapped, and a plain tsx script lets
// us re-run it quickly on any machine.
import {
crc8,
encodeFrame,
FrameDecoder,
MsgType,
Emotion,
type DecodedFrame,
} from '../src/hardware/protocol.js';
function assert(cond: unknown, msg: string): void {
if (!cond) {
console.error(`FAIL: ${msg}`);
process.exitCode = 1;
} else {
console.log(`ok: ${msg}`);
}
}
// CRC reference values (also validated against Python implementation).
assert(crc8(Buffer.from('123456789', 'ascii')) === 0xf4, "crc8('123456789') === 0xF4");
assert(crc8(Buffer.from([0x00])) === 0x00, 'crc8([0x00]) === 0x00');
assert(crc8(Buffer.from([0x01])) === 0x07, 'crc8([0x01]) === 0x07');
assert(crc8(Buffer.from([0xaa])) === 0x5f, 'crc8([0xAA]) === 0x5F');
// DISPLAY_EMOTION HAPPY must produce a 6-byte frame with CRC 0xDC
// (matches Python reference computed in verify script).
const happy = encodeFrame(MsgType.DISPLAY_EMOTION, Buffer.from([Emotion.HAPPY]));
assert(happy.length === 6, 'DISPLAY_EMOTION frame length === 6');
assert(happy[0] === 0xaa, 'start byte 0xAA');
assert(happy[1] === 0x20, 'type 0x20');
assert(happy[2] === 0x00 && happy[3] === 0x01, 'length = 1');
assert(happy[4] === 0x01, 'payload = HAPPY (1)');
assert(happy[5] === 0xdc, 'CRC === 0xDC');
// Round-trip through the decoder.
let received: DecodedFrame | null = null;
const decoder = new FrameDecoder((f) => {
received = f;
});
decoder.feed(happy);
assert(received !== null, 'frame emitted');
assert(received!.type === MsgType.DISPLAY_EMOTION, 'type preserved');
assert(received!.payload[0] === Emotion.HAPPY, 'payload preserved');
// Resync after corrupted CRC.
received = null;
const bad = Buffer.from(happy);
bad[bad.length - 1] = (bad[bad.length - 1]! ^ 0xff) & 0xff;
decoder.feed(bad);
assert(received === null, 'bad CRC not emitted');
assert(decoder.framesDropped === 1, 'drop counter === 1');
const again = encodeFrame(MsgType.DISPLAY_EMOTION, Buffer.from([Emotion.WINK]));
decoder.feed(again);
assert(received !== null, 'decoder resynced');
assert((received as unknown as DecodedFrame).payload[0] === Emotion.WINK, 'resync payload ok');
console.log('\nall protocol checks passed');