Implement Phase 1 tasks 1.5-1.7 + 1.9 (Codec 8/8E/16 parsers + fixture suite)
- Codec 8 parser (1-byte IO IDs, no NX/Generation Type) - Codec 8 Extended parser (2-byte IO IDs + variable-length NX section) - Codec 16 parser (mixed widths + Generation Type, supports IO IDs > 255) - Shared GPS element / timestamp helpers in gps-element.ts - Fixture loader with bigint/Buffer sentinel encoding and auto-discovery - 12 fixture pairs across codec8/8E/16 (canonical doc + synthetic edge cases) - Cross-checked Codec 8 against Traccar's TeltonikaProtocolDecoder (no discrepancies) 26 new tests. Total 62 passing across 10 test files. typecheck/lint/test/build all clean.
This commit is contained in:
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
# Teltonika Fixture Suite
|
||||
|
||||
Binary protocol fixtures for the Codec 8, 8E, and 16 parsers.
|
||||
|
||||
## File format
|
||||
|
||||
Each fixture is a pair:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `<name>.hex` | Hex-encoded AVL body (CodecID + N1 + records + N2) |
|
||||
| `<name>.expected.json` | Expected `Position[]` output and ACK record count |
|
||||
|
||||
### .hex format
|
||||
|
||||
The body slice only — **not** the full TCP packet (no preamble, no DataFieldLength, no CRC).
|
||||
This is exactly what `frame.payload` contains and what `handler.handle(body, ctx)` receives.
|
||||
|
||||
Whitespace and newlines are stripped before parsing, so you can write multi-line hex for readability.
|
||||
|
||||
### .expected.json format
|
||||
|
||||
```json
|
||||
{
|
||||
"positions": [
|
||||
{
|
||||
"device_id": "FIXTURE",
|
||||
"timestamp": "2019-06-10T10:04:46.000Z",
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"altitude": 0,
|
||||
"angle": 0,
|
||||
"speed": 0,
|
||||
"satellites": 0,
|
||||
"priority": 1,
|
||||
"attributes": {
|
||||
"21": 3,
|
||||
"78": "__bigint:0",
|
||||
"100": "__buffer_b64:AAEC"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ack_record_count": 1
|
||||
}
|
||||
```
|
||||
|
||||
`device_id` is always `"FIXTURE"` — the IMEI comes from the session context, not the body.
|
||||
|
||||
#### Attribute value sentinels
|
||||
|
||||
| Sentinel | Decoded type | Example |
|
||||
|---------|-------------|---------|
|
||||
| number literal | `number` | `24079` |
|
||||
| `"__bigint:<decimal>"` | `bigint` | `"__bigint:893700218"` |
|
||||
| `"__buffer_b64:<base64>"` | `Buffer` | `"__buffer_b64:qw=="` |
|
||||
|
||||
For a zero-length Buffer, use `"__buffer_b64:"` (empty base64 string).
|
||||
|
||||
## How to add a new fixture
|
||||
|
||||
1. Get the AVL body bytes (from a live capture, a device emulator, or hand-computed).
|
||||
2. Write the body as hex into `<name>.hex`.
|
||||
3. Manually trace the expected parse output and write `<name>.expected.json`.
|
||||
4. Run `pnpm test` — the new fixture is automatically discovered and tested.
|
||||
|
||||
No changes to any test file are needed.
|
||||
|
||||
## Bootstrap vs. synthetic fixtures
|
||||
|
||||
- **Bootstrap** fixtures (01-canonical, etc.) are sourced directly from the Teltonika documentation hex examples. Their expected outputs are read from the doc's parsed tables.
|
||||
- **Synthetic** fixtures are hand-constructed for edge cases not covered by the canonical examples. Their expected outputs are computed manually and cross-checked.
|
||||
|
||||
## Cross-check methodology
|
||||
|
||||
Synthetic Codec 8 fixtures were cross-checked against the Traccar open-source
|
||||
decoder (`TeltonikaProtocolDecoder.java`) for field widths and IO section parsing.
|
||||
No discrepancies were found for the basic N1/N2/N4/N8 sections.
|
||||
The NX section in Codec 8E has no equivalent in older Traccar versions; it was
|
||||
verified by byte-level manual trace only.
|
||||
Reference in New Issue
Block a user