Files
tcp-ingestion/.planning/phase-1-telemetry/07-codec-16.md
T
julian 90d6a73a60 Sync ROADMAP statuses with landed work; mark 1.10/1.12/1.13 as paused
Tasks 1.1-1.9 marked done with their landing commit SHAs. Tasks 1.10
(observability), 1.12 (production hardening), and 1.13 (device
authority) marked paused with explicit resume triggers — pilot
deployment on real Teltonika hardware takes priority. Task 1.11
remains as next, in slimmed form for the pilot (no /readyz healthcheck
since the metrics endpoint is part of paused 1.10).
2026-04-30 16:49:07 +02:00

85 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Task 1.7 — Codec 16 parser
**Phase:** 1 — Inbound telemetry
**Status:** 🟩 Done — landed in commit `381287b`
**Depends on:** 1.4, 1.5 (shared helpers), 1.9
**Wiki refs:** `docs/wiki/concepts/avl-data-format.md` § Codec 16, `docs/wiki/sources/teltonika-data-sending-protocols.md` § Codec 16
## Goal
Parse Codec 16 (`0x10`) AVL data bodies into `Position` records, including the per-record **Generation Type** byte.
## Deliverables
- `src/adapters/teltonika/codec/data/codec16.ts` exporting `codec16Handler: CodecDataHandler` with `codec_id: 0x10`.
- Test file `test/codec16.test.ts` with the canonical doc example (multi-record) plus at least one synthetic fixture covering each Generation Type value.
## Specification
### Differences from Codec 8 / Codec 8E
| Field | Codec 8 | Codec 16 | Codec 8E (for contrast) |
|-------|---------|----------|-------------------------|
| Codec ID | `0x08` | `0x10` | `0x8E` |
| Event IO ID width | 1B | **2B** | 2B |
| Generation Type | — | **1B** | — |
| N total / N* counts | 1B | **1B** | 2B |
| IO ID width | 1B | **2B** | 2B |
| Value widths | 1/2/4/8B | 1/2/4/8B | 1/2/4/8B |
| Variable-length IO (NX) | — | — | Yes |
Codec 16 is a "mixed" layout: 2-byte IO IDs (like 8E) but 1-byte counts (like 8), plus the new Generation Type field. This is the trap — implementers who copy from Codec 8E will get the count widths wrong; implementers who copy from Codec 8 will get the IO ID widths wrong. Read the spec carefully and write fixture-driven tests first.
### IO Element layout (Codec 16)
```
[Event IO ID 2B]
[Generation Type 1B] ← unique to Codec 16
[N total 1B]
[N1 1B] then N1 × ([IO ID 2B][Value 1B])
[N2 1B] then N2 × ([IO ID 2B][Value 2B])
[N4 1B] then N4 × ([IO ID 2B][Value 4B])
[N8 1B] then N8 × ([IO ID 2B][Value 8B])
```
No NX section.
### Generation Type
1-byte enum:
| Value | Meaning |
|-------|---------|
| 0 | On Exit |
| 1 | On Entrance |
| 2 | On Both |
| 3 | Reserved |
| 4 | Hysteresis |
| 5 | On Change |
| 6 | Eventual |
| 7 | Periodical |
Storage decision: **store as `attributes['__generation_type']`** (consistent with the `__event` convention from task 1.5). Codec 8 and 8E omit this key entirely. Downstream code can pattern-match on its presence.
> **Open question (carried from task 1.5):** if we promote Generation Type to a typed `Position` field, then `__event` should also become typed. Recommendation: keep them in `attributes` for Phase 1; revisit when Processor-side modeling firms up. Flagged in [[position-record]] open questions.
### AVL ID range
Codec 16 (and 8E) supports IO IDs > 255. The parser treats this transparently — IO IDs are read as 2-byte unsigned values; nothing prevents `ioId = 1234`. Just confirm no fixture has an off-by-one assumption that breaks for >255.
## Acceptance criteria
- [ ] Canonical doc example (two records, N1=2, N2=2, codec ID `0x10`, generation type `0x05`) parses correctly with both records' attributes populated.
- [ ] A synthetic fixture exists for each Generation Type 07 (eight fixtures total or one fixture with eight records varying the field).
- [ ] At least one synthetic fixture has an IO ID > 255 to verify 2-byte read.
- [ ] `attributes['__generation_type']` is set on every Codec 16 position; absent on Codec 8 / 8E positions.
## Risks / open questions
- The "mixed widths" trap is real. Pair-review (or have a second LLM agent review) the field-width table before declaring done. Fixture tests catch this if they're built carefully.
- Reserved value `3` for Generation Type: spec says reserved. Decision: log a `debug` if observed; do not reject. We do not police reserved values that don't break parsing.
## Done
(Fill in once complete.)