Files
obdash/ARCHITECTURE.md
T
justin 6bee9c0d7f Scaffold obdcore (headless acquisition core) + ARCHITECTURE.md
Foundation for the PySide6 + pyqtgraph Windows GUI, shared with the terminal
tool. Pure data/IO -- no Qt, no curses.

obdcore/
  link.py      ElmLink   -- ELM327 serial (Mode-01/22, ATRV, DTC read/clear)
  mock.py      MockLink  -- synthetic crank for tests + GUI dev (no truck)
  registry.py  PidRegistry (verified Ford 6.0 PIDs + confidence) + DtcDatabase
  scheduler.py PollScheduler -- prioritized round-robin polling, dead-PID park,
               derived channels; tick() is fake-clock test-drivable
  store.py     TimeSeriesStore (ring buffers + min/max) + CsvRecorder/replay

Design centers on the ELM327 bandwidth limit (~7-15 reads/sec): the active
view subscribes PIDs at chosen rates; acquisition runs off the UI thread;
the GUI only reads the store. FICM_M (09D0) promoted to verified after the
2026-06-30 on-truck crank read (48.0V, intermittent).

tests/test_obdcore.py: decoders vs real truck bytes, crank ramp + peak,
derived BOOST, dead-PID park/revive, record/replay roundtrip -- all pass.

ARCHITECTURE.md: layers, data model, GUI plan, 6.0 stock-PID limits
(no EGT/oil-PSI), feature backlog, P0-P5 roadmap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
2026-06-30 13:41:24 -04:00

135 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ford-obd — Architecture & Roadmap
Plan for growing the terminal `obd_reader.py` into a graphical Windows scan
tool for the 6.0L Power Stroke, on a shared headless core.
## Vision
A desktop app with:
- a **PID browser** (searchable list, live values) on the side,
- a flexible **graph workspace** — overlay many metrics on one plot, split into
a grid of single-metric plots, or show gauges (one metric each),
- purpose-built **perspectives**: Cranking, Driving, Diagnostics (DTCs), Logging,
- a **Ford DTC database** behind the codes page,
- **session record + playback** so intermittent faults can be reviewed offline.
## The constraint that shapes everything: ELM327 bandwidth
The ELM327 is a one-request-at-a-time link: each Mode-22 round-trip is
~40150 ms over the CH340, so total throughput is **~715 PID reads/sec**,
shared across the whole UI. Consequences baked into the design:
- A **prioritized polling scheduler** owns the link. The active view subscribes
the PIDs it needs at the rates it needs (cranking = ICP fast, ignore the rest).
- Acquisition runs **off the UI thread**; the GUI only reads the store.
- Per-PID rates and dead-PID parking keep the sample rate from collapsing.
- A faster adapter (**OBDLink SX/MX+**, STN chip — batched PIDs, faster
protocols) multiplies throughput; the link layer is abstracted so either works.
## Layers
```
+------------------- GUI (PySide6 + pyqtgraph) -------------------+
| PID browser | Graph workspace | Perspectives | DTC page | Log |
+--------------------------------|-------------------------------+
reads only | (Qt thread)
+--------------------------------v-------------------------------+
| obdcore (headless) |
| PollScheduler --reads--> ElmLink / MockLink --serial--> PCM |
| | pushes samples |
| v |
| TimeSeriesStore <--- CsvRecorder / replay_csv |
| PidRegistry (verified PIDs) DtcDatabase |
+-----------------------------------------------------------------+
```
`obdcore` is pure data/IO — no Qt, no curses — so it's shared by the terminal
tool, the GUI, and tests.
### obdcore modules (built, tested)
| Module | Responsibility |
|---|---|
| `link.py` `ElmLink` | ELM327 serial: init, protocol negotiate, Mode-01/22 reads, ATRV, DTC read/clear. Returns raw bytes. |
| `mock.py` `MockLink` | Synthetic crank (ICP ramp, FICM ~48V, batt sag) — same interface; powers tests + GUI dev with no truck. |
| `registry.py` `PidRegistry` | Verified Ford 6.0 PID table (corrected addresses + scaling + confidence) and subscription presets. `DtcDatabase` seeds the code DB. |
| `scheduler.py` `PollScheduler` | Prioritized round-robin polling; per-PID Hz; derived channels; dead-PID park/revive. `tick()` is test-drivable with a fake clock. |
| `store.py` `TimeSeriesStore` | Per-PID ring buffers + min/max; `CsvRecorder` (long format) + `replay_csv` for record/playback. |
Tests: `tests/test_obdcore.py` — decoders vs real truck bytes, crank ramp +
peak capture, derived BOOST, dead-PID parking, record/replay round-trip.
## Data model
- **Pid**: key, name, mode (`01`/`22`/`atrv`/`derived`), pid hex, nbytes,
decode fn, unit, group, vmin/vmax, **confidence** (`verified|doc|tentative`),
deps (for derived), notes.
- **Channel**: rolling `(t, value)` + session min/max.
- **Derived/virtual channels**: e.g. `BOOST = MAP BARO`; later `ICP_error =
ICP_DES ICP`, `FICM_sag = FICM_M FICM_V`.
## GUI plan (PySide6 + pyqtgraph)
- **Left dock**: `QTreeView` PID browser grouped by system (fuel/ficm/air/…),
live value + confidence badge, checkbox to add to the focused panel.
- **Central workspace**: dockable/tabbed panels. Panel types:
- *Overlay plot* — many PIDs, multiple Y-axes (pyqtgraph `ViewBox` linking).
- *Split grid* — one plot per PID.
- *Gauge* — radial/linear single metric with warn/crit bands.
Drag a PID from the browser onto a panel to add it.
- **Perspectives** (saved layouts):
- **Cranking** — ICP big readout + 500-psi firing line + peak-hold + trace
(port of terminal `--crank`).
- **Driving** — boost (MGP), EOT, ECT, EBP, load, RPM, IPR, FICM, trans temp.
- **Diagnostics** — DTC read/clear (guarded) + freeze frame, joined to the
Ford DTC DB (description, causes, no-start relevance).
- **Logging/Playback** — record a session; scrub/replay through the graphs.
- **Settings** — COM port, baud, protocol, per-PID rates, units (psi/°F vs kPa/°C),
dark/night theme.
- **Bottom status** — adapter, protocol, port voltage, dropped-response rate.
### Honest 6.0 data-stream limits
Two commonly-wanted gauges are **not in the stock 6.0 PCM stream** from the OBD
port and need aftermarket sensors:
- **Engine oil PRESSURE** — only ICP (injection oil) + EOT (oil temp) exist; lube
pressure is an idiot-light switch, not a PID.
- **EGT** — only EBP (exhaust *back pressure*) exists; exhaust *gas temperature*
is an add-on pyrometer.
Plan: present what the PCM exposes; design an **aux-input** path so external
sensors (e.g. a serial/analog EGT/oil-PSI module) can feed extra channels later.
## Additional features (backlog)
- Session record + **playback/scrub** (highest value; foundation already in `store`).
- Bi-directional tests: **KOEO/KOER self-tests, injector buzz, cylinder
contribution/balance**.
- **Alarms** + min/max hold per PID (EOT>230°F, ICP<500 cranking).
- Timeline **annotations** ("started cranking", "stabbed throttle").
- **Computed channels** (ICP error, FICM sag).
- **Multi-vehicle profiles** + per-truck DTC history.
- **Export/report** (CSV/PDF, graph screenshots, one-click "share state").
- **PID discovery scan** in GUI (the brute-scan, auto-add hits).
- Units toggle, night theme, big-touch cab mode.
## Roadmap
- **P0 — core (this commit):** `obdcore` package + tests + this doc. *Next:*
migrate `obd_reader.py` to import `obdcore` (remove the duplicated ELM/PID
logic) so terminal + GUI share one source of truth.
- **P1 — GUI shell:** PySide6 window, connect dialog, PID browser, one overlay
plot fed by the scheduler/store. Validate against `MockLink` first.
- **P2 — panels + perspectives:** split plots, gauges, Cranking + Driving views.
- **P3 — diagnostics:** DTC read/clear page + Ford DTC DB (built by the
cross-verified workflow, same method as the PID hunt).
- **P4 — record/playback + alarms + computed channels.**
- **P5 — packaging:** PyInstaller one-file `.exe` (+ CH340 driver note),
optional code-signing; OBDLink/STN fast-path support.
## Dependencies
- Runtime (core): `pyserial`.
- GUI: `PySide6`, `pyqtgraph`, `numpy`.
- Dev: `pytest`, `pyinstaller`.