feat(sim): enable DIR rotor-offset/resolver learning on the sim bench #21

Merged
outlandnish merged 3 commits from feat/dir-learn-bench-enable into main 2026-09-11 14:38:05 -05:00
Owner

What

Enable the DIR rotor-offset and resolver / resolver-error learning procedures to run on the sim bench, by answering the MCU-published CID gates they need from CAN — the way the car does — instead of hand-maintaining each mapping.

How

  • VAPI alias table (vapi_registry.py, new) — emulate libQtCarVAPI's init_array under Unicorn and extract the firmware's own DataValue registration table (name ← ETH source signal, render kind: enum/bool/num). BenchBackend._derive_cid now resolves aliases (VAPI_shiftState ← DI_gear via ShiftStateNameMap, plus ~300 more) from it; the hand-written DI_gear map is deleted. The table is cached per-rev (gitignored); an empty registry (no MCU libs) falls back to seeds, so a firmware-less bench still runs.
  • Computed values stay explicit (no single-signal alias exists): VAPI_{drive,acc,hvac}RailOn from VCFRONT_vehiclePowerState (0x221); GUI_tractionControlModeRequest from DI_tractionControlMode.
  • Service mode — UI_serviceMode on 0x284, driven by a per-procedure session that toggles vehicle_sim's service mode. That is the DIR's learn-routine start gate (FAILED_INCORRECT_CONDITIONS without it).
  • GTW_time carries a real clock on the 2022 variant so the brake-temp estimator does not trip DI_a228.
  • odj_codec fix — a negative value on a uint ODJ field now wraps to two's complement (ROTOR_TEMPERATURE −40 → 0xD8) instead of raising OverflowError, which unblocks the ROTOR-OFFSET start request.

Readiness

Resolver and resolver-error learning were already fully wired (extended session, TesterPresent keepalive, SecurityAccess L5 tesla_hash, routines decode to named results). This makes rotor-offset work too. Remaining dependencies are physical, not code: the DIR must be in dyno mode and actually spinning for a routine to complete.

Tests

318 pass — new tests/test_vapi_registry.py; extended test_cid.py, test_odj_codec.py, test_vehicle_sim_nodes.py. Ruff clean, LF endings.

🤖 Generated with Claude Code

## What Enable the DIR rotor-offset and resolver / resolver-error learning procedures to run on the sim bench, by answering the MCU-published CID gates they need from CAN — the way the car does — instead of hand-maintaining each mapping. ## How - **VAPI alias table (`vapi_registry.py`, new)** — emulate `libQtCarVAPI`'s `init_array` under Unicorn and extract the firmware's own `DataValue` registration table (name ← ETH source signal, render kind: enum/bool/num). `BenchBackend._derive_cid` now resolves aliases (`VAPI_shiftState ← DI_gear` via `ShiftStateNameMap`, plus ~300 more) from it; the hand-written `DI_gear` map is deleted. The table is cached per-rev (gitignored); an empty registry (no MCU libs) falls back to seeds, so a firmware-less bench still runs. - **Computed values** stay explicit (no single-signal alias exists): `VAPI_{drive,acc,hvac}RailOn` from `VCFRONT_vehiclePowerState` (0x221); `GUI_tractionControlModeRequest` from `DI_tractionControlMode`. - **Service mode** — `UI_serviceMode` on 0x284, driven by a per-procedure session that toggles vehicle_sim's service mode. That is the DIR's learn-routine start gate (`FAILED_INCORRECT_CONDITIONS` without it). - **`GTW_time`** carries a real clock on the 2022 variant so the brake-temp estimator does not trip `DI_a228`. - **`odj_codec` fix** — a negative value on a `uint` ODJ field now wraps to two's complement (`ROTOR_TEMPERATURE` −40 → `0xD8`) instead of raising `OverflowError`, which unblocks the ROTOR-OFFSET start request. ## Readiness Resolver and resolver-error learning were already fully wired (extended session, TesterPresent keepalive, SecurityAccess L5 `tesla_hash`, routines decode to named results). This makes rotor-offset work too. Remaining dependencies are physical, not code: the DIR must be in dyno mode and actually spinning for a routine to complete. ## Tests 318 pass — new `tests/test_vapi_registry.py`; extended `test_cid.py`, `test_odj_codec.py`, `test_vehicle_sim_nodes.py`. Ruff clean, LF endings. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Answer the MCU-published CID gates these procedures need from CAN, the way the
car does, instead of hand-maintaining each mapping:

- vapi_registry: extract libQtCarVAPI's DataValue alias table by emulating its
  init_array under Unicorn (name + ETH source + render kind per DataValue). The
  bench's _derive_cid now resolves VAPI_shiftState <- DI_gear and ~300 more from
  the firmware's own table; the hand DI_gear map is gone. Cached per-rev
  (gitignored); empty when the MCU libs are absent, so it degrades to seeds.
- Computed values (no single-signal alias) stay explicit: VAPI_{drive,acc,hvac}
  RailOn from VCFRONT_vehiclePowerState (0x221); GUI_tractionControlModeRequest
  from DI_tractionControlMode.
- Service mode on the bus: UI_serviceMode in 0x284, driven by a per-procedure
  session that toggles vehicle_sim's service mode -- the DIR's learn start gate.
- GTW_time carries a real clock on 2022 so the brake-temp estimator does not
  trip DI_a228.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(uds): encode a negative value on a uint ODJ field as two's complement
Some checks failed
Tests / test (pull_request) Has been cancelled
9a2d588f7a
ROTOR_LEARNING's ROTOR_TEMPERATURE is typed uint in the ODJ, but the DIR learn
script sends a signed degC (-40) and the ECU reads it as two's complement. Wrap
a negative value into the field width instead of raising OverflowError, so -40
packs as 0xD8. Signed ('int') fields and non-negative values are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(web): show a live run-log tail in the ODIN run modal
Some checks failed
Tests / test (pull_request) Has been cancelled
1dccacb884
The run scrim covers the pane's run log, so a normal procedure's step
narration was invisible behind it -- the modal showed only the progress
bar, which only earns its keep on a flash. Mirror the newest log lines
into a short scrolling tail in the modal (capped at 50, auto-scrolled),
colored by kind. The bar stays for a flash's byte counts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
outlandnish force-pushed feat/dir-learn-bench-enable from 1dccacb884
Some checks failed
Tests / test (pull_request) Has been cancelled
to f9a70b9b91
Some checks failed
Tests / test (pull_request) Has been cancelled
2026-09-11 14:37:00 -05:00
Compare
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
outlandnish/tm3diag!21
No description provided.