1e9219d14a
- Project scaffold (Node 22 + TS 5 + pnpm + vitest + ESLint flat config) - Core shell: TCP server, session loop, adapter registry, types - Configuration (zod-validated env) and pino logger - Teltonika adapter: IMEI handshake, frame envelope, CRC-16/IBM, codec dispatch registry, DeviceAuthority seam (AllowAllAuthority default) Codec data parsers (1.5-1.7), Redis publisher (1.8), and downstream tasks remain. 36 tests covering CRC, framing, handshake, device authority, config, and core server. typecheck/lint/test/build all clean.
86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { crc16Ibm } from '../src/adapters/teltonika/crc.js';
|
|
|
|
describe('crc16Ibm', () => {
|
|
it('returns 0x0000 for an empty buffer', () => {
|
|
expect(crc16Ibm(Buffer.alloc(0))).toBe(0x0000);
|
|
});
|
|
|
|
it('matches the canonical Teltonika Codec 8 first example (0xC7CF)', () => {
|
|
// Full frame (hex stream from the Teltonika doc, first example):
|
|
// 000000000000003608010000016B40D8EA30010000000000000000000000000000000105021503010101425E0F01F10000601A014E0000000000000000010000C7CF
|
|
//
|
|
// Structure:
|
|
// 00 00 00 00 preamble
|
|
// 00 00 00 36 DataFieldLength = 0x36 = 54
|
|
// 08 01 ... body (CodecID through N2) — 54 bytes
|
|
// 00 00 C7 CF CRC field
|
|
//
|
|
// CRC is computed over the 54-byte body only.
|
|
const body = Buffer.from(
|
|
'08' + // CodecID
|
|
'01' + // N1 = 1
|
|
'0000016B40D8EA30' + // Timestamp
|
|
'01' + // Priority
|
|
'00000000' + // Longitude
|
|
'00000000' + // Latitude
|
|
'0000' + // Altitude
|
|
'0000' + // Angle
|
|
'00' + // Satellites
|
|
'0000' + // Speed
|
|
'01' + // Event IO ID
|
|
'05' + // N total IO
|
|
'02' + // N1 IO (1-byte values)
|
|
'15' + '03' + // IO 21 = 3
|
|
'01' + '01' + // IO 1 = 1
|
|
'01' + // N2 IO (2-byte values)
|
|
'42' + '5E0F' + // IO 66 = 0x5E0F
|
|
'01' + // N4 IO (4-byte values)
|
|
'F1' + '0000601A' + // IO 241 = 0x0000601A
|
|
'01' + // N8 IO (8-byte values)
|
|
'4E' + '0000000000000000' + // IO 78 = 0
|
|
'01', // N2 = 1
|
|
'hex',
|
|
);
|
|
|
|
expect(body.length).toBe(0x36); // sanity-check: must be 54 bytes
|
|
expect(crc16Ibm(body)).toBe(0xc7cf);
|
|
});
|
|
|
|
it('matches the second Teltonika Codec 8 example (0xF22A)', () => {
|
|
// Full frame hex:
|
|
// 000000000000002808010000016B40D9AD80010000000000000000000000000000000103021503010101425E100000010000F22A
|
|
// Body = bytes from CodecID through N2 (0x28 = 40 bytes)
|
|
const body = Buffer.from(
|
|
'08' +
|
|
'01' +
|
|
'0000016B40D9AD80' +
|
|
'01' +
|
|
'00000000' +
|
|
'00000000' +
|
|
'0000' +
|
|
'0000' +
|
|
'00' +
|
|
'0000' +
|
|
'01' +
|
|
'03' +
|
|
'02' +
|
|
'15' + '03' +
|
|
'01' + '01' +
|
|
'01' +
|
|
'42' + '5E10' +
|
|
'00' +
|
|
'00' +
|
|
'01',
|
|
'hex',
|
|
);
|
|
|
|
expect(body.length).toBe(0x28); // 40 bytes
|
|
expect(crc16Ibm(body)).toBe(0xf22a);
|
|
});
|
|
|
|
it('produces a non-zero CRC for a single 0xFF byte', () => {
|
|
expect(crc16Ibm(Buffer.from([0xff]))).not.toBe(0);
|
|
});
|
|
});
|