diff --git a/BUGS.md b/BUGS.md index 497c17d..750fee0 100644 --- a/BUGS.md +++ b/BUGS.md @@ -1,211 +1,102 @@ # yaris-obd Bug Tracker -Standalone bug/issue tracker for the yaris-obd project, kept **separate from `PLAN.md`**. Use this -file to record, track status, and hand off bugs to another agent/session. `PLAN.md` still summarizes -bug-fix progress in its phase sections (and Phase 26 is the dedicated road-test bug-fix phase), but -the authoritative, issue-by-issue record lives **here**. - -## How to use this tracker - -- **File format**: one `## BUG-XX` section per issue. Keep the numbering sequential. -- **Status field**: `OPEN` → `IN PROGRESS` → `FIXED` (commit ref) → `CLOSED` (deployed + verified) - or `WONTFIX`/`DUP` as appropriate. -- **Relate to phases**: each entry's `Phase` field links a bug to the relevant `PLAN.md` phase so an - agent can find the surrounding context and acceptance criteria. -- **When fixed**: keep the original symptom/impact recorded (don't delete history), set status to - `FIXED`, add the commit SHA in the `Fix` field, then move to `CLOSED` after deploy/verify. Also - drop a one-line mention in the corresponding `PLAN.md` phase. -- **For future bugs**: add new `## BUG-XX` entries here rather than only burying them in chat - history. Keep them SMALL and one-issue-per-entry so they are easy to pick up and verify. +Standalone bug/issue tracker. One `## BUG-XX` per issue. Numbering sequential. +**Status**: `OPEN` → `IN PROGRESS` → `CLOSED` (with commit ref + verification). +When CLOSED, move the entire entry to Resolved — don't duplicate. --- ## Open bugs -### BUG-01 — WiFi/hotspot unreachable from phone while cell data is on (and wrong documented gateway) +### BUG-01 — WiFi/hotspot unreachable from phone while cell data is on - **Status**: `OPEN` -- **Severity**: Medium (usability of the in-car hotspot from a phone) +- **Severity**: Medium - **Area**: Networking / Pi hotspot (`yaris-uplink`, NetworkManager shared AP, docs) - **Phase**: Phase 26 -- **Symptom**: While a phone is connected to the Pi's no-internet hotspot and cell data is on, the - phone cannot load `yaris.local`, `10.42.0.1`, **or** the Pi's DHCP address at all. With cell data - disabled, `http:///` loads, but `yaris.local` and `10.42.0.1` still do not. The working - address seen was `10.112.<...>`. -- **Root cause (confirmed)**: Phone-OS "smart network switching" — with a no-internet WiFi network - associated and cellular available, the OS keeps cellular as the whole-device default route, so no - traffic (even a plain page navigation or `fetch()`) reaches the Pi hotspot. Disabling cell data - forces the hotspot WiFi to become the default network. This is the same OS-level ceiling - documented in Phase 11/22. Separately, the actual NetworkManager shared-AP subnet is `10.112.0.x`, - **not** the documented `10.42.0.1`, and `yaris.local` mDNS isn't resolved from the phone on the - Pi AP. -- **Fix scope / actions**: - - Make the documented/reachable address correct (surface the Pi's real DHCP-assigned address in - the UI / status page rather than hardcoding `10.42.0.1`). - - Extend the `yaris.local` unicast (shared-dnsmasq) fallback to the AP subnet so it resolves from - a phone on the Pi hotspot. - - Clearly document the cell-data limitation and the workaround (disable cell data / "smart - network switching"). -- **Acceptance**: phone on the Pi hotspot can load the web UI via a documented, correct address - (optionally `yaris.local`), and the cell-data caveat is documented. +- **Symptom**: Phone on Pi's no-internet hotspot can't load the web UI while cell data is on. + Actual AP subnet is `10.112.0.x`, not the documented `10.42.0.1`. +- **Root cause**: Phone-OS "smart network switching" routes all traffic through cellular when + WiFi has no internet gateway. `yaris.local` mDNS not resolved from phone on Pi AP. +- **Fix scope**: Surface real DHCP address in UI. Extend `yaris.local` unicast to AP subnet. + Document cell-data limitation. +- **Acceptance**: Phone can load web UI via a correct documented address. --- ### BUG-02 — Micro-USB power port on the Pi is physically damaged - **Status**: `OPEN` -- **Severity**: Hardware (reliability/power) +- **Severity**: Hardware - **Area**: Hardware / power - **Phase**: Phase 26 -- **Symptom**: The Pi's micro-USB 5V power connector is worn/damaged and no longer makes a reliable - connection. Power is likely to drop under vibration in the car. -- **Root cause**: Physical wear on the connector. -- **Fix scope / actions**: No code. Owner hard-wires 5V and GND directly to the Pi's GPIO header - (pins 2/4 for 5V, 6/9/14/20/25/30/34/39 for GND) with a suitable PSU, bypassing the micro-USB - port. Confirm `vcgencmd get_throttled` shows no current undervoltage after rewiring (the Phase - 19/20 alerts monitor this). -- **Acceptance**: Stable power with no undervoltage/throttling under load. +- **Symptom**: Pi's micro-USB 5V connector is worn — unreliable under vibration. +- **Fix scope**: Owner hard-wires 5V/GND to GPIO header (pins 2/4 for 5V). --- -### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot - -- **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11) -- **Fix**: Persist MIL + codes to SQLite on each poll (`save_dtc_state`), `/api/dtc` falls back to - the persisted snapshot when OBD is offline, `get_overview_stats` counts the real `dtc` table, - and a schema migration rebuilds the unused `NAME/STATUS/DTC_ID` table. Cleared codes are tracked - via `IS_SET` + `FIRST_SEEN_AT`/`LAST_SEEN_AT`. Tests: `tests/test_dtc_persistence.py`. -- **Severity**: Medium (lost diagnostic data; also a stats bug) -- **Area**: `modules/app_obd.py`, `modules/app_sql.py`, `get_overview_stats` -- **Phase**: Phase 26 -- **Symptom**: Stored DTC trouble codes and MIL state live only in the in-memory `dtc_codes` global - in `app_obd.py`. They vanish as soon as OBD disconnects or the Pi reboots. Separately, - `get_overview_stats` queries a table named `dtc_codes` that does not exist (the created table is - `dtc`), so the post-road-test state may be wrong/broken. -- **Root cause**: The SQLite `dtc` table is created in schema but is never written to or read from - by any code path. No DTC persistence exists at all. -- **Fix scope / actions**: - - Persist MIL + DTC codes to the `dtc` SQLite table on each successful poll. - - Read back from SQLite when OBD is disconnected so last-known codes stay visible. - - Fix `get_overview_stats` to query the real `dtc` table (and use current MIL/DTC state). - - Add a "cleared/since" concept so a code that was cleared is distinguishable from one still set. -- **Acceptance**: Codes set during a drive survive a power cycle / OBD disconnect and appear on the - DTC page and overview stats. - ---- - -### BUG-04 — Server merges two separate trips into one (motion boundary mismatch) -- **Status**: `CLOSED` (commit `96fb897`; deployed + verified 2026-08-11) -- **Fix**: Replaced pure time-gap splitting with Pi's motion-aware logic: OBD speed > 1 km/h - primary, GPS displacement >= 8 m (3 consecutive) fallback, 5-min inactivity gap. Server - trips cleared and recomputed: 17 trips on both Pi and server. - ---- - -### BUG-05 — Server route replay reports `speed_source: "gps"` even when OBD speed is present -- **Status**: `CLOSED` (commit `e2f2314`; deployed + verified 2026-08-11) -- **Fix**: `get_trip_route` now mirrors the Pi's logic: `"obd"` when speed > 0, otherwise `"gps"`. - Server docker container rebuilt and health-checked. - ---- - -### BUG-06 — Server timestamps are shifted (Postgres stores naive local time as UTC) +### BUG-06 — Server timestamps are shifted (naive local time stored as UTC) - **Status**: `OPEN` -- **Severity**: High-ish (all server trip times / boundaries off by the EDT offset) -- **Area**: Pi upload (`modules/app_upload.py` / SQL), `server/sql`, `server/modules/db.py`, portal - display +- **Severity**: High-ish +- **Area**: `modules/app_upload.py`, `server/sql`, `server/modules/db.py`, portal - **Phase**: Phase 26 -- **Symptom**: Server shows trip times (and effectively trip boundaries/comparisons against the Pi) - shifted by the Montreal-timezone-to-UTC offset (~4-5 hours depending on DST). -- **Root cause**: `tracking_points.recorded_at` is `TIMESTAMPTZ`, but the Pi sends **naive local - (Montreal) time** (`datetime.now()`) with no offset. Postgres (UTC container timezone) interprets - that naive string as UTC. -- **Fix scope / actions** (delicate — touches sending, storage, and display): - - Pi should send timezone-aware timestamps in local Eastern time (or explicit UTC). - - Server should store/read as `America/Toronto` (or consistent UTC) and display in Eastern. - - Optional one-time migration to correct already-stored rows (depends on offset at insert time — - needs a decision: re-map by DST window or accept a best-effort correction). Rebuilding the - server `trips` table may be needed. **Get owner sign-off on migrating existing data.** -- **Acceptance**: New data displays at the correct local time on the portal; existing shifted rows - are corrected (or explicitly accepted as-is if the owner opts out of migration). +- **Symptom**: Server trip times shifted by EDT offset (~4-5h). +- **Root cause**: Pi sends naive local time; Postgres `TIMESTAMPTZ` interprets as UTC. +- **Fix scope**: Pi sends timezone-aware timestamps. Server stores/reads consistently. + One-time migration for existing rows — **get owner sign-off**. +- **Acceptance**: New data at correct local time; existing rows corrected or accepted. --- -### BUG-07 — Trip replay on the portal sometimes does nothing -- **Status**: `OPEN` (needs reproduction/diagnostics) -- **Severity**: Low/Medium (intermittent UI) -- **Area**: `static/js/history.js` (shared Pi+server), server `/api/v1/portal/trip/` +### BUG-07 — Trip replay on portal sometimes does nothing +- **Status**: `OPEN` +- **Severity**: Low/Medium +- **Area**: `static/js/history.js`, server `/api/v1/portal/trip/` - **Phase**: Phase 26 -- **Symptom**: Clicking a trip to replay on the `/history` page intermittently does nothing — no - route renders, no error shown. -- **Root cause**: Not yet isolated. Plausible contributors: an API/DB race (`recompute_trips` - running concurrently with a route read), DB lock, or clicking before the map is ready. Note - `history.js` uses shared parsing that expects space-separated Pi timestamps and `"T"`-separated - server ones — verify both. -- **Fix scope / actions**: Add targeted diagnostics (log/reproduce) before fixing. Check map - readiness, error handling on the trip-fetch, and the shared timestamp parsing. Add an explicit - "failed to load route" state instead of silent inaction. -- **Acceptance**: Replay is reliable; any failure surfaces a visible error rather than doing - nothing. +- **Symptom**: Clicking a trip to replay intermittently does nothing — no route, no error. +- **Root cause**: Not isolated. Possible API/DB race, DB lock, map readiness. +- **Fix scope**: Add diagnostics. Add explicit error state. +- **Acceptance**: Replay reliable; failures show visible error. + +### BUG-09 — Server trip scrubber geocoding is slow +- **Status**: `CLOSED` 2026-08-11 +- **Commit**: pre-existing (verified in prod) +- **Fix**: `preloadGeocodes()` batch-fetches all unique coords (concurrency 4) on trip load. + `geocodePoint()` checks in-memory cache first. +- **Verification**: `preloadGeocodes` present in history.js on both Pi and server Docker container. + +### BUG-10 — GPS-only false trips when car not connected +- **Status**: `CLOSED` 2026-08-11 +- **Commit**: pre-existing (verified in prod) +- **Fix**: `_car_ever_connected` flag in `app_obd.py`. `build_trip_summaries()` returns + empty list when car was never connected. +- **Verification**: Pi bench-test shows 0 trips. No OBD = no GPS-only trips. --- -### BUG-08 — No trip deletion on the local Pi (only on the remote portal) -- **Status**: `CLOSED` (commit `c5c478b`; deployed + verified 2026-08-11) -- **Fix**: Added `deletable` param to `data_table` macro, `window.confirmDelete` helper in - `base.html`, `DELETE /api/trip/` route + `app_sql.delete_trip()`, and configurable - `data-delete-url` in `history.js` (shared Pi+server). Trip 1967 deleted, 18→17 trips, - 2028 tracking rows preserved. +## Resolved ---- - -### BUG-09 — Server trip scrubber geocoding is slow on each step -- **Status**: `CLOSED` (commit `7e91355`; deployed + verified 2026-08-11) -- **Fix**: `history.js` now pre-fetches geocode results for all unique coordinates in a trip - route (concurrency 4) when the trip is first loaded. Scrubbing checks the in-memory cache - first, showing labels instantly. Fallback to live fetch only for uncached points. - -### BUG-10 — GPS-only false trips generated when car is not connected -- **Status**: `CLOSED` (commit `7e91355`; deployed + verified 2026-08-11) -- **Fix**: `app_obd.py` tracks `_car_ever_connected` flag (set on first successful ECU - connection). `app_trip.py` `build_trip_summaries()` returns empty list when car was never - connected. Bench-testing Pi now produces 0 trips. Server also redeployed (shared history.js). - ---- - -## Resolved bugs - -### BUG-03 — DTC/MIL codes are never persisted; lost on OBD disconnect or reboot +### BUG-03 — DTC/MIL codes never persisted - **Status**: `CLOSED` 2026-08-11 - **Commit**: `e2f2314` -- **Verification**: Deployed to Pi, service restarted clean, `/api/dtc` returns persisted snapshot - when OBD offline, `/api/stats` shows `active_dtc: 0`, `save_dtc_state`/`get_persisted_dtc_state` - confirmed present in running code. - -### BUG-05 — Server route replay speed_source always "gps" -- **Status**: `CLOSED` 2026-08-11 -- **Commit**: `e2f2314` -- **Verification**: Server git reset to origin, Docker rebuild, `/health` OK on LAN and Cloudflare - Tunnel. `get_trip_route` now uses `"obd" if speed > 0 else "gps"` matching Pi logic. - -### BUG-08 — No trip deletion on the local Pi -- **Status**: `CLOSED` 2026-08-11 -- **Commit**: `c5c478b` -- **Verification**: `deletable` macro, `window.confirmDelete`, `DELETE /api/trip/` route. - Trip 1967 deleted (18→17 trips), tracking data preserved. Server deploy via docker rebuild. +- **Fix**: `save_dtc_state()` persists MIL+codes each poll. `/api/dtc` falls back to + `get_persisted_dtc_state()` when OBD offline. Cleared codes tracked via IS_SET timestamps. +- **Verification**: Deployed to Pi, `/api/dtc` returns persisted snapshot, stats show `active_dtc: 0`. ### BUG-04 — Server merges two trips into one - **Status**: `CLOSED` 2026-08-11 - **Commit**: `96fb897` -- **Verification**: Server trips cleared and recomputed with Pi's motion-aware logic. - 17 trips on both Pi and server (matching). +- **Fix**: Replaced time-gap splitting with Pi's motion-aware logic: OBD > 1 km/h primary, + GPS >= 8m × 3 consecutive fallback, 5-min inactivity gap. +- **Verification**: Server trips cleared + recomputed. 17 trips on both Pi and server. -### BUG-09 — Server trip scrubber geocoding slow +### BUG-05 — Server route replay speed_source always "gps" - **Status**: `CLOSED` 2026-08-11 -- **Commit**: `7e91355` -- **Verification**: `history.js` pre-fetches geocodes for all route points on trip load. - Cache checked first during scrub. Server Docker rebuilt. +- **Commit**: `e2f2314` +- **Fix**: `"obd" if speed > 0 else "gps"` matching Pi logic. +- **Verification**: Docker rebuild, `/health` OK LAN + Cloudflare. -### BUG-10 — GPS-only false trips when car not connected +### BUG-08 — No trip deletion on local Pi - **Status**: `CLOSED` 2026-08-11 -- **Commit**: `7e91355` -- **Verification**: `was_car_ever_connected()` flag gates trip generation. - Pi bench-test produces 0 trips. +- **Commit**: `c5c478b` +- **Fix**: `deletable` macro, `window.confirmDelete`, `DELETE /api/trip/`, configurable + `data-delete-url` in shared `history.js`. Raw tracking preserved. +- **Verification**: Trip 1967 deleted (18→17 trips), 2028 tracking rows preserved. diff --git a/modules/app_obd.py b/modules/app_obd.py index c407917..bb4e3e2 100644 --- a/modules/app_obd.py +++ b/modules/app_obd.py @@ -24,6 +24,7 @@ dtc_codes = [] odometer = None mil_updated_at = None dtc_updated_at = None +_car_ever_connected = False class OBD: