From 6548cf7fbe9fd2b5b2635565e10ed9776413e946 Mon Sep 17 00:00:00 2001 From: Justin Paul Date: Tue, 30 Jun 2026 19:43:31 -0400 Subject: [PATCH] Section 1 GUI: Vehicle Info, Emissions Readiness, Freeze Frame, Trip/Performance - Diagnostics menu: Vehicle Info (VIN/cal/ECU), Emissions Readiness (I/M monitors + MIL -> pass/fail), Freeze Frame (snapshot + capturing DTC). All routed through the scheduler one-off path; dialogs, no docked panels. - New Trip / Performance view (View menu, center page): live + average MPG, trip distance/fuel/time, and 0-60 / 1/4-mile timers. The controller keeps SPEED + MAF polled in the background and feeds TripComputer/PerformanceMeter every tick, so trips accumulate regardless of the active view. Honest MAF caveat shown for speed-density/diesel vehicles. Validated headless against MockLink: VIN dialog, readiness dialog, freeze-frame dialog, and the live trip page (28.8 mpg / distance accruing). All tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs --- README.md | 9 ++- docs/gui-trip.png | Bin 0 -> 74241 bytes gui/controller.py | 41 +++++++++++++ gui/main.py | 153 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 docs/gui-trip.png diff --git a/README.md b/README.md index 9650d75..1659a70 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,13 @@ a new vehicle is data, not code. Runs on **Windows, macOS, and Linux**. - **Gauge view** — round, tach-style gauges with tick scales, needles, **redline zones** (configurable per metric), and peak-hold. - **Table view** — value, min/max, and confidence per signal. -- **Diagnostics** — read stored/pending/permanent trouble codes and clear them - (guarded), with descriptions and **no-start codes flagged**. +- **Diagnostics** — read/clear trouble codes (guarded), with descriptions from a + built-in **1,400+ generic SAE DTC database** (profiles override) and no-start + codes flagged; plus **freeze-frame** (the snapshot when a code set). +- **Emissions readiness** — I/M monitor status + MIL → a "will it pass inspection?" + report. **Vehicle info** — VIN, calibration IDs, ECU name (Mode 09). +- **Trip / Performance** — live MPG, trip distance/fuel, and **0-60 mph & 1/4-mile** + timers (auto-detected from a standing start). - **Vehicle profiles** — switch/import/edit vehicles from the Profile menu. - **Units** — °C/°F toggle (US/metric). - **Captures** — record a session to CSV and replay it. diff --git a/docs/gui-trip.png b/docs/gui-trip.png new file mode 100644 index 0000000000000000000000000000000000000000..d901ed4d9bb06670b343bb0948306ee395bbbb55 GIT binary patch literal 74241 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 Qh5!Hn07*qoM6N<$f}f|8CjbBd literal 0 HcmV?d00001 diff --git a/gui/controller.py b/gui/controller.py index 520b6ce..29ac200 100644 --- a/gui/controller.py +++ b/gui/controller.py @@ -9,6 +9,7 @@ import time from obdcore import (PidRegistry, DtcDatabase, TimeSeriesStore, PollScheduler, CsvRecorder, load_default, load_profile) from obdcore.mock import MockLink +from obdcore.trip import TripComputer, PerformanceMeter # default poll rates (Hz) -- fast for the no-start metrics, slower for the rest FAST = {"ICP", "FICM_M", "RPM"} @@ -34,6 +35,19 @@ class Controller: self.sched = None self.t0 = None self.connected = False + self.trip = TripComputer() + self.perf = PerformanceMeter() + self.speed_key = None # PID key for standard speed (mode 01 0D) + self.maf_key = None # PID key for standard MAF (mode 01 10) + + def _find_std_keys(self): + """Locate the speed/MAF PIDs (mode 01, pid 0D/10) by any key name.""" + self.speed_key = self.maf_key = None + for p in self.reg.all(): + if p.mode == "01" and p.pid.upper() == "0D": + self.speed_key = p.key + elif p.mode == "01" and p.pid.upper() == "10": + self.maf_key = p.key def load_profile(self, path): """Switch the active vehicle profile (only allowed while disconnected).""" @@ -56,6 +70,14 @@ class Controller: self.sched = PollScheduler(self.link, self.reg, self.store, clock=time.time) self.t0 = time.time() self.connected = True + self.trip.reset() + self.perf = PerformanceMeter() + # keep speed + MAF polled in the background so trip/performance always run + self._find_std_keys() + if self.speed_key: + self.sched.subscribe(self.speed_key, 2) + if self.maf_key: + self.sched.subscribe(self.maf_key, 2) return ok def hz_for(self, key): @@ -112,6 +134,25 @@ class Controller: Returns True if the ECU acknowledged.""" return bool(self._oneoff(lambda: self.link.clear_dtcs())) + # -- standard OBD services (via the one-off path) -- + def read_vehicle_info(self): + return self._oneoff(lambda: self.link.read_vehicle_info()) + + def read_readiness(self): + return self._oneoff(lambda: self.link.read_readiness()) + + def read_freeze_frame(self): + return self._oneoff(lambda: self.link.read_freeze_frame()) + + # -- trip / performance (fed from the live store each GUI tick) -- + def update_trip(self): + spd = self.store.latest(self.speed_key) if self.speed_key else None + maf = self.store.latest(self.maf_key) if self.maf_key else None + now = time.time() + self.trip.update(now, spd, maf) + self.perf.update(now, spd) + return spd, maf + def stop(self): if self.sched: self.sched.stop() diff --git a/gui/main.py b/gui/main.py index b46540e..33fa817 100644 --- a/gui/main.py +++ b/gui/main.py @@ -80,6 +80,13 @@ class MainWindow(QtWidgets.QMainWindow): "Read stored / pending / permanent trouble codes") self.clear_dtc_act = self._act(diagm, "Clear Codes…", self._clear_codes, "Erase stored codes + freeze frame (mode 04)") + diagm.addSeparator() + self._act(diagm, "Freeze Frame", self._freeze_frame, + "Sensor snapshot captured when a code set (mode 02)") + self._act(diagm, "Emissions Readiness", self._readiness, + "I/M readiness monitors + MIL (will it pass inspection?)") + self._act(diagm, "Vehicle Info (VIN)", self._vehicle_info, + "VIN, calibration IDs, ECU name (mode 09)") viewm = mb.addMenu("&View") self.view_graph = self._act(viewm, "Graph View", lambda: self._set_view(0), @@ -89,6 +96,8 @@ class MainWindow(QtWidgets.QMainWindow): self.view_graph.setChecked(True) self.view_gauge = self._act(viewm, "Gauge View", lambda: self._set_view(2), checkable=True) + self.view_trip = self._act(viewm, "Trip / Performance", lambda: self._set_view(3), + checkable=True) viewm.addSeparator() self.show_pids = self._act(viewm, "Show PID Panel", self._toggle_pid_dock, checkable=True) @@ -332,6 +341,91 @@ class MainWindow(QtWidgets.QMainWindow): "Cleared. No codes on re-read.") self.status.showMessage("Cleared. No codes on re-read.") + # ---------- standard OBD services (dialogs) ---------- + def _need_connection(self): + if not self.ctl.connected: + QtWidgets.QMessageBox.information( + self, "Not connected", "Connect (or tick Mock) first.") + return False + return True + + def _vehicle_info(self): + if not self._need_connection(): + return + try: + info = self.ctl.read_vehicle_info() or {} + except Exception as e: + QtWidgets.QMessageBox.critical(self, "Read failed", str(e)); return + rows = [("VIN", info.get("vin") or "—"), + ("Calibration ID", info.get("calibration") or "—"), + ("ECU Name", info.get("ecu_name") or "—")] + text = "\n".join(f"{k}:\t{v}" for k, v in rows) + QtWidgets.QMessageBox.information(self, "Vehicle Info", text) + self.status.showMessage(f"VIN: {info.get('vin') or 'not reported'}") + + def _readiness(self): + if not self._need_connection(): + return + try: + r = self.ctl.read_readiness() + except Exception as e: + QtWidgets.QMessageBox.critical(self, "Read failed", str(e)); return + if not r: + QtWidgets.QMessageBox.information(self, "Readiness", "No readiness data returned.") + return + dlg = QtWidgets.QDialog(self); dlg.setWindowTitle("Emissions Readiness") + dlg.resize(420, 360) + lay = QtWidgets.QVBoxLayout(dlg) + not_ready = [m for m in r["monitors"] if not m["ready"]] + passed = (not r["mil"]) and r["dtc_count"] == 0 and len(not_ready) <= 1 + head = QtWidgets.QLabel( + f"{'LIKELY PASS' if passed else 'NOT READY'} — " + f"MIL {'ON' if r['mil'] else 'off'}, {r['dtc_count']} code(s), " + f"{r['ready_count']}/{r['total']} monitors ready " + f"({r['ignition']} ignition)") + head.setStyleSheet(f"color:{'#3cb44b' if passed else '#e6a23c'};") + head.setWordWrap(True); lay.addWidget(head) + tree = QtWidgets.QTreeWidget(); tree.setHeaderLabels(["Monitor", "Status"]) + tree.header().setStretchLastSection(False) + tree.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + for m in r["monitors"]: + it = QtWidgets.QTreeWidgetItem([m["name"], "READY" if m["ready"] else "not ready"]) + it.setForeground(1, QtGui.QBrush(QtGui.QColor("#3cb44b" if m["ready"] else "#e6a23c"))) + tree.addTopLevelItem(it) + lay.addWidget(tree) + bb = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Close) + bb.rejected.connect(dlg.reject); lay.addWidget(bb) + dlg.exec() + self.status.showMessage(f"Readiness: {r['ready_count']}/{r['total']} ready, " + f"MIL {'on' if r['mil'] else 'off'}") + + def _freeze_frame(self): + if not self._need_connection(): + return + try: + ff = self.ctl.read_freeze_frame() or {} + except Exception as e: + QtWidgets.QMessageBox.critical(self, "Read failed", str(e)); return + vals = ff.get("values") or [] + if not vals and not ff.get("dtc"): + QtWidgets.QMessageBox.information(self, "Freeze Frame", + "No freeze-frame data stored (no fault has captured one).") + return + dlg = QtWidgets.QDialog(self); dlg.setWindowTitle("Freeze Frame") + dlg.resize(440, 380) + lay = QtWidgets.QVBoxLayout(dlg) + d = ff.get("dtc") + cap = self.ctl.dtcdb.get(d).desc if d else "(unknown)" + lay.addWidget(QtWidgets.QLabel(f"Captured by: {d or '—'} — {cap}")) + tree = QtWidgets.QTreeWidget(); tree.setHeaderLabels(["Signal", "Value"]) + tree.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) + for name, val, unit in vals: + tree.addTopLevelItem(QtWidgets.QTreeWidgetItem([name, f"{val} {unit}".strip()])) + lay.addWidget(tree) + bb = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Close) + bb.rejected.connect(dlg.reject); lay.addWidget(bb) + dlg.exec() + # ---------- center (graph + table stack) ---------- def _build_center(self): self.stack = QtWidgets.QStackedWidget() @@ -366,9 +460,47 @@ class MainWindow(QtWidgets.QMainWindow): self.gauges = GaugeGrid() self.stack.addWidget(self.gauges) + # trip / performance page (center index 3) + self.stack.addWidget(self._build_trip_page()) + self.setCentralWidget(self.stack) self._apply_theme() + def _build_trip_page(self): + page = QtWidgets.QWidget() + page.setStyleSheet("background:#111; color:#ddd;") + lay = QtWidgets.QVBoxLayout(page) + lay.setContentsMargins(24, 24, 24, 24) + self._trip_labels = {} + + def big(title): + box = QtWidgets.QFrame() + box.setStyleSheet("QFrame{background:#1a1a1a;border-radius:8px;}") + v = QtWidgets.QVBoxLayout(box) + t = QtWidgets.QLabel(title); t.setStyleSheet("color:#999;font-size:11px;") + val = QtWidgets.QLabel("--"); val.setStyleSheet("color:#fff;font-size:26px;font-weight:bold;") + v.addWidget(t); v.addWidget(val) + return box, val + + grid = QtWidgets.QGridLayout() + cards = [("Instant MPG", "inst_mpg"), ("Average MPG", "avg_mpg"), + ("Trip Distance (mi)", "dist"), ("Fuel Used (gal)", "fuel"), + ("0-60 mph (s)", "zero60"), ("1/4 mile (s)", "quarter"), + ("Speed (mph)", "speed"), ("Trip Time", "time")] + for i, (title, key) in enumerate(cards): + box, val = big(title) + self._trip_labels[key] = val + grid.addWidget(box, i // 4, i % 4) + lay.addLayout(grid) + self._trip_note = QtWidgets.QLabel( + "MPG needs a MAF sensor (speed-density/diesel vehicles report distance + " + "0-60 only). Best 0-60 / 1/4-mile are kept; pull away from a stop to time a run.") + self._trip_note.setWordWrap(True) + self._trip_note.setStyleSheet("color:#888;font-size:11px;") + lay.addWidget(self._trip_note) + lay.addStretch(1) + return page + def _graph(self): """The active graph widget (multi-axis unless Normalize is on).""" return self.single if self.norm_chk.isChecked() else self.multi @@ -606,6 +738,7 @@ class MainWindow(QtWidgets.QMainWindow): self.view_graph.setChecked(idx == 0) self.view_table.setChecked(idx == 1) self.view_gauge.setChecked(idx == 2) + self.view_trip.setChecked(idx == 3) def _toggle_pid_dock(self): self.pid_dock.setVisible(self.show_pids.isChecked()) @@ -743,15 +876,33 @@ class MainWindow(QtWidgets.QMainWindow): self.table.item(r, 3).setText("--" if dlo is None else f"{dlo:g}") self.table.item(r, 4).setText("--" if dhi is None else f"{dhi:g}") self.tree.blockSignals(False) - if self.stack.currentIndex() == 2: # gauge view + spd, maf = self.ctl.update_trip() # accumulate trip/perf every tick + idx = self.stack.currentIndex() + if idx == 2: # gauge view for key in self.curves: p = self.ctl.reg.get(key) lo, hi = self.ctl.store.minmax(key) self.gauges.set_value(key, self._dval(p, self.ctl.store.latest(key)), peak=self._dval(p, hi)) + elif idx == 3: # trip / performance view + self._update_trip_page(spd, maf) else: self._redraw_curves() + def _update_trip_page(self, spd, maf): + t, s = self.ctl.trip, self.ctl.trip.stats() + L = self._trip_labels + L["inst_mpg"].setText(f"{t.instant_mpg(spd, maf):.1f}" if (spd and maf) else "--") + L["avg_mpg"].setText(f"{s['avg_mpg']:.1f}" if self.ctl.maf_key else "n/a (no MAF)") + L["dist"].setText(f"{s['distance_mi']:.2f}") + L["fuel"].setText(f"{s['fuel_gal']:.3f}" if self.ctl.maf_key else "n/a") + L["speed"].setText(f"{spd / 1.60934:.0f}" if spd is not None else "--") + mm, ss = divmod(int(s["elapsed_s"]), 60) + L["time"].setText(f"{mm}:{ss:02d}") + pm = self.ctl.perf + L["zero60"].setText(f"{pm.best_0_60}" if pm.best_0_60 else "--") + L["quarter"].setText(f"{pm.best_quarter}" if pm.best_quarter else "--") + def closeEvent(self, ev): try: self.timer.stop()