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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user