Files
tcp-ingestion/.planning/phase-1-telemetry/07-codec-16.md
T
julian c8a5f4cd68 Add Phase 1 and Phase 2 planning documents
ROADMAP plus granular task files per phase. Phase 1 (12 tasks + 1.13
device authority) covers Codec 8/8E/16 telemetry ingestion; Phase 2
(6 tasks) covers Codec 12/14 outbound commands; Phase 3 enumerates
deferred items.
2026-04-30 15:50:49 +02:00

3.6 KiB
Raw Blame History

Task 1.7 — Codec 16 parser

Phase: 1 — Inbound telemetry Status: Not started 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.)