Skip to content

feat(v0.15.0): slice 2c — Live Gauges panel data source flip (main.js + setSource) - #235

Merged
ohgeeceee merged 1 commit into
mainfrom
feat/v0.15.0-slice-2c-caller-integration
Aug 5, 2026
Merged

feat(v0.15.0): slice 2c — Live Gauges panel data source flip (main.js + setSource)#235
ohgeeceee merged 1 commit into
mainfrom
feat/v0.15.0-slice-2c-caller-integration

Conversation

@ohgeeceee

Copy link
Copy Markdown
Owner

Summary

Tier A, frontend-only PR. 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. 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 — 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 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 the old source if running, replaces via
    a sourceHolder indirection (the destructured source from
    options 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.controller so main.js
    can grab it after initKdcanDataSource runs.

Refactor of slice 2b

  • src/js/live_data_source_wiring.js — slice 2b's 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, 202 LOC, 10 tests):
    module surface, initKdcanDataSource fallback (no modules
    loaded), 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 (+74 LOC, +4 tests for setSource):
    replace when stopped (no auto-restart), replace when running
    (stops old, starts new), setSource(null) detach, setSource
    on the controller surface. Full suite: 14/14 pass (10
    existing + 4 new).

What this PR does NOT do

  • ❌ No new Tauri commands. Reuses the existing read_live_data
    command added in v0.14.2 (PR feat(v0.14.2): n62.toml — swap local:10 oil placeholder for OBD-II 0x5C #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 — the current
    panel header FPS counter is the user-visible signal.

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.

Cycle context

v0.15.0 "Live Gauges from the Bench" — see
docs/v0.15.0_plan.md for the
full plan. The cycle's user-facing win: connect the existing
read_live_data UDS path to the v0.14.0 Live Gauges panel so it
shows 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.com to bypass
GH007 (private-email push block). Content unchanged.

… + 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-tools

ecc-tools Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/js/main.js
const kdcanSrc = kdcanDataSource.getKdcanSource();
const gaugesApi = window.beeemuuLiveGauges;
if (kdcanSrc && gaugesApi && gaugesApi.controller && typeof gaugesApi.controller.setSource === "function") {
gaugesApi.controller.setSource(kdcanSrc);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/js/main.js
try {
const wiring = window.beeemuuKdcanDataSource;
if (wiring && typeof wiring.initKdcanDataSource === "function") {
kdcanDataSource = wiring.initKdcanDataSource({ invoke, log });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/js/main.js
// when the panel hasn't been started (applySweepFromTauri
// guards on `running`).
if (kdcanDataSource) {
kdcanDataSource.applySweep(result.values || [], result.errors || []);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@ohgeeceee
ohgeeceee merged commit 3739f7b into main Aug 5, 2026
16 of 20 checks passed
@ohgeeceee
ohgeeceee deleted the feat/v0.15.0-slice-2c-caller-integration branch August 5, 2026 07:55
github-actions Bot pushed a commit that referenced this pull request Aug 5, 2026
…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>
ohgeeceee added a commit that referenced this pull request Aug 5, 2026
…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>
github-actions Bot pushed a commit that referenced this pull request Aug 5, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant