feat(dashboard): split basin charts by unit + add y-axis labels to all charts
Some checks failed
CI / lint-and-test (push) Has been cancelled

Flow: m³/h, Power: kW, Basin Level: m, Basin Fill: % (0-100 fixed).
Level and fill in separate chart groups with their own gauges.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-04-14 13:19:32 +02:00
parent b693e0b90c
commit c8f149e204
2 changed files with 233 additions and 63 deletions

View File

@@ -755,12 +755,14 @@ def build_ui_tab():
ui_group(g_pump_b, "4b. Pump B", PG, width=4, order=6),
ui_group(g_pump_c, "4c. Pump C", PG, width=4, order=7),
# Trends on separate pages
ui_group(g_trend_short_flow, "Flow (10 min)", PG_SHORT, width=12, order=1),
ui_group(g_trend_short_power, "Power (10 min)", PG_SHORT, width=12, order=2),
ui_group("ui_grp_trend_short_basin", "Basin (10 min)", PG_SHORT, width=12, order=3),
ui_group(g_trend_long_flow, "Flow (1 hour)", PG_LONG, width=12, order=1),
ui_group(g_trend_long_power, "Power (1 hour)", PG_LONG, width=12, order=2),
ui_group("ui_grp_trend_long_basin", "Basin (1 hour)", PG_LONG, width=12, order=3),
ui_group(g_trend_short_flow, "Flow (10 min)", PG_SHORT, width=12, order=1),
ui_group(g_trend_short_power, "Power (10 min)", PG_SHORT, width=12, order=2),
ui_group("ui_grp_trend_short_basin_level", "Basin Level (10 min)", PG_SHORT, width=12, order=3),
ui_group("ui_grp_trend_short_basin_fill", "Basin Fill (10 min)", PG_SHORT, width=12, order=4),
ui_group(g_trend_long_flow, "Flow (1 hour)", PG_LONG, width=12, order=1),
ui_group(g_trend_long_power, "Power (1 hour)", PG_LONG, width=12, order=2),
ui_group("ui_grp_trend_long_basin_level", "Basin Level (1 hour)", PG_LONG, width=12, order=3),
ui_group("ui_grp_trend_long_basin_fill", "Basin Fill (1 hour)", PG_LONG, width=12, order=4),
]
nodes.append(comment("c_ui_title", TAB_UI, LANE_X[2], 20,
@@ -929,10 +931,10 @@ def build_ui_tab():
["ui_ps_netflow"],
["ui_ps_timeleft"],
["ui_ps_qin"],
# Trend + gauge outputs (short + long page gauges)
["trend_short_basin", "trend_long_basin", "gauge_ps_fill", "gauge_ps_fill_long"], # fill %
["trend_short_basin", "trend_long_basin", "gauge_ps_level", "gauge_ps_level_long"], # level
["trend_short_basin", "trend_long_basin"], # net flow
# Trend + gauge outputs — split level and fill to separate charts
["trend_short_fill", "trend_long_fill", "gauge_ps_fill", "gauge_ps_fill_long"], # fill % → fill chart + gauges
["trend_short_level", "trend_long_level", "gauge_ps_level", "gauge_ps_level_long"], # level → level chart + gauges
["trend_short_level", "trend_long_level"], # net flow → level chart (shared axis)
],
))
@@ -1082,16 +1084,16 @@ def build_ui_tab():
nodes.append(ui_chart(
"trend_short_flow", TAB_UI, LANE_X[3], y_charts + 40,
g_trend_short_flow,
"Flow per pump — 10 min", "Flow per pump (m³/h)",
width="12", height="8",
"Flow per pump — 10 min", "Flow per pump",
width=12, height=8, y_axis_label="m³/h",
remove_older="10", remove_older_unit="60", remove_older_points="300",
order=1,
))
nodes.append(ui_chart(
"trend_short_power", TAB_UI, LANE_X[3], y_charts + 120,
g_trend_short_power,
"Power per pump — 10 min", "Power per pump (kW)",
width="12", height="8",
"Power per pump — 10 min", "Power per pump",
width=12, height=8, y_axis_label="kW",
remove_older="10", remove_older_unit="60", remove_older_points="300",
order=1,
))
@@ -1099,16 +1101,16 @@ def build_ui_tab():
nodes.append(ui_chart(
"trend_long_flow", TAB_UI, LANE_X[3], y_charts + 200,
g_trend_long_flow,
"Flow per pump — 1 hour", "Flow per pump (m³/h)",
width="12", height="8",
"Flow per pump — 1 hour", "Flow per pump",
width=12, height=8, y_axis_label="m³/h",
remove_older="60", remove_older_unit="60", remove_older_points="1800",
order=1,
))
nodes.append(ui_chart(
"trend_long_power", TAB_UI, LANE_X[3], y_charts + 280,
g_trend_long_power,
"Power per pump — 1 hour", "Power per pump (kW)",
width="12", height="8",
"Power per pump — 1 hour", "Power per pump",
width=12, height=8, y_axis_label="kW",
remove_older="60", remove_older_unit="60", remove_older_points="1800",
order=1,
))
@@ -1130,25 +1132,41 @@ def build_ui_tab():
{"color": "#f44336", "from": 95},
]
for suffix, grp, remove_older, remove_points, y_off in [
("short", "ui_grp_trend_short_basin", "10", "300", 360),
("long", "ui_grp_trend_long_basin", "60", "1800", 540),
for suffix, remove_older, remove_points, y_off in [
("short", "10", "300", 360),
("long", "60", "1800", 540),
]:
# Basin trend chart (width 8 to leave room for gauges)
label = "10 min" if suffix == "short" else "1 hour"
grp_level = f"ui_grp_trend_{suffix}_basin_level"
grp_fill = f"ui_grp_trend_{suffix}_basin_fill"
# Basin LEVEL chart (m) — also receives net flow (m³/h) on right axis
# via eCharts dual-axis configured by the first data message
nodes.append(ui_chart(
f"trend_{suffix}_basin", TAB_UI, LANE_X[3], y_charts + y_off,
grp,
f"Basin {'10 min' if suffix == 'short' else '1 hour'}", "Basin metrics",
width=8, height=8,
f"trend_{suffix}_level", TAB_UI, LANE_X[3], y_charts + y_off,
grp_level,
f"Basin Level — {label}", "Basin Level + Net Flow",
width=8, height=8, y_axis_label="m",
remove_older=remove_older, remove_older_unit="60",
remove_older_points=remove_points,
y_axis_label="", order=1,
order=1,
))
# Tank gauge: basin level 03 m
# Basin FILL chart (%) — simple single-axis
nodes.append(ui_chart(
f"trend_{suffix}_fill", TAB_UI, LANE_X[3], y_charts + y_off + 80,
grp_fill,
f"Basin Fill — {label}", "Basin Fill",
width=8, height=6, y_axis_label="%",
remove_older=remove_older, remove_older_unit="60",
remove_older_points=remove_points,
ymin=0, ymax=100, order=1,
))
# Tank gauge: basin level 04 m — in the level group
gauge_id_suffix = "" if suffix == "short" else "_long"
nodes.append({
"id": f"gauge_ps_level{gauge_id_suffix}", "type": "ui-gauge",
"z": TAB_UI, "group": grp,
"z": TAB_UI, "group": grp_level,
"name": f"Basin level gauge ({suffix})",
"gtype": "gauge-tank", "gstyle": "Rounded",
"title": "Level", "units": "m",
@@ -1159,10 +1177,10 @@ def build_ui_tab():
"icon": "", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10,
"x": LANE_X[4], "y": y_charts + y_off, "wires": [],
})
# 270° arc: fill %
# 270° arc: fill % — in the fill group
nodes.append({
"id": f"gauge_ps_fill{gauge_id_suffix}", "type": "ui-gauge",
"z": TAB_UI, "group": grp,
"z": TAB_UI, "group": grp_fill,
"name": f"Basin fill gauge ({suffix})",
"gtype": "gauge-34", "gstyle": "Rounded",
"title": "Fill", "units": "%",

View File

@@ -1355,9 +1355,9 @@
"visible": true
},
{
"id": "ui_grp_trend_short_basin",
"id": "ui_grp_trend_short_basin_level",
"type": "ui-group",
"name": "Basin (10 min)",
"name": "Basin Level (10 min)",
"page": "ui_page_short_trends",
"width": "12",
"height": "1",
@@ -1368,6 +1368,20 @@
"disabled": false,
"visible": true
},
{
"id": "ui_grp_trend_short_basin_fill",
"type": "ui-group",
"name": "Basin Fill (10 min)",
"page": "ui_page_short_trends",
"width": "12",
"height": "1",
"order": 4,
"showTitle": true,
"className": "",
"groupType": "default",
"disabled": false,
"visible": true
},
{
"id": "ui_grp_trend_long_flow",
"type": "ui-group",
@@ -1397,9 +1411,9 @@
"visible": true
},
{
"id": "ui_grp_trend_long_basin",
"id": "ui_grp_trend_long_basin_level",
"type": "ui-group",
"name": "Basin (1 hour)",
"name": "Basin Level (1 hour)",
"page": "ui_page_long_trends",
"width": "12",
"height": "1",
@@ -1410,6 +1424,20 @@
"disabled": false,
"visible": true
},
{
"id": "ui_grp_trend_long_basin_fill",
"type": "ui-group",
"name": "Basin Fill (1 hour)",
"page": "ui_page_long_trends",
"width": "12",
"height": "1",
"order": 4,
"showTitle": true,
"className": "",
"groupType": "default",
"disabled": false,
"visible": true
},
{
"id": "c_ui_title",
"type": "comment",
@@ -1899,20 +1927,20 @@
"ui_ps_qin"
],
[
"trend_short_basin",
"trend_long_basin",
"trend_short_fill",
"trend_long_fill",
"gauge_ps_fill",
"gauge_ps_fill_long"
],
[
"trend_short_basin",
"trend_long_basin",
"trend_short_level",
"trend_long_level",
"gauge_ps_level",
"gauge_ps_level_long"
],
[
"trend_short_basin",
"trend_long_basin"
"trend_short_level",
"trend_long_level"
]
]
},
@@ -3170,7 +3198,7 @@
"z": "tab_ui",
"group": "ui_grp_trend_short_flow",
"name": "Flow per pump \u2014 10 min",
"label": "Flow per pump (m\u00b3/h)",
"label": "Flow per pump",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3184,7 +3212,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "m\u00b3/h",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3232,7 +3260,7 @@
"z": "tab_ui",
"group": "ui_grp_trend_short_power",
"name": "Power per pump \u2014 10 min",
"label": "Power per pump (kW)",
"label": "Power per pump",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3246,7 +3274,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "kW",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3294,7 +3322,7 @@
"z": "tab_ui",
"group": "ui_grp_trend_long_flow",
"name": "Flow per pump \u2014 1 hour",
"label": "Flow per pump (m\u00b3/h)",
"label": "Flow per pump",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3308,7 +3336,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "m\u00b3/h",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3356,7 +3384,7 @@
"z": "tab_ui",
"group": "ui_grp_trend_long_power",
"name": "Power per pump \u2014 1 hour",
"label": "Power per pump (kW)",
"label": "Power per pump",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3370,7 +3398,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "kW",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3413,12 +3441,12 @@
]
},
{
"id": "trend_short_basin",
"id": "trend_short_level",
"type": "ui-chart",
"z": "tab_ui",
"group": "ui_grp_trend_short_basin",
"name": "Basin \u2014 10 min",
"label": "Basin metrics",
"group": "ui_grp_trend_short_basin_level",
"name": "Basin Level \u2014 10 min",
"label": "Basin Level + Net Flow",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3432,7 +3460,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "m",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3474,11 +3502,73 @@
[]
]
},
{
"id": "trend_short_fill",
"type": "ui-chart",
"z": "tab_ui",
"group": "ui_grp_trend_short_basin_fill",
"name": "Basin Fill \u2014 10 min",
"label": "Basin Fill",
"order": 1,
"chartType": "line",
"interpolation": "linear",
"category": "topic",
"categoryType": "msg",
"xAxisLabel": "",
"xAxisType": "time",
"xAxisProperty": "",
"xAxisPropertyType": "timestamp",
"xAxisFormat": "",
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "%",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "0",
"ymax": "100",
"removeOlder": "10",
"removeOlderUnit": "60",
"removeOlderPoints": "300",
"action": "append",
"stackSeries": false,
"pointShape": "circle",
"pointRadius": 4,
"showLegend": true,
"bins": 10,
"colors": [
"#0095FF",
"#FF0000",
"#FF7F0E",
"#2CA02C",
"#A347E1",
"#D62728",
"#FF9896",
"#9467BD",
"#C5B0D5"
],
"textColor": [
"#666666"
],
"textColorDefault": true,
"gridColor": [
"#e5e5e5"
],
"gridColorDefault": true,
"width": 8,
"height": 6,
"className": "",
"x": 900,
"y": 2720,
"wires": [
[]
]
},
{
"id": "gauge_ps_level",
"type": "ui-gauge",
"z": "tab_ui",
"group": "ui_grp_trend_short_basin",
"group": "ui_grp_trend_short_basin_level",
"name": "Basin level gauge (short)",
"gtype": "gauge-tank",
"gstyle": "Rounded",
@@ -3525,7 +3615,7 @@
"id": "gauge_ps_fill",
"type": "ui-gauge",
"z": "tab_ui",
"group": "ui_grp_trend_short_basin",
"group": "ui_grp_trend_short_basin_fill",
"name": "Basin fill gauge (short)",
"gtype": "gauge-34",
"gstyle": "Rounded",
@@ -3569,12 +3659,12 @@
"wires": []
},
{
"id": "trend_long_basin",
"id": "trend_long_level",
"type": "ui-chart",
"z": "tab_ui",
"group": "ui_grp_trend_long_basin",
"name": "Basin \u2014 1 hour",
"label": "Basin metrics",
"group": "ui_grp_trend_long_basin_level",
"name": "Basin Level \u2014 1 hour",
"label": "Basin Level + Net Flow",
"order": 1,
"chartType": "line",
"interpolation": "linear",
@@ -3588,7 +3678,7 @@
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "",
"yAxisLabel": "m",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "",
@@ -3630,11 +3720,73 @@
[]
]
},
{
"id": "trend_long_fill",
"type": "ui-chart",
"z": "tab_ui",
"group": "ui_grp_trend_long_basin_fill",
"name": "Basin Fill \u2014 1 hour",
"label": "Basin Fill",
"order": 1,
"chartType": "line",
"interpolation": "linear",
"category": "topic",
"categoryType": "msg",
"xAxisLabel": "",
"xAxisType": "time",
"xAxisProperty": "",
"xAxisPropertyType": "timestamp",
"xAxisFormat": "",
"xAxisFormatType": "auto",
"xmin": "",
"xmax": "",
"yAxisLabel": "%",
"yAxisProperty": "payload",
"yAxisPropertyType": "msg",
"ymin": "0",
"ymax": "100",
"removeOlder": "60",
"removeOlderUnit": "60",
"removeOlderPoints": "1800",
"action": "append",
"stackSeries": false,
"pointShape": "circle",
"pointRadius": 4,
"showLegend": true,
"bins": 10,
"colors": [
"#0095FF",
"#FF0000",
"#FF7F0E",
"#2CA02C",
"#A347E1",
"#D62728",
"#FF9896",
"#9467BD",
"#C5B0D5"
],
"textColor": [
"#666666"
],
"textColorDefault": true,
"gridColor": [
"#e5e5e5"
],
"gridColorDefault": true,
"width": 8,
"height": 6,
"className": "",
"x": 900,
"y": 2900,
"wires": [
[]
]
},
{
"id": "gauge_ps_level_long",
"type": "ui-gauge",
"z": "tab_ui",
"group": "ui_grp_trend_long_basin",
"group": "ui_grp_trend_long_basin_level",
"name": "Basin level gauge (long)",
"gtype": "gauge-tank",
"gstyle": "Rounded",
@@ -3681,7 +3833,7 @@
"id": "gauge_ps_fill_long",
"type": "ui-gauge",
"z": "tab_ui",
"group": "ui_grp_trend_long_basin",
"group": "ui_grp_trend_long_basin_fill",
"name": "Basin fill gauge (long)",
"gtype": "gauge-34",
"gstyle": "Rounded",