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.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# 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 0–7 (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.)
|
||||
Reference in New Issue
Block a user