feat(v0.15.0): slice 2b — K+DCAN data source wiring module (src/js/live_data_source_wiring.js) - #234
Conversation
…ve_data_source_wiring.js) Tier A, frontend only. The wiring module initializes the DID- projection bridge + K+DCAN source adapter and exposes startPolling(), stopPolling(), reset() for main.js to call. When the caller invokes startPolling(), the module polls read_live_data at 250ms intervals and feeds results into the bridge cache. ## What this slice adds - src/js/live_data_source_wiring.js (new, ~95 LOC): initKdcanDataSource() factory that creates the bridge + source, starts/stops polling, and exposes getKdcanSource() for live_gauges.js to wire as its source. ## What this slice does NOT do - ❌ No main.js caller integration (the actual initKdcanDataSource() call and source wiring into live_gauges is the next commit). - ❌ No CSS changes (data-source indicator is still pending). - ❌ No new Tauri commands. ## Verification - Module loads without error: `node -e "require(...)"` passes - git diff --cached --stat: 1 file, +94 insertions - No new test file (the module is thin glue; integration tests in main.js caller will verify end-to-end behavior) Author note: commit authored with ohgeeceee@users.noreply.github.com to bypass GH007 (private-email push block). Content unchanged.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b7c9a0d8e
ℹ️ 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".
| * @returns {Object} — `{ startPolling, stopPolling, reset }` | ||
| */ | ||
| function initKdcanDataSource({ invoke, log }) { | ||
| const { createDIDBridge } = window.beeemuuDIDBridge || {}; |
There was a problem hiding this comment.
Guard Node callers from the window lookup
When this module is imported via its new module.exports path and a Node test or other non-webview caller exercises initKdcanDataSource, this unguarded window reference throws ReferenceError: window is not defined before the sim-only fallback can run. The neighboring frontend modules guard their browser globals, so this glue cannot currently be instantiated in the repository's node --test style coverage unless callers fabricate a global window; guard the lookup with typeof window !== "undefined" or accept the bridge/source dependencies as options.
Useful? React with 👍 / 👎.
| const profile = document.getElementById("live-profile")?.value; | ||
| if (!profile) return; | ||
|
|
||
| const result = await invoke("read_live_data", { profile }); | ||
| kdcanSource.applySweepFromTauri(result.values || [], result.errors || []); |
There was a problem hiding this comment.
Reset the bridge when the live profile changes
When the user switches #live-profile while this polling loop is running, pollOnce starts invoking read_live_data for the new profile but keeps reusing the same DID bridge cache; any gauge param that is absent or unsupported in the new profile keeps its previous last-known value, so Live Gauges can continue displaying stale data from the prior profile indefinitely. lastProfile is never compared before applying the sweep, so reset or recreate the bridge/source on profile changes before feeding the next result.
Useful? React with 👍 / 👎.
| } catch (e) { | ||
| log("K+DCAN poll failed: " + e); |
There was a problem hiding this comment.
Stop polling after systemic read failures
If read_live_data rejects here for a systemic condition such as disconnecting the transport or selecting an invalid profile, the catch block only logs the error while the setInterval created by startPolling keeps firing. That leaves the app repeatedly invoking a command that is guaranteed to fail every interval until something else explicitly stops this source, which can spam the log and keep contending for the shared transport path; mirror the existing live-data poller by stopping on these command-level failures.
Useful? React with 👍 / 👎.
… + setSource) (#235) 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. Co-authored-by: ohgeeceee <ohgeeceee@users.noreply.github.com>
…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. Lands the K+DCAN data source wiring module
(
src/js/live_data_source_wiring.js) that the Live Gauges panel willuse to read real DID data from the K+DCAN cable via
read_live_data.This is slice 2b of the v0.15.0 cycle. Slices 1 (
live_data_bridge.js)live_kdcan_source.js) merged in PR feat(v0.15.0): slices 1 + 2a — DID-projection bridge + K+DCAN source adapter #229. Slice 2b is thewiring layer that ties the bridge + K+DCAN source adapter into a shape
main.jscan call. Slice 2c (the caller integration inmain.js+source swap in
live_gauges.js) is the next PR.What this PR adds
src/js/live_data_source_wiring.js(new, 94 LOC): theinitKdcanDataSource({ invoke, log })factory. Creates theDID-projection bridge + K+DCAN source adapter, exposes
startPolling(intervalMs)/stopPolling()/reset(), andgetter helpers
getKdcanSource()/getBridge()forlive_gauges.jsto wire the source into the gauges controller.
module.exports+window.beeemuuKdcanDataSourcedual export,matching the project's existing pattern (
live_data_bridge.js,live_kdcan_source.js).What this PR does NOT do
main.jscaller integration.initKdcanDataSource()isexported but not called yet. That wiring lands in slice 2c.
live_gauges.jssource swap (sim → kdcan). That lands inslice 2c.
"sim" vs "K+DCAN") lands in slice 2c.
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.rschanges. Pure frontend, ~95 LOC.
Verification
node --check src/js/live_data_source_wiring.js— passes (no syntaxerrors).
git diff --cached --stat— 1 file, +94 insertions.the bridge (
live_data_bridge.js, 20 unit tests in PR feat(v0.15.0): slices 1 + 2a — DID-projection bridge + K+DCAN source adapter #229) andthe source adapter (
live_kdcan_source.js, 6 unit tests + 1 skipin PR feat(v0.15.0): slices 1 + 2a — DID-projection bridge + K+DCAN source adapter #229). End-to-end behavior is verified in slice 2c's tests.
transport/**, noprotocol/**,no
commands.rs, noCargo.tomlchanges). Puresrc/js/.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 spine: route the existing
read_live_dataUDS path through to the v0.14.0 Live Gauges panel, so the panel
shows real data on the K+DCAN cable without needing an OBDLink SX.
Author note
Commit authored with
ohgeeceee@users.noreply.github.comto bypassGH007 (private-email push block). Content unchanged.