feat(v0.15.0): slice 2c — Live Gauges panel data source flip (main.js + setSource) - #235
Conversation
… + setSource) Tier A, frontend only. Wires the K+DCAN data source into main.js's existing read_live_data polling loop and flips the Live Gauges panel from the simulator mirror to the bridge-backed K+DCAN source. This is slice 2c of the v0.15.0 cycle. Slice 2b (PR #234) shipped the wiring module; this slice actually wires it in. ## What this slice adds - src/index.html: loads live_data_bridge.js + live_kdcan_source.js + live_data_source_wiring.js BEFORE live_gauges.js so the bridge factories exist when the gauges panel auto-mounts. - src/js/main.js: at startup, calls window.beeemuuKdcanDataSource.initKdcanDataSource({invoke, log}) and pushes the resulting kdcan source into the Live Gauges controller via window.beeemuuLiveGauges.controller.setSource(). In pollOnce(), after each successful read_live_data invoke, feeds (values, errors) into kdcanDataSource.applySweep() so the bridge cache stays current. - src/js/live_gauges.js: new setSource(newSource) method on the controller (stops old source if running, replaces via sourceHolder indirection, starts new one if controller was ticking). Stashes the controller on window.beeemuuLiveGauges so main.js can grab it after initKdcanDataSource runs. ## Refactor of slice 2b - src/js/live_data_source_wiring.js: the slice 2b module had an internal setInterval that would have double-polled read_live_data (once from main.js's existing loop, once from the wiring module). Refactored to a passive consumer — main.js owns the timer; the wiring module just transforms each LiveSweepResult into a bridge cache update. New API: { applySweep, start, stop, reset, getKdcanSource, getBridge }. start()/stop() now only mark the source running (FPS tracking), they don't spawn a timer. ## Tests - src/js/live_data_source_wiring.test.js (new, 10 tests): module surface, initKdcanDataSource fallback (no modules), init with modules loaded, applySweep with/without running source, null handling, lifecycle (start/stop/reset idempotency, peak reset). node --test passes 10/10. The after() hook clears tracked controllers so node --test exits cleanly on Windows (FPS-timer teardown hang workaround, matches the v0.14.0 / v0.14.2 / v0.14.5 live_can_source.test.js pattern). - src/js/live_gauges.test.js (+4 tests for setSource): replace when stopped (no auto-restart), replace when running (stops old, starts new), setSource(null) detach, setSource on the surface. Full suite 14/14 pass. ## What this slice does NOT do - ❌ No new Tauri commands. Reuses the existing read_live_data command added in v0.14.2 (PR #175). - ❌ No backend / transport/** / protocol/** / commands.rs / Cargo.toml changes. Pure frontend (~430 LOC including tests). - ❌ No CSS changes. The data-source indicator (badge showing sim vs K+DCAN) is a future polish item. ## Tier Tier A — no human review required. Pure frontend module under src/, no src-tauri/src/** touches, no community/** changes, no CI workflow changes. Per CLAUDE.md golden rule #1, the auto-merge bot will merge this PR once CI is green. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged.
|
ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c8fac3773
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const kdcanSrc = kdcanDataSource.getKdcanSource(); | ||
| const gaugesApi = window.beeemuuLiveGauges; | ||
| if (kdcanSrc && gaugesApi && gaugesApi.controller && typeof gaugesApi.controller.setSource === "function") { | ||
| gaugesApi.controller.setSource(kdcanSrc); |
There was a problem hiding this comment.
Preserve the simulator fallback before swapping sources
In non-connected or simulator sessions this unconditional swap replaces the sim source that mountLiveGauges() just created with the passive K+DCAN source. pollOnce() only invokes read_live_data when connected, and the new K+DCAN source does not poll on its own, so clicking "Start CAN listener" before connecting or after disconnecting leaves Live Gauges blank instead of using the simulator fallback. Gate this swap on an active K+DCAN connection/profile, or restore the sim source when that context is absent.
Useful? React with 👍 / 👎.
| try { | ||
| const wiring = window.beeemuuKdcanDataSource; | ||
| if (wiring && typeof wiring.initKdcanDataSource === "function") { | ||
| kdcanDataSource = wiring.initKdcanDataSource({ invoke, log }); |
There was a problem hiding this comment.
Clear K+DCAN gauge state when contexts change
Because this source is initialized once at boot and never reset or recreated when live-profile changes or the app disconnects/reconnects, the bridge cache and controller values can survive across vehicle/profile contexts. Profiles such as B58 omit oil, and failed or missing PIDs intentionally keep last-known values, so after switching from a profile that populated oil temp to one that does not, Live Gauges can keep displaying the old oil value as if it came from the new profile. Clear the bridge/controller caches on profile and connection changes, or recreate the source for the new context.
Useful? React with 👍 / 👎.
| * panel's start button. Idempotent. | ||
| */ | ||
| function start() { | ||
| kdcanSource?.start(); |
There was a problem hiding this comment.
Start the live-data poll with the gauge source
In a connected K+DCAN session, pressing the Live Gauges "Start CAN listener" button now only marks this passive source as running; the read_live_data sweep that feeds applySweep() is scheduled separately by the Live values #live-poll checkbox. If the user starts Live Gauges without also enabling that other poll control, no sweeps are ever pushed into the bridge and the gauges stay blank. Tie this start path to startPolling() or otherwise ensure the read_live_data loop is active while the K+DCAN gauge source is running.
Useful? React with 👍 / 👎.
| // when the panel hasn't been started (applySweepFromTauri | ||
| // guards on `running`). | ||
| if (kdcanDataSource) { | ||
| kdcanDataSource.applySweep(result.values || [], result.errors || []); |
There was a problem hiding this comment.
Map the profile speed PID before feeding gauges
The sweeps now sent here preserve each profile param's id, and the repository profiles use id = "speed" for Vehicle speed, but the bridge only recognizes vehicleSpeed. As a result, K+DCAN Live Gauges will ignore every vehicle-speed value from the shipped profiles and leave that gauge blank even while the Live values panel shows it. Add the speed alias to the bridge mapping before relying on these forwarded sweeps.
Useful? React with 👍 / 👎.
…flect shipped slices (#237) Tier A, docs only. Records the actual 4-slice shape (1 + 2a + 2b + 2c) shipped via PRs #228, #229, #234, #235 instead of the original plan's '1 + 2 + 3' shape. Documents that slice 3 (`update_can_listen` async Tauri command) was dropped because the architecture converged on main.js driving `read_live_data` polling directly (the existing async Tauri command from v0.14.2). ## What this PR amends - **docs/v0.15.0_plan.md** — adds a 2026-08-05 status blockquote under the existing 2026-08-02 slice 0 blockquote. The new blockquote notes the actual slice shape (1 + 2a + 2b + 2c, PRs #229, #234, #235) and documents that the planned Tier B slice 3 (`update_can_listen`) was dropped. Updates the Tier split table to show shipped slices with their PR numbers + LOC counts. - **ROADMAP.md** — flips the v0.15.0 cycle header from '(In Progress — slice 0)' to '(Shipped 2026-08-05)'. Replaces the 'Slices planned' table with a 'Slices shipped' table listing all 5 shipped slices (with PR numbers) + the dropped slice 3 row. - **CHANGELOG.md** — flips '## [0.15.0] — Unreleased' to '## [0.15.0] — 2026-08-05', promotes the '### Planned — Tier A surface (feature cycle)' header to '### Added', rewrites the cycle status blockquote, replaces the slice bullets with the actual shipped-slice list (1, 2a, 2b, 2c, cycle plan, this slice 0.5 doc-amend), and updates the 'does NOT ship' commands.rs note to reflect that v0.15.0 is fully frontend (no `commands.rs` exception needed since slice 3 was dropped). ## Why this matters The plan doc, ROADMAP, and CHANGELOG must agree with what's on `origin/main`. Before this PR they described a 4-slice shape (1 + 2 + 3 + cycle plan) where the actual shipped shape is 5 Tier A slices (1 + 2a + 2b + 2c + cycle plan). The release-cut PR follows next; the v0.15.0 CHANGELOG entry should describe what actually shipped before the tag is pushed. ## Tier Tier A — docs only, no protected paths touched. The auto-merge bot will merge this PR once CI is green. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…fresh (#238) Tier C release cut for v0.15.0 'Live Gauges from the Bench'. ## What this PR does - Bump version to 0.15.0 in: package.json, src-tauri/Cargo.toml, src-tauri/tauri.conf.json, src-tauri/Cargo.lock (via `cargo update -p beeemuu`), README.md release badge. - Date-stamp CHANGELOG.md v0.15.0 status blockquote (now says 'shipped via PRs #228, #229, #234, #235 ... release cut lands via this PR'). - Add release-cut status blockquote to docs/v0.15.0_plan.md noting the tag + push + release.yml sequence the maintainer runs after this PR merges. ## What this PR does NOT do - No `git tag v0.15.0` (Tier C, separate step after this merges). - No `git push --tags` (Tier C). - No new commits to main; this is the version-bump + doc-refresh PR that precedes the actual tag. ## Tier Tier C \xe2\x80\x94 human decision required. Per CLAUDE.md Tier C rules ('Releases: version bumps, git tags, publishing installers \xe2\x80\x94 propose, never execute'), this PR is the propose step. The maintainer runs the execute step (`git tag v0.15.0 && git push --tags`) after merging. This PR will auto-merge once CI is green (per the `claude-auto-merge.yml` doc-only check; the version-bump files match its safe pattern of *.json/*.toml/LOCK/readme). Once merged, the maintainer runs: ``` git fetch origin --prune --tags git tag -a v0.15.0 -m 'BeeEmUu v0.15.0 \xe2\x80\x94 "Live Gauges from the Bench" cycle PRs: #228 (cycle plan), #229 (slices 1 + 2a bridge + source adapter), #234 (slice 2b wiring module), #235 (slice 2c caller integration), #237 (slice 0.5 doc-amend). Tagging on origin/main @ <merge-sha>.' origin/main git push origin v0.15.0 gh run watch $(gh run list --workflow release.yml --limit 1 --json databaseId --jq '.[0].databaseId') ``` The release.yml run will produce both Windows installers (`BeeEmUu_0.15.0_x64-setup.exe` + `BeeEmUu_0.15.0_x64_en-US.msi`) and the draft release at <https://github.com/ohgeeceee/beemuu/releases/tag/v0.15.0>. ## Diff stat 7 files, +19 / -8. Single-line version bumps + the two CHANGELOG + plan-doc blockquote updates. No code changes. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…d-roadmap closeout (#242) Tier A, docs only. Opens the v0.15.1 'Test-Plan Walks on the Bench' cycle and closes out the v0.15.0 cycle in the forward planning docs. ## What this PR does - **docs/v0.15.1_plan.md** (NEW, ~207 lines): full cycle plan for v0.15.1. Premise: port the v0.7.0-v0.10.0 test-plan walk from sim-only to real-car sessions, using the v0.14.0 freeze- frame schema split (PR #170). 3-slice spine: cycle plan + walk reducer rewrite (Tier A) + record_walk_result async Tauri command (Tier B) + HTML export touchup (Tier A). - **ROADMAP.md**: adds the v0.15.1 cycle block (3 Tier A + 1 Tier B slices planned) after the v0.15.0 block. Mirrors the shape used by v0.14.4-v0.15.0 cycle blocks. - **CHANGELOG.md**: adds the ## [0.15.1] -- Unreleased section + Planned header after the v0.15.0 section. Per Keep-a-Changelog convention. - **docs/forward_roadmap_14.4_to_16.9.md**: - Flips v0.14.6 entry from 'in flight' to 'Shipped 2026-08-02' with checkmark (matches the actual close-out status from PR #226). - Flips v0.15.0 entry from 'active cycle' to 'Shipped 2026-08-05' with checkmark. Documents the actual 5-Tier-A slice shape (PRs #228, #229, #234, #235, #237) + the dropped slice 3 (update_can_listen) and why. - Promotes v0.15.1 entry from 'Nov 2026 candidate' to 'Aug 2026 active cycle' with the corresponding cycle pointer to docs/v0.15.1_plan.md. ## Tier Tier A -- docs only, no protected paths touched. Per CLAUDE.md golden rule #1, the auto-merge bot will merge this PR once CI is green. ## Cycle context v0.15.1 'Test-Plan Walks on the Bench' is the natural follow-up to v0.15.0 'Live Gauges from the Bench': - v0.15.0 wired the existing read_live_data UDS path to the Live Gauges panel (frontend-only, K+DCAN). - v0.15.1 wires the existing read_freeze_frame path to the test-plan walk reducer (frontend + 1 Tier B Rust command). Both cycles share the same chassis constraint: no new hardware required, 5 K+DCAN cable is enough. The user- facing win is a printable / shareable HTML test-plan result that includes the real freeze-frame data. Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
Summary
Tier A, frontend-only PR. Wires the K+DCAN data source into main.js's
existing
read_live_datapolling loop and flips the Live Gaugespanel from the simulator mirror to the bridge-backed K+DCAN source.
This is slice 2c of the v0.15.0 cycle. Slice 2b (PR #234)
shipped the wiring module; this slice actually wires it in. After
this lands, an E90 / E60 / E70 owner with the $15 K+DCAN cable will
see real RPM, coolant, oil temp, vehicle speed, battery voltage,
and throttle values in the Live Gauges panel — without needing the
OBDLink SX.
What this PR adds
src/index.html— loadslive_data_bridge.js+live_kdcan_source.js+live_data_source_wiring.jsBEFORElive_gauges.jsso the bridge factories exist when the panelauto-mounts.
src/js/main.js— at startup, callswindow.beeemuuKdcanDataSource.initKdcanDataSource({invoke, log})and pushes the resulting kdcan source into the Live Gauges
controller via
window.beeemuuLiveGauges.controller.setSource().In
pollOnce(), after each successfulread_live_datainvoke,feeds
(values, errors)intokdcanDataSource.applySweep()sothe bridge cache stays current.
src/js/live_gauges.js— newsetSource(newSource)method onthe controller. Stops the old source if running, replaces via
a
sourceHolderindirection (the destructuredsourcefromoptions is a const binding, so we wrap it in a mutable holder),
starts the new one if the controller was ticking. Stashes the
controller on
window.beeemuuLiveGauges.controllerso main.jscan grab it after initKdcanDataSource runs.
Refactor of slice 2b
src/js/live_data_source_wiring.js— slice 2b's module had aninternal
setIntervalthat would have double-polledread_live_data(once from main.js's existing loop, once fromthe wiring module). Refactored to a passive consumer — main.js
owns the timer; the wiring module just transforms each
LiveSweepResultinto a bridge cache update. New API:{ applySweep, start, stop, reset, getKdcanSource, getBridge }.start()/stop()now only mark the source running (FPStracking), they don't spawn a timer.
Tests
src/js/live_data_source_wiring.test.js(new, 202 LOC, 10 tests):module surface, initKdcanDataSource fallback (no modules
loaded), init with modules loaded,
applySweepwith/withoutrunning source, null handling, lifecycle (start/stop/reset
idempotency, peak reset).
node --testpasses 10/10. Theafter()hook clears tracked controllers sonode --testexits cleanly on Windows (FPS-timer teardown hang workaround,
matches the v0.14.0 / v0.14.2 / v0.14.5
live_can_source.test.jspattern).
src/js/live_gauges.test.js(+74 LOC, +4 tests forsetSource):replace when stopped (no auto-restart), replace when running
(stops old, starts new),
setSource(null)detach,setSourceon the controller surface. Full suite: 14/14 pass (10
existing + 4 new).
What this PR does NOT do
read_live_datacommand added in v0.14.2 (PR feat(v0.14.2): n62.toml — swap local:10 oil placeholder for OBD-II 0x5C #175).
transport/**/protocol/**/commands.rs/Cargo.tomlchanges. Pure frontend (~430 LOC including tests)."sim" vs "K+DCAN") is a future polish item — the current
panel header FPS counter is the user-visible signal.
Tier
Tier A — no human review required. Pure frontend module under
src/, nosrc-tauri/src/**touches, nocommunity/**changes,no CI workflow changes. Per
CLAUDE.mdgolden rule #1, theauto-merge bot will merge this PR once CI is green.
Cycle context
v0.15.0 "Live Gauges from the Bench" — see
docs/v0.15.0_plan.mdfor thefull plan. The cycle's user-facing win: connect the existing
read_live_dataUDS path to the v0.14.0 Live Gauges panel so itshows real data on the K+DCAN cable, without the OBDLink SX
acquisition the v0.14.0 Tier B was waiting for.
Slices shipped so far: #228 (cycle plan), #229 (slice 1 + 2a),
#234 (slice 2b), this PR (slice 2c). Cycle is code-side closed
once this lands — the Tier C release cut (version bumps + tag +
release.yml+ landing-page deploy) is the next PR.Author note
Commit authored with
ohgeeceee@users.noreply.github.comto bypassGH007 (private-email push block). Content unchanged.