Skip to content

Windowed live-data fetching + coordinated ECharts flush timers - #88

Merged
Bilchreis merged 1 commit into
mainfrom
only-fetch-neccesary-in-live-view
Jul 4, 2026
Merged

Windowed live-data fetching + coordinated ECharts flush timers#88
Bilchreis merged 1 commit into
mainfrom
only-fetch-neccesary-in-live-view

Conversation

@Bilchreis

Copy link
Copy Markdown
Owner

Summary

Live plots previously fetched a parameter's entire value history on every load, then relied on the client to trim it down to the visible time window wasteful for parameters with long histories. This PR makes live-mode fetches window-aware end to end, and fixes a related client-side performance issue in the chart flush loop that was scaling badly with plot count.

Backend: window-aware live data fetching

  • Builders.fetch_param_data/2 now takes a mode. For :live, it fetches only the configured default time window (fetch_param_data_in_range/3) instead of full history; :historical keeps the old unbounded fetch.
  • Added Builders.any_data?/1 to distinguish "no data in the live window" from "no data ever recorded" without re-fetching full history, and Builders.plot_available?/3 to use that distinction when deciding whether a plot can render (a parameter with only old data outside the live window is still "available", just showing an empty chart).
  • If the live window is empty and the parameter has no data at all, we fall back to reading from the device (read_from_device_if_empty/2), preserving existing bootstrap behavior.
  • Range buttons and the default active range index are now configurable via config :secant_service, :plot_db (falls back to the previous hardcoded defaults), and Builders.default_live_range_ms/0 centralizes resolving the default window against the current time so the fetch path and each dtype's initial x-axis window can never disagree.
  • All plot spec modules (Calibratable, Drivable, Readable, PlotDB) and dtype option builders (Scalar, Enum, Struct, ArrayHeatmap) updated to thread mode through and use the shared helpers instead of duplicating range-computation logic.

Frontend: coordinate ECharts flush timers

Each mounted chart ran its own setInterval doing a full series rebuild + setOption every tick, regardless of whether new data had arrived, so CPU cost scaled with timer frequency × plot count. This PR:

  • Replaces per-chart timers with a single shared ChartFlushDriver ticking all mounted charts once a second.
  • Adds a _dataDirty flag so the expensive part of a flush (trimming, series rebuild, heatmap visualMap recompute) only runs when data has actually changed since the last flush.
  • Adds a shared IntersectionObserver (ChartVisibilityObserver) so fully off-screen charts skip setOption entirely; buffered data isn't lost, it's just not rendered until the chart is visible again.
  • The x-axis window still slides every tick regardless of dirty/visibility state, so live charts keep visibly advancing with wall-clock time.
  • Generalized point-timestamp access (_pointTs) and range-expansion backfill (_earliestBufferedTs) to handle both plain [ts, ...] points and enum's {value: [ts, y]} point shape, and to correctly trigger a backfill fetch even when a series has zero buffered points.

Each mounted chart ran its own setInterval doing a full series
rebuild + setOption every tick regardless of whether new data had
arrived, so CPU cost scaled with timer frequency x plot count. Replace
the per-chart timers with one shared driver, add a dirty flag so the
expensive series rebuild only runs when data actually changed, and
skip off-screen charts via a shared IntersectionObserver. The
wall-clock xAxis slide still runs every tick so live charts keep
visibly advancing in time.
@Bilchreis
Bilchreis merged commit a71e806 into main Jul 4, 2026
1 check passed
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