Files
docs/wiki/concepts/codec-dispatch.md
T
julian 22b1b069df Bootstrap LLM-maintained wiki with TRM architecture knowledge
Initialize CLAUDE.md schema, index, and log; ingest three architecture
sources (system overview, Teltonika ingestion design, official Teltonika
data-sending protocols) into 7 entity pages, 8 concept pages, and 3
source pages with wikilink cross-references.
2026-04-30 13:20:17 +02:00

3.6 KiB

title, type, created, updated, sources, tags
title type created updated sources tags
Codec Dispatch (Registry) concept 2026-04-30 2026-04-30
teltonika-ingestion-architecture
teltonika-data-sending-protocols
teltonika
parser
design-seam

Codec Dispatch

How the teltonika adapter routes incoming frames to the right parser, and the seam that makes phase-2-commands additive instead of a rewrite.

The mechanism

The codec ID (byte 0 of the AVL data payload) indexes into a flat registry of handlers. Each handler implements:

interface CodecHandler {
  codec_id: number
  handle(
    payload: Buffer,
    ctx: { imei: string; publish: (p: Position) => Promise<void> }
  ): Promise<{ ack_count: number }>
}

Phase 1 registers handlers for IDs 0x08, 0x8E, and 0x10. Phase 2 will register additional handlers for 0x0C, 0x0D, 0x0E — these will use a different ctx shape (they need to write bytes back, not just publish), but the registry shape is identical.

Per-codec directionality matters for the Phase 2 handler shape:

Codec ID Direction Handler needs
8 / 8E / 16 0x08 / 0x8E / 0x10 device → server publish(Position) only
12 0x0C bidirectional respond(bytes) for outbound + parse responses
13 0x0D device → server only parse-only (no respond)
14 0x0E bidirectional respond(bytes) + ACK type 0x06 / nACK type 0x11
15 0x0F device → server only out of scope (FMX6 RS232 only)

Codec 13 and 15, despite living in the GPRS-message family, are one-way — handlers for them never write to the socket. This matters for handler-shape design: the Phase 2 outbound family is not "all command codecs," it's specifically 0x0C and 0x0E.

Why a registry, not a switch

  • Adding a new codec is a registration, not a code change in dispatch logic.
  • Phase 2 command codecs slot in alongside Phase 1 data codecs without modifying Phase 1 paths.
  • Codec parsers are independent — there is no shared base class. Their record shapes diverge in ways abstraction would obscure rather than help.

The "session owns the socket; handler borrows it" rule

Phase 1 handlers receive payload + a publish(Position) callback and emit records via Redis. They never write to the socket directly — the session loop handles ACKs.

Phase 2 command handlers will need to write to the socket (to send commands). They borrow write access through a respond(bytes: Buffer) callback added to ctx for command codecs. The session retains socket ownership; handlers borrow write access through a narrow interface.

Unknown codec policy

When the codec ID does not match a registered handler:

  • WARN log entry with IMEI, offending codec ID, raw header bytes.
  • Socket is destroyed.
  • No ACK sent.
  • No attempt to "skip ahead" or guess record layout.

Reasoning: a Teltonika device sending an unrecognized codec is misconfigured, not subtly broken. Silently truncating its data — or worse, mis-parsing — produces records with plausible-looking but wrong coordinates. Loud failure beats quiet corruption.

The teltonika_unknown_codec_total{codec_id} counter is the canary for codec coverage drift.

CRC failure policy

Different from unknown-codec. CRC mismatch = transient transmission issue:

  • Frame is not ACK'd → device retransmits on next session.
  • WARN log with IMEI, expected CRC, computed CRC, frame length.
  • Connection stays open.

Repeated CRC failures from the same device in a short window indicate a deeper problem (firmware, line quality) — surface via metrics, not just logs.