Skip to content

feat(gr26): persist CAN frames and trace signals back to source#139

Open
BK1031 wants to merge 5 commits intomainfrom
bk1031/gr26-can-trace
Open

feat(gr26): persist CAN frames and trace signals back to source#139
BK1031 wants to merge 5 commits intomainfrom
bk1031/gr26-can-trace

Conversation

@BK1031
Copy link
Copy Markdown
Contributor

@BK1031 BK1031 commented May 8, 2026

  • New gr26_can table stores each decoded CAN frame (vehicle, node, can id, post-MQTT-header bytes, timestamp) with a composite unique key on (vehicle_id, node_id, timestamp).
  • New gr26_can_signal table is a 1-N join keyed on signal_id that maps each persisted signal back to the frame it came from.
  • HandleMessage now upserts the gr26_can row first, persists signals, then bulk-inserts the join rows.
  • Signal upsert no longer overwrites id on conflict and uses clause.Returning so signals[i].ID reflects the actually-stored value across retransmits — required because the join table assumes signal ids are stable.
  • WS publish moves to after the DB insert so subscribers see the persistent id rather than the freshly-generated-then-discarded one.
  • Both new tables are gr26-only; the shared signal table and mapache-go are untouched so this can change year-to-year without affecting other services.

Adds two gr26-local tables. gr26_can stores the raw CAN frame (vehicle,
node, can id, post-MQTT-header bytes, timestamp) with a composite unique
key on (vehicle_id, node_id, timestamp). gr26_can_signal is a 1-N join
keyed on signal_id that maps each persisted signal back to the frame it
was decoded from. Both tables are gr26-only so the schema can change
year-to-year without touching the shared signal table or mapache-go.

HandleMessage now upserts the gr26_can row first (returning the stored
ulid), persists the decoded signals, then bulk-inserts the join rows.

The signal upsert in CreateSignals stops overwriting id on conflict and
uses Returning so signals[i].ID reflects the actually-stored value
across retransmits — the join table assumes signal ids are stable. WS
publish moves to after the DB insert so subscribers see the persistent
id rather than the freshly-generated-then-discarded one.
@netlify
Copy link
Copy Markdown

netlify Bot commented May 8, 2026

Deploy Preview for gr-mapache canceled.

Name Link
🔨 Latest commit 2d6ae22
🔍 Latest deploy log https://app.netlify.com/projects/gr-mapache/deploys/69fd44afaa4dcc00088388c0

BK1031 added 4 commits May 7, 2026 19:00
Move CreateCAN ahead of the messageStruct lookup and FillFromBytes step
so unknown can ids and decode failures still produce a gr26_can row.
The frame ends up in the database with no signals or join rows, which
is the right shape for "what bytes did we receive that we couldn't
parse" debugging — same dataset answers both "trace this signal" and
"why did this signal never appear."

Length and upload-key checks still bail before any persistence: a frame
shorter than the MQTT envelope can't be sliced safely, and an invalid
upload key means the data isn't trusted to belong to the vehicle.
Adds a nullable jsonb metadata column on gr26_can. HandleMessage now
attempts the decode first, then writes the frame in a single insert
with the outcome stamped in:

  - decode succeeded -> metadata is null
  - unknown can id   -> {"status":"unknown_can_id"}
  - decode error     -> {"status":"decode_error","error":"..."}

Single write per frame instead of insert-then-update, and the upsert's
DoUpdates picks up the new column so retransmits with different
outcomes overwrite the old metadata.

Queries: "frames we couldn't decode" is now a one-liner — WHERE
metadata IS NOT NULL — without the LEFT JOIN against gr26_can_signal.
Pairs the categorical status field with a human-readable note. Renames
the prior decode_error 'error' field to 'note' so both states share a
schema:

  {"status":"unknown_can_id","note":"no decoder registered for can id 0x999"}
  {"status":"decode_error","note":"<err.Error()>"}
Successful decodes now write {"status":"ok"} into gr26_can.metadata
instead of leaving it null. Every frame has a populated metadata field,
so the decode-outcome filter is metadata->>'status' across the board.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant