From 111a54203415d2a72df0f300adbb75f42a336c70 Mon Sep 17 00:00:00 2001 From: Julian Cuni Date: Mon, 4 May 2026 17:57:16 +0200 Subject: [PATCH] wiki: confirm SPA implements IMEI join per WS contract Adds an explicit SPA-side join section in position-record.md and references the implementation file (src/data/devices.ts -> useDevicesByImei) from the deviceId field row in the WS contract page. Captures the brief drift-and-realign episode where the SPA was keying by uuid before Phase 3.4 and the device list panel rendered id prefixes. The contract itself didn't change - the SPA implementation just caught up with what the wiki already said. --- wiki/concepts/position-record.md | 17 +++++++++++++++-- wiki/synthesis/processor-ws-contract.md | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/wiki/concepts/position-record.md b/wiki/concepts/position-record.md index 4a26a70..d672bd5 100644 --- a/wiki/concepts/position-record.md +++ b/wiki/concepts/position-record.md @@ -2,7 +2,7 @@ title: Position Record type: concept created: 2026-04-30 -updated: 2026-05-03 +updated: 2026-05-04 sources: [gps-tracking-architecture, teltonika-ingestion-architecture, teltonika-data-sending-protocols] tags: [data-model, boundary-contract] --- @@ -94,4 +94,17 @@ Why store the IMEI rather than the uuid: - [[tcp-ingestion]] writes positions without any business-plane round-trip ([[plane-separation]]), so the only identifier it has at write time is the IMEI. - Devices can move between `entry_devices` rows across events; positions are an immutable per-IMEI record. -If/when a Phase 2 redesign moves to uuid-keyed positions, this is the section to revise — and `processor` `src/live/snapshot.ts` + `src/live/device-event-map.ts` both lose the translation hop. +If/when a Phase 2 redesign moves to uuid-keyed positions, this is the section to revise — and `processor` `src/live/snapshot.ts` + `src/live/device-event-map.ts` both lose the translation hop, alongside `react-spa`'s `src/data/devices.ts` (`useDevicesByImei` becomes `useDevicesById`). + +### Consumer-side join (SPA) + +The same translation hop applies in [[react-spa]] when the live channel's `position.deviceId` (IMEI) needs to resolve to a Device row's `model` / `serial_number` / vehicle / crew metadata. Implementation: + +```ts +// src/data/devices.ts +export function useDevicesByImei() { + // readItems('devices') -> Map +} +``` + +Used by ``, ``, and ``. Briefly drifted to keying by `devices.id` (uuid) between Phase 2 and Phase 3.2 — surfaced as device labels rendering 8-char id prefixes ("35042406") instead of model + IMEI tail ("FMB920 #3619"). Re-aligned with this contract on 2026-05-04 before Task 3.4's per-device detail panel started consuming the join. diff --git a/wiki/synthesis/processor-ws-contract.md b/wiki/synthesis/processor-ws-contract.md index b605863..cf0bbb6 100644 --- a/wiki/synthesis/processor-ws-contract.md +++ b/wiki/synthesis/processor-ws-contract.md @@ -2,7 +2,7 @@ title: Processor WebSocket contract type: synthesis created: 2026-05-02 -updated: 2026-05-03 +updated: 2026-05-04 sources: [gps-tracking-architecture, traccar-maps-architecture] tags: [websocket, protocol, contract, telemetry-plane, decision] --- @@ -161,7 +161,7 @@ Field semantics: |---|---|---|---| | `type` | `"position"` | yes | Discriminator. | | `topic` | string | yes | Echoes the subscription. Allows multiplexing on one connection. | -| `deviceId` | string | yes | **Phase 1: the IMEI** (vendor identifier, e.g. `"350424064163619"`) — same value as `Position.device_id` per [[position-record]]. Originally specified as `devices.id` (uuid) here; the implementation diverged because [[tcp-ingestion]] only knows the IMEI at write time and the live channel ships the same identifier through end-to-end. SPA joins `deviceId` → `devices.imei` to look up entry/vehicle/crew via TanStack Query against [[directus]]. Closing this divergence (uuid on the wire) is a Phase 2 question; not blocking dogfood. | +| `deviceId` | string | yes | **Phase 1: the IMEI** (vendor identifier, e.g. `"350424064163619"`) — same value as `Position.device_id` per [[position-record]]. Originally specified as `devices.id` (uuid) here; the implementation diverged because [[tcp-ingestion]] only knows the IMEI at write time and the live channel ships the same identifier through end-to-end. SPA joins `deviceId` → `devices.imei` to look up entry/vehicle/crew via TanStack Query against [[directus]]. Consumer-side implementation lives in [[react-spa]] at `src/data/devices.ts` (`useDevicesByImei()` — builds `Map`); used by ``, ``, and `` for label / metadata lookup. Closing this divergence (uuid on the wire) is a Phase 2 question; not blocking dogfood. | | `lat` / `lon` | number (degrees, WGS84) | yes | GPS coordinates. **Coordinate order in JSON is `lat`/`lon`** (not `[lon,lat]` GeoJSON ordering — that conversion happens in the SPA). | | `ts` | number (epoch milliseconds, UTC) | yes | Authoritative timestamp from the device's GPS fix. **Always use this, never `Date.now()` on the client.** | | `speed` | number (km/h) | optional | Omitted if device reports speed=0 with invalid GPS fix (per [[teltonika]] convention). |