2026-01-13 14:29:43 +01:00
{
"annotations" : {
"list" : [
{
"builtIn" : 1 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"datasource" : {
"type" : "grafana" ,
"uid" : "-- Grafana --"
} ,
2026-01-13 14:29:43 +01:00
"enable" : true ,
"hide" : true ,
"iconColor" : "rgba(0, 211, 255, 1)" ,
"name" : "Annotations & Alerts" ,
"type" : "dashboard"
}
]
} ,
"editable" : true ,
"graphTooltip" : 0 ,
"id" : null ,
"links" : [ ] ,
"panels" : [
{
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 1 , "w" : 24 , "x" : 0 , "y" : 0 } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"id" : 1 ,
"title" : "Status" ,
"type" : "row"
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
"thresholds" : {
"mode" : "absolute" ,
2026-05-28 10:32:52 +02:00
"steps" : [ { "color" : "blue" , "value" : null } ]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
}
} ,
"overrides" : [ ]
} ,
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 4 , "w" : 6 , "x" : 0 , "y" : 1 } ,
2026-01-13 14:29:43 +01:00
"id" : 2 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"reduceOptions" : { "calcs" : [ "lastNotNull" ] , "fields" : "/.*/" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"colorMode" : "value" ,
"graphMode" : "none"
} ,
2026-01-13 14:29:43 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"direction\")\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-01-13 14:29:43 +01:00
] ,
2026-03-11 11:13:44 +01:00
"title" : "Direction" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "stat" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "direction" ] }
2026-01-13 14:29:43 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
"unit" : "s" ,
"thresholds" : {
"mode" : "absolute" ,
"steps" : [
2026-05-28 10:32:52 +02:00
{ "color" : "green" , "value" : null } ,
{ "color" : "orange" , "value" : 300 } ,
{ "color" : "red" , "value" : 600 }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
]
}
} ,
"overrides" : [ ]
} ,
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 4 , "w" : 6 , "x" : 6 , "y" : 1 } ,
2026-01-13 14:29:43 +01:00
"id" : 3 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"reduceOptions" : { "calcs" : [ "lastNotNull" ] } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"colorMode" : "value" ,
"graphMode" : "area"
} ,
2026-01-13 14:29:43 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"timeleft\")\n |> group(columns:[\"_field\"])\n |> last()" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-01-13 14:29:43 +01:00
] ,
2026-03-11 11:13:44 +01:00
"title" : "Time Left" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "stat" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "timeLeft" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
"thresholds" : {
"mode" : "absolute" ,
2026-05-28 10:32:52 +02:00
"steps" : [ { "color" : "purple" , "value" : null } ]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
}
} ,
"overrides" : [ ]
} ,
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 4 , "w" : 6 , "x" : 12 , "y" : 1 } ,
2026-03-11 11:13:44 +01:00
"id" : 4 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"reduceOptions" : { "calcs" : [ "lastNotNull" ] , "fields" : "/.*/" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"colorMode" : "value" ,
"graphMode" : "none"
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field==\"flowSource\")\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
"title" : "Flow Source" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "stat" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "flowSource" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
2026-05-28 10:32:52 +02:00
"unit" : "lengthm" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"thresholds" : {
"mode" : "absolute" ,
2026-05-28 10:32:52 +02:00
"steps" : [ { "color" : "green" , "value" : null } ]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
}
} ,
"overrides" : [ ]
} ,
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 4 , "w" : 6 , "x" : 18 , "y" : 1 } ,
"id" : 6 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"reduceOptions" : { "calcs" : [ "lastNotNull" ] } ,
"colorMode" : "value" ,
"graphMode" : "area"
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
2026-05-28 10:32:52 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
2026-05-28 10:32:52 +02:00
"title" : "Level" ,
"type" : "stat" ,
"meta" : { "emittedFields" : [ "level" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"gridPos" : { "h" : 1 , "w" : 24 , "x" : 0 , "y" : 5 } ,
"id" : 13 ,
"title" : "Basin" ,
"type" : "row"
} ,
{
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"unit" : "lengthm" ,
2026-05-28 10:32:52 +02:00
"min" : 0 ,
"max" : { { h e i g h t B a s i n } } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"thresholds" : {
"mode" : "absolute" ,
"steps" : [
2026-05-28 10:32:52 +02:00
{ "color" : "#3a3a3a" , "value" : null } ,
{ "color" : "semi-dark-grey" , "value" : { { o u t f l o w L e v e l } } } ,
{ "color" : "blue" , "value" : { { d r y R u n L e v e l } } } ,
{ "color" : "green" , "value" : { { i n f l o w L e v e l } } } ,
{ "color" : "orange" , "value" : { { h i g h S a f e t y L e v e l } } } ,
{ "color" : "red" , "value" : { { o v e r f l o w L e v e l } } }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
]
}
} ,
"overrides" : [ ]
} ,
2026-05-28 10:45:51 +02:00
"gridPos" : { "h" : 20 , "w" : 4 , "x" : 0 , "y" : 6 } ,
2026-05-28 10:32:52 +02:00
"id" : 16 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"displayMode" : "basic" ,
"orientation" : "vertical" ,
"reduceOptions" : { "calcs" : [ "lastNotNull" ] , "fields" : "" } ,
"showThresholdLabels" : true ,
"showThresholdMarkers" : true ,
"showUnfilled" : true ,
"minVizWidth" : 8 ,
"minVizHeight" : 16 ,
"valueMode" : "color" ,
"namePlacement" : "auto"
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
2026-05-28 10:32:52 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> last()\n |> keep(columns:[\"_value\"])" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
2026-05-28 10:32:52 +02:00
"title" : "Water Level" ,
"type" : "bargauge" ,
"meta" : { "emittedFields" : [ "basinLevel" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
"fieldConfig" : {
"defaults" : {
"unit" : "none" ,
"decimals" : 2
} ,
"overrides" : [
{
"matcher" : { "id" : "byRegexp" , "options" : "^(outflowLevel|inflowLevel|overflowLevel|heightBasin|dryRunLevel|highVolumeSafetyLevel|level)$" } ,
2026-05-28 10:53:08 +02:00
"properties" : [ { "id" : "unit" , "value" : "lengthm" } , { "id" : "decimals" , "value" : 2 } ]
2026-05-28 10:32:52 +02:00
} ,
{
"matcher" : { "id" : "byRegexp" , "options" : "^(volume|maxVol|minVol|maxVolAtOverflow|minVolAtOutflow|minVolAtInflow)$" } ,
2026-05-28 10:53:08 +02:00
"properties" : [ { "id" : "unit" , "value" : "m3" } , { "id" : "decimals" , "value" : 2 } ]
2026-05-28 10:32:52 +02:00
} ,
{
2026-05-28 10:53:08 +02:00
"matcher" : { "id" : "byRegexp" , "options" : "^volumePercent$" } ,
"properties" : [ { "id" : "unit" , "value" : "percent" } , { "id" : "decimals" , "value" : 1 } ]
2026-05-28 10:32:52 +02:00
}
]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-05-28 10:53:08 +02:00
"gridPos" : { "h" : 20 , "w" : 6 , "x" : 4 , "y" : 6 } ,
2026-05-28 10:32:52 +02:00
"id" : 17 ,
"options" : {
"inlineEditing" : false ,
"showAdvancedTypes" : true ,
"panZoom" : false ,
"infinitePan" : false ,
"root" : {
"name" : "Basin" ,
"type" : "frame" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"placement" : { "left" : 0 , "top" : 0 , "right" : 0 , "bottom" : 0 } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "dark-green" } } ,
"elements" : [
{
"name" : "Zone Spill" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : 5.26 , "left" : 2.5 , "right" : 2.5 , "bottom" : { { z b _ s p i l l } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "rgba(229, 67, 67, 0.18)" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "" } }
} ,
{
"name" : "Zone HighSafety" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ o v e r f l o w } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { z b _ h i g h S a f e t y } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "rgba(242, 165, 67, 0.16)" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "" } }
} ,
{
"name" : "Zone Operating" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ h i g h S a f e t y } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { z b _ o p e r a t i n g } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "rgba(95, 179, 122, 0.14)" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "" } }
} ,
{
"name" : "Zone Dead" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ o u t f l o w } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { z b _ d e a d } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "rgba(128, 128, 128, 0.20)" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "" } }
} ,
{
"name" : "Tank Outline" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : 5.26 , "left" : 2.5 , "right" : 2.5 , "bottom" : 5.27 } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "#8a8a8a" } , "width" : 2 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "" } }
} ,
{
"name" : "Line Overflow" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ o v e r f l o w } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { y b _ o v e r f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "#e54343" } } ,
"border" : { "color" : { "fixed" : "#e54343" } , "width" : 0 }
} ,
{
"name" : "Line HighSafety" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ h i g h S a f e t y } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { y b _ h i g h S a f e t y } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "#f2a543" } } ,
"border" : { "color" : { "fixed" : "#f2a543" } , "width" : 0 }
} ,
{
"name" : "Line Inflow" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ i n f l o w } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { y b _ i n f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "#5fb37a" } } ,
"border" : { "color" : { "fixed" : "#5fb37a" } , "width" : 0 }
} ,
{
"name" : "Line DryRun" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ d r y R u n } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { y b _ d r y R u n } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "#5b9bd5" } } ,
"border" : { "color" : { "fixed" : "#5b9bd5" } , "width" : 0 }
} ,
{
"name" : "Line Outflow" ,
"type" : "rectangle" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { y _ o u t f l o w } } , "left" : 2.5 , "right" : 2.5 , "bottom" : { { y b _ o u t f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "#bfbfbf" } } ,
"border" : { "color" : { "fixed" : "#bfbfbf" } , "width" : 0 }
} ,
{
"name" : "Label Overflow Name" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ o v e r f l o w } } , "left" : 15 , "right" : 53 , "bottom" : { { t y b _ o v e r f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "fixed" , "fixed" : "overflowLevel" } , "color" : { "fixed" : "#c92020" } , "size" : 14 , "align" : "right" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Label HighSafety Name" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ h i g h S a f e t y } } , "left" : 15 , "right" : 53 , "bottom" : { { t y b _ h i g h S a f e t y } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "fixed" , "fixed" : "highSafety" } , "color" : { "fixed" : "#cf7e20" } , "size" : 14 , "align" : "right" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Label Inflow Name" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ i n f l o w } } , "left" : 15 , "right" : 53 , "bottom" : { { t y b _ i n f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "fixed" , "fixed" : "inflowLevel" } , "color" : { "fixed" : "#3d8a5a" } , "size" : 14 , "align" : "right" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Label DryRun Name" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ d r y R u n } } , "left" : 15 , "right" : 53 , "bottom" : { { t y b _ d r y R u n } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "fixed" , "fixed" : "dryRunLevel" } , "color" : { "fixed" : "#3a76a8" } , "size" : 14 , "align" : "right" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Label Outflow Name" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ o u t f l o w } } , "left" : 15 , "right" : 53 , "bottom" : { { t y b _ o u t f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "fixed" , "fixed" : "outflowLevel" } , "color" : { "fixed" : "#6a6a6a" } , "size" : 14 , "align" : "right" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Value Overflow" ,
"type" : "metric-value" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ o v e r f l o w } } , "left" : 53 , "right" : 12 , "bottom" : { { t y b _ o v e r f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "field" , "fixed" : "" , "field" : "overflowLevel" } , "color" : { "fixed" : "#c92020" } , "size" : 14 , "align" : "left" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Value HighSafety" ,
"type" : "metric-value" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ h i g h S a f e t y } } , "left" : 53 , "right" : 12 , "bottom" : { { t y b _ h i g h S a f e t y } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "field" , "fixed" : "" , "field" : "highVolumeSafetyLevel" } , "color" : { "fixed" : "#cf7e20" } , "size" : 14 , "align" : "left" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Value Inflow" ,
"type" : "metric-value" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ i n f l o w } } , "left" : 53 , "right" : 12 , "bottom" : { { t y b _ i n f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "field" , "fixed" : "" , "field" : "inflowLevel" } , "color" : { "fixed" : "#3d8a5a" } , "size" : 14 , "align" : "left" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Value DryRun" ,
"type" : "metric-value" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ d r y R u n } } , "left" : 53 , "right" : 12 , "bottom" : { { t y b _ d r y R u n } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "field" , "fixed" : "" , "field" : "dryRunLevel" } , "color" : { "fixed" : "#3a76a8" } , "size" : 14 , "align" : "left" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Value Outflow" ,
"type" : "metric-value" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : { { t y _ o u t f l o w } } , "left" : 53 , "right" : 12 , "bottom" : { { t y b _ o u t f l o w } } } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"config" : { "text" : { "mode" : "field" , "fixed" : "" , "field" : "outflowLevel" } , "color" : { "fixed" : "#6a6a6a" } , "size" : 14 , "align" : "left" , "valign" : "middle" }
2026-05-28 10:32:52 +02:00
} ,
{
"name" : "Header Rim" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : 2.63 , "left" : 2.5 , "right" : 2.5 , "bottom" : 95.26 } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "rim ({{heightBasin}} m)" } , "color" : { "fixed" : "#8a8a8a" } , "size" : 10 , "align" : "center" , "valign" : "middle" }
} ,
{
"name" : "Footer Floor" ,
"type" : "text" ,
chore(dashboardAPI): center tank via canvas scale constraint + bigger labels
Per Grafana Canvas docs, the correct way to make elements stay centered
and stretch with panel size is to set 'constraint: { horizontal: scale,
vertical: scale }' on every element AND use margin-style placement
(top + bottom + left + right, all as percentages of the panel) instead
of pixel-based 'top + left + width + height'.
This commit:
- Adds 'constraint: scale/scale' to every canvas element.
- Converts all placements to percentage margins. Hardcoded canvas
geometry (tank, zones, threshold lines, header, footer) uses literal
percentages; per-basin geometry (yp_*, ty_*, etc.) is precomputed in
_templateVarsForNode and emitted as percent values from the substitution.
- Adds derived 'zb_*', 'yb_*', 'tyb_*' substitution vars for bottom
margins of zones, lines, and labels respectively.
- Splits name/value labels left/right of tank centre with a visible gap
between them (was touching) and bumps font size 11 -> 14 for readability.
Result: at any panel/viewport size the tank fills the card with equal
left/right margins (~2.5%) and equal top/bottom margins (~5.26%) for
rim/floor captions, no letterboxing or right-side padding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 12:22:11 +02:00
"constraint" : { "horizontal" : "scale" , "vertical" : "scale" } ,
"placement" : { "top" : 95.26 , "left" : 2.5 , "right" : 2.5 , "bottom" : 2.63 } ,
2026-05-28 10:32:52 +02:00
"background" : { "color" : { "fixed" : "transparent" } } ,
"border" : { "color" : { "fixed" : "transparent" } , "width" : 0 } ,
"config" : { "text" : { "mode" : "fixed" , "fixed" : "floor (0.00 m)" } , "color" : { "fixed" : "#8a8a8a" } , "size" : 10 , "align" : "center" , "valign" : "middle" }
}
]
}
} ,
"targets" : [
{
"query" : "from(bucket: \"${bucket}\")\n |> range(start: -7d)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and (r._field==\"outflowLevel\" or r._field==\"inflowLevel\" or r._field==\"overflowLevel\" or r._field==\"heightBasin\" or r._field==\"dryRunLevel\" or r._field==\"highVolumeSafetyLevel\" or r._field =~ /^level\\.predicted\\.atequipment/ or r._field =~ /^volume\\.predicted\\.atequipment/ or r._field =~ /^volumePercent\\.predicted\\.atequipment/))\n |> last()\n |> map(fn: (r) => ({ r with _field: if r._field =~ /^volumePercent\\.predicted/ then \"volumePercent\" else if r._field =~ /^volume\\.predicted/ then \"volume\" else if r._field =~ /^level\\.predicted/ then \"level\" else r._field, _time: 2020-01-01T00:00:00Z }))\n |> group()\n |> keep(columns:[\"_field\",\"_value\",\"_time\"])\n |> pivot(rowKey:[\"_time\"], columnKey:[\"_field\"], valueColumn:\"_value\")" ,
"refId" : "A"
}
] ,
"title" : "Tank Layout" ,
"type" : "canvas" ,
"meta" : { "emittedFields" : [ "basinLayout" ] }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"unit" : "lengthm" ,
2026-05-28 10:32:52 +02:00
"custom" : { "drawStyle" : "line" , "lineWidth" : 2 , "fillOpacity" : 10 }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
"overrides" : [ ]
} ,
2026-05-28 10:53:08 +02:00
"gridPos" : { "h" : 10 , "w" : 14 , "x" : 10 , "y" : 6 } ,
2026-03-11 11:13:44 +01:00
"id" : 8 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"legend" : { "displayMode" : "list" , "placement" : "bottom" } ,
"tooltip" : { "mode" : "multi" }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^level\\.(predicted|measured)\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
2026-05-28 10:32:52 +02:00
"title" : "Level (over time)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "timeseries" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "level" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"unit" : "m3" ,
2026-05-28 10:32:52 +02:00
"custom" : { "drawStyle" : "line" , "lineWidth" : 2 , "fillOpacity" : 10 }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
"overrides" : [ ]
} ,
2026-05-28 10:53:08 +02:00
"gridPos" : { "h" : 10 , "w" : 14 , "x" : 10 , "y" : 16 } ,
2026-03-11 11:13:44 +01:00
"id" : 9 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"legend" : { "displayMode" : "list" , "placement" : "bottom" } ,
"tooltip" : { "mode" : "multi" }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^volume\\.predicted\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
2026-05-28 10:32:52 +02:00
"title" : "Volume (over time)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "timeseries" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "volume" ] }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
{
2026-05-28 10:45:51 +02:00
"gridPos" : { "h" : 1 , "w" : 24 , "x" : 0 , "y" : 26 } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"id" : 10 ,
"title" : "Flow" ,
"type" : "row"
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"unit" : "m3/h" ,
2026-05-28 10:32:52 +02:00
"custom" : { "drawStyle" : "line" , "lineWidth" : 2 , "fillOpacity" : 10 }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
"overrides" : [ ]
} ,
2026-05-28 10:45:51 +02:00
"gridPos" : { "h" : 8 , "w" : 12 , "x" : 0 , "y" : 27 } ,
2026-03-11 11:13:44 +01:00
"id" : 11 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"legend" : { "displayMode" : "list" , "placement" : "bottom" } ,
"tooltip" : { "mode" : "multi" }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^netFlowRate\\.(predicted|measured)\\.atequipment/)\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
"title" : "Net Flow Rate" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "timeseries" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "flow.net" , "flow" ] }
2026-03-11 11:13:44 +01:00
} ,
{
2026-05-28 10:32:52 +02:00
"datasource" : { "type" : "influxdb" , "uid" : "cdzg44tv250jkd" } ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"fieldConfig" : {
"defaults" : {
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"unit" : "m3/h" ,
2026-05-28 10:32:52 +02:00
"custom" : { "drawStyle" : "line" , "lineWidth" : 2 , "fillOpacity" : 10 }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
"overrides" : [ ]
} ,
2026-05-28 10:45:51 +02:00
"gridPos" : { "h" : 8 , "w" : 12 , "x" : 12 , "y" : 27 } ,
2026-03-11 11:13:44 +01:00
"id" : 12 ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"options" : {
2026-05-28 10:32:52 +02:00
"legend" : { "displayMode" : "list" , "placement" : "bottom" } ,
"tooltip" : { "mode" : "multi" }
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
2026-03-11 11:13:44 +01:00
"targets" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
fix(dashboardAPI): clean stat panels — dedup stray-tag series, value-only text, meter units
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>
2026-05-27 21:42:47 +02:00
"query" : "from(bucket: \"${bucket}\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn:(r) => r._measurement==\"${measurement}\" and r._field =~ /^flow\\.(predicted|measured)\\.(upstream|in|out|overflow)/)\n |> group(columns:[\"_field\"])\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"refId" : "A"
}
2026-03-11 11:13:44 +01:00
] ,
"title" : "Inflow + Outflow" ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"type" : "timeseries" ,
2026-05-28 10:32:52 +02:00
"meta" : { "emittedFields" : [ "flow.in" , "flow.out" ] }
2026-01-13 14:29:43 +01:00
}
] ,
"schemaVersion" : 39 ,
2026-05-28 10:32:52 +02:00
"tags" : [ "EVOLV" , "pumpingStation" , "template" ] ,
2026-01-13 14:29:43 +01:00
"templating" : {
"list" : [
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
{
"name" : "dbase" ,
"type" : "custom" ,
"label" : "dbase" ,
"query" : "cdzg44tv250jkd" ,
2026-05-28 10:32:52 +02:00
"current" : { "text" : "cdzg44tv250jkd" , "value" : "cdzg44tv250jkd" , "selected" : false } ,
"options" : [ { "text" : "cdzg44tv250jkd" , "value" : "cdzg44tv250jkd" , "selected" : true } ] ,
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
"hide" : 2
} ,
{
"name" : "measurement" ,
"type" : "custom" ,
"query" : "template" ,
2026-05-28 10:32:52 +02:00
"current" : { "text" : "template" , "value" : "template" , "selected" : false } ,
"options" : [ { "text" : "template" , "value" : "template" , "selected" : true } ]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
} ,
{
"name" : "bucket" ,
"type" : "custom" ,
"query" : "lvl2" ,
2026-05-28 10:32:52 +02:00
"current" : { "text" : "lvl2" , "value" : "lvl2" , "selected" : false } ,
"options" : [ { "text" : "lvl2" , "value" : "lvl2" , "selected" : true } ]
feat(dashboardapi): no-data-duplication rule for parent dashboards (#39)
When generateDashboardsForGraph builds a root dashboard for a parent (e.g.
pumpingStation) and a set of child dashboards (e.g. measurements), it now
removes any non-row panel from the root whose meta.emittedFields are fully
covered by panels declared in any child dashboard. Result: the parent
shows only metrics its children don't already plot, eliminating redundant
rendering of the same series in two dashboards.
- config/pumpingStation.json: 11 non-row panels annotated with
meta.emittedFields (Direction, Time Left, Flow Source, Fill %, Level (x2),
Volume, Net Flow Rate, Inflow+Outflow, Heights, Volume Limits).
- src/specificClass.js: generateDashboardsForGraph runs the parent-panel
filter after composing children; row panels always kept; panels without
emittedFields declaration always kept (no silent removal).
- test/basic/slice39-no-duplication.basic.test.js: 4 cases — annotation
presence, child-covered removal, no-overlap preservation, row preservation.
Closes #39
2026-05-26 18:01:58 +02:00
}
2026-01-13 14:29:43 +01:00
]
} ,
2026-05-28 10:32:52 +02:00
"time" : { "from" : "now-6h" , "to" : "now" } ,
2026-01-13 14:29:43 +01:00
"timezone" : "" ,
"title" : "template" ,
"uid" : null ,
"version" : 1
fix(dashboardAPI): show string fields in stat panels (reduceOptions.fields)
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>
2026-05-27 21:09:24 +02:00
}