Three display defects surfaced when rendering the live PS/pump/MGC dashboards:
1. Doubled values everywhere. EVOLV telemetry historically carried stray tags
(tagcode="undefined"/uuid="null") that newer writes dropped, so InfluxDB holds
two series per field and last() returned two of everything (e.g. Time Left,
Runtime, Heights). Add |> group(columns:["_field"]) before last()/aggregateWindow
in every template query so each field collapses to one value/line regardless of
tag-set history.
2. fields:"/.*/" also rendered the _time column as a stat value. For single-field
string panels (Direction, Flow Source, Mode, State, Prediction Quality) append
|> keep(columns:["_value"]); for mixed string+numeric panels (valve/vgc/monster)
drop _time/_start/_stop instead.
3. Level/Heights showed "0.12 min" — Grafana unit id "m" means minutes, not meters.
Change to lengthm; normalize m³->m3, m³/h->m3/h on pumpingStation.
Verified live via headless screenshots: PS, pump, and measurement dashboards now
show single clean values with correct units.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Grafana Stat/Gauge panels default the Fields option to "Numeric Fields"
(reduceOptions.fields == ""), so string-valued fields (mode, state,
movementState, direction, flowSource, predictionQuality, running) are excluded
and the panel renders "No data" even though the Flux query (last()) returns the
string correctly.
Set reduceOptions.fields = "/.*/" ("All fields") on every stat panel bound to a
string field across machine, machineGroup, pumpingStation, valve,
valveGroupControl, and monster templates. lastNotNull calc was already correct.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- config/machineGroup.json: every non-row panel now annotated with
meta.emittedFields (mode, scaling, abs/relDistFromPeak, flow.total/group,
power.total/group). Per-pump fields (ctrl, state, runtime, pressure,
temperature) deliberately absent — those live on rotatingMachine children
per #39's no-data-duplication contract.
- Timeseries panels gain byRegexp dashed-bounds overrides for .min$/.max$
(same pattern as #38).
- test/basic/slice40-mgc-template.basic.test.js: 4 cases — no per-pump
fields leak in, every non-row annotated, dashed overrides present on TS,
composer dedup applies when a child claims an MGC-level field.
Closes#40
Adds per-panel `meta.emittedFields` to machine.json (rotatingMachine) and
machineGroup.json (MGC) templates. Each non-row panel declares the Influx
field paths it visualizes, so a parent template's composer can filter out
panels already covered by its children (#39 no-data-duplication rule).
- config/machine.json: 13 non-row panels annotated.
- config/machineGroup.json: panels annotated.
- src/specificClass.js: collectEmittedFields(dashboard) helper.
- test/basic/slice37-emitted-fields.basic.test.js: 4 cases (template loads
with annotations, aggregation, missing-meta graceful, null input).
PRD F-6 panel set audit: machine.json already covers all the PRD-required
panels (State/Mode/Ctrl%/Runtime/NCog%/Flow/Efficiency/Pressure/Temperature/
Diagnostics) — substantially more than asked. No new panels added.
PRD F-7 predicted-vs-measured side-by-side: deferred. Current architecture
is "1 dashboard per node" (each child gets its own dashboard, cross-linked
from the parent), not "1 dashboard with N composed panels." Side-by-side
rendering of predicted (rotatingMachine dashboard) + measured (measurement
child dashboard) lives naturally as drill-down navigation today. Refactor
to a single-dashboard composition model would be substantial — flagged in
the issue comment for v2 if the drill-down UX proves insufficient.
Closes#37