Files
EVOLV/examples/pumpingstation-3pumps-dashboard/build_flow.py

1369 lines
60 KiB
Python
Raw Normal View History

feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
#!/usr/bin/env python3
"""
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
Generate the multi-tab Node-RED flow for the
'pumpingstation-3pumps-dashboard' end-to-end demo.
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
Layout philosophy
-----------------
Every node gets a home tab based on its CONCERN, not the data it touches:
Tab 1 Process Plant only EVOLV nodes (pumps, MGC, PS, measurements)
+ small per-node output formatters. NO UI, NO
demo drivers, NO setup logic. This is the
deployable plant model in isolation.
Tab 2 Dashboard UI only ui-* widgets. NO routing logic beyond
topic-tagging for the chart legends.
Tab 3 Demo Drivers auto random demand generator + station-wide
command fan-outs. Only here so the demo "lives"
without an operator. Removable in production.
Tab 4 Setup & Init one-shot deploy-time injects (mode, scaling,
auto-startup). Easy to disable for production.
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
Cross-tab wiring is via NAMED link-out / link-in pairs, not direct
wires. The channel names are the contract see CHANNELS below.
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
Spacing
-------
Five lanes per tab, x in [120, 380, 640, 900, 1160]. Row pitch 80 px.
Major sections separated by 200 px y-shift + a comment header.
To regenerate:
python3 build_flow.py > flow.json
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"""
import json
import sys
# ---------------------------------------------------------------------------
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# Tab IDs
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
# ---------------------------------------------------------------------------
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
TAB_PROCESS = "tab_process"
TAB_UI = "tab_ui"
TAB_DRIVERS = "tab_drivers"
TAB_SETUP = "tab_setup"
# ---------------------------------------------------------------------------
# Spacing constants
# ---------------------------------------------------------------------------
LANE_X = [120, 380, 640, 900, 1160, 1420]
ROW = 80 # standard inter-row pitch
SECTION_GAP = 200 # additional shift between major sections
# ---------------------------------------------------------------------------
# Cross-tab link channel names (the wiring contract)
# ---------------------------------------------------------------------------
# command channels: dashboard or drivers -> process
CH_DEMAND = "cmd:demand" # numeric demand (m3/h)
CH_RANDOM_TOGGLE = "cmd:randomToggle" # 'on' / 'off'
CH_MODE = "cmd:mode" # 'auto' / 'virtualControl' setMode broadcast
CH_STATION_START = "cmd:station-startup"
CH_STATION_STOP = "cmd:station-shutdown"
CH_STATION_ESTOP = "cmd:station-estop"
CH_PUMP_SETPOINT = {"pump_a": "cmd:setpoint-A",
"pump_b": "cmd:setpoint-B",
"pump_c": "cmd:setpoint-C"}
CH_PUMP_SEQUENCE = {"pump_a": "cmd:pump-A-seq", # carries startup/shutdown
"pump_b": "cmd:pump-B-seq",
"pump_c": "cmd:pump-C-seq"}
# event channels: process -> dashboard
CH_PUMP_EVT = {"pump_a": "evt:pump-A",
"pump_b": "evt:pump-B",
"pump_c": "evt:pump-C"}
CH_MGC_EVT = "evt:mgc"
CH_PS_EVT = "evt:ps"
PUMPS = ["pump_a", "pump_b", "pump_c"]
PUMP_LABELS = {"pump_a": "Pump A", "pump_b": "Pump B", "pump_c": "Pump C"}
# ---------------------------------------------------------------------------
# Generic node-builder helpers
# ---------------------------------------------------------------------------
def comment(node_id, tab, x, y, name, info=""):
return {"id": node_id, "type": "comment", "z": tab, "name": name,
"info": info, "x": x, "y": y, "wires": []}
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def inject(node_id, tab, x, y, name, topic, payload, payload_type="str",
once=False, repeat="", once_delay="0.5", wires=None):
"""Inject node using the per-prop v/vt form so payload_type=json works."""
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "inject", "z": tab, "name": name,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"props": [
{"p": "topic", "vt": "str"},
{"p": "payload", "v": str(payload), "vt": payload_type},
],
"topic": topic, "payload": str(payload), "payloadType": payload_type,
"repeat": repeat, "crontab": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"once": once, "onceDelay": once_delay,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"x": x, "y": y, "wires": [wires or []],
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def function_node(node_id, tab, x, y, name, code, outputs=1, wires=None):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "function", "z": tab, "name": name,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"func": code, "outputs": outputs,
"noerr": 0, "initialize": "", "finalize": "", "libs": [],
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": x, "y": y, "wires": wires if wires is not None else [[] for _ in range(outputs)],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def link_out(node_id, tab, x, y, channel_name, target_in_ids):
"""Mode 'link' — fires the named link-in nodes (by id)."""
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "link out", "z": tab, "name": channel_name,
"mode": "link", "links": list(target_in_ids),
"x": x, "y": y, "wires": [],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def link_in(node_id, tab, x, y, channel_name, source_out_ids, downstream):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "link in", "z": tab, "name": channel_name,
"links": list(source_out_ids),
"x": x, "y": y, "wires": [downstream or []],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def debug_node(node_id, tab, x, y, name, target="payload",
target_type="msg", active=False):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "debug", "z": tab, "name": name,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"active": active, "tosidebar": True, "console": False, "tostatus": False,
"complete": target, "targetType": target_type,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": x, "y": y, "wires": [],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
# ---------------------------------------------------------------------------
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# Dashboard scaffolding (ui-base / ui-theme / ui-page / ui-group)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
# ---------------------------------------------------------------------------
def dashboard_scaffold():
base = {
"id": "ui_base_ps_demo", "type": "ui-base", "name": "EVOLV Demo",
"path": "/dashboard", "appIcon": "",
"includeClientData": True,
"acceptsClientConfig": ["ui-notification", "ui-control"],
"showPathInSidebar": True, "headerContent": "page",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"navigationStyle": "default", "titleBarStyle": "default",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
theme = {
"id": "ui_theme_ps_demo", "type": "ui-theme", "name": "EVOLV Theme",
"colors": {
"surface": "#ffffff", "primary": "#0f52a5",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"bgPage": "#f4f6fa", "groupBg": "#ffffff", "groupOutline": "#cccccc",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
},
"sizes": {
"density": "default", "pagePadding": "12px",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"groupGap": "12px", "groupBorderRadius": "6px", "widgetGap": "8px",
},
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
page_control = {
"id": "ui_page_control", "type": "ui-page",
"name": "Control", "ui": "ui_base_ps_demo",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"path": "/pumping-station-demo", "icon": "water_pump",
"layout": "grid", "theme": "ui_theme_ps_demo",
"breakpoints": [{"name": "Default", "px": "0", "cols": "12"}],
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"order": 1, "className": "",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
page_short = {
"id": "ui_page_short_trends", "type": "ui-page",
"name": "Trends — 10 min", "ui": "ui_base_ps_demo",
"path": "/pumping-station-demo/trends-short", "icon": "show_chart",
"layout": "grid", "theme": "ui_theme_ps_demo",
"breakpoints": [{"name": "Default", "px": "0", "cols": "12"}],
"order": 2, "className": "",
}
page_long = {
"id": "ui_page_long_trends", "type": "ui-page",
"name": "Trends — 1 hour", "ui": "ui_base_ps_demo",
"path": "/pumping-station-demo/trends-long", "icon": "timeline",
"layout": "grid", "theme": "ui_theme_ps_demo",
"breakpoints": [{"name": "Default", "px": "0", "cols": "12"}],
"order": 3, "className": "",
}
return [base, theme, page_control, page_short, page_long]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
def ui_group(group_id, name, page_id, width=6, order=1):
return {
"id": group_id, "type": "ui-group", "name": name, "page": page_id,
"width": str(width), "height": "1", "order": order,
"showTitle": True, "className": "", "groupType": "default",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"disabled": False, "visible": True,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def ui_text(node_id, tab, x, y, group, name, label, fmt, layout="row-left"):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "ui-text", "z": tab, "group": group,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"order": 1, "width": "0", "height": "0", "name": name, "label": label,
"format": fmt, "layout": layout, "style": False, "font": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"fontSize": 14, "color": "#000000",
"x": x, "y": y, # editor canvas position — without these
# Node-RED dumps every ui-text at (0,0)
# and you get a pile in the top-left corner
"wires": [],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def ui_button(node_id, tab, x, y, group, name, label, payload, payload_type,
topic, color="#0f52a5", icon="play_arrow", wires=None):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "ui-button", "z": tab, "group": group,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": name, "label": label, "order": 1, "width": "0", "height": "0",
"tooltip": "", "color": "#ffffff", "bgcolor": color,
"className": "", "icon": icon, "iconPosition": "left",
"payload": payload, "payloadType": payload_type,
"topic": topic, "topicType": "str", "buttonType": "default",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": x, "y": y, "wires": [wires or []],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def ui_slider(node_id, tab, x, y, group, name, label, mn, mx, step=1.0,
topic="", wires=None):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "ui-slider", "z": tab, "group": group,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": name, "label": label, "tooltip": "", "order": 1,
"width": "0", "height": "0", "passthru": True, "outs": "end",
"topic": topic, "topicType": "str",
"min": str(mn), "max": str(mx), "step": str(step),
"showLabel": True, "showValue": True, "labelPosition": "top",
"valuePosition": "left", "thumbLabel": False, "iconStart": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"iconEnd": "", "x": x, "y": y, "wires": [wires or []],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def ui_switch(node_id, tab, x, y, group, name, label, on_value, off_value,
topic, wires=None):
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "ui-switch", "z": tab, "group": group,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": name, "label": label, "tooltip": "", "order": 1,
"width": "0", "height": "0", "passthru": True, "decouple": "false",
"topic": topic, "topicType": "str",
"style": "", "className": "", "evaluate": "true",
"onvalue": on_value, "onvalueType": "str",
"onicon": "auto_mode", "oncolor": "#0f52a5",
"offvalue": off_value, "offvalueType": "str",
"officon": "back_hand", "offcolor": "#888888",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": x, "y": y, "wires": [wires or []],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def ui_chart(node_id, tab, x, y, group, name, label,
width=12, height=6,
remove_older="15", remove_older_unit="60",
remove_older_points="",
y_axis_label="", ymin=None, ymax=None, order=1):
"""
FlowFuse ui-chart (line type, time x-axis).
IMPORTANT: This template is derived from the working charts in
rotatingMachine/examples/03-Dashboard.json. Every field listed below
is required or the chart renders blank. Key gotchas:
- `width` / `height` must be NUMBERS not strings.
- `interpolation` must be set ("linear", "step", "bezier",
"cubic", "cubic-mono") or no line is drawn.
- `yAxisProperty: "payload"` + `yAxisPropertyType: "msg"` tells
the chart WHERE in the msg to find the y-value. Without these
the chart has no data to plot.
- `xAxisPropertyType: "timestamp"` tells the chart to use
msg.timestamp (or auto-generated timestamp) for the x-axis.
- `removeOlderPoints` should be "" (empty string) to let
removeOlder + removeOlderUnit control retention, OR a number
string to cap points per series.
"""
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return {
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": node_id, "type": "ui-chart", "z": tab, "group": group,
"name": name, "label": label, "order": order,
"chartType": "line",
"interpolation": "linear",
# Series identification
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"category": "topic", "categoryType": "msg",
# X-axis (time)
"xAxisLabel": "", "xAxisType": "time",
"xAxisProperty": "", "xAxisPropertyType": "timestamp",
"xAxisFormat": "", "xAxisFormatType": "auto",
"xmin": "", "xmax": "",
# Y-axis (msg.payload)
"yAxisLabel": y_axis_label,
"yAxisProperty": "payload", "yAxisPropertyType": "msg",
"ymin": "" if ymin is None else str(ymin),
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"ymax": "" if ymax is None else str(ymax),
# Data retention
"removeOlder": str(remove_older),
"removeOlderUnit": str(remove_older_unit),
"removeOlderPoints": str(remove_older_points),
# Rendering
"action": "append",
"stackSeries": False,
"pointShape": "circle", "pointRadius": 4,
"showLegend": True,
"bins": 10,
# Colours (defaults — chart auto-cycles through these per series)
"colors": [
"#0095FF", "#FF0000", "#FF7F0E", "#2CA02C",
"#A347E1", "#D62728", "#FF9896", "#9467BD", "#C5B0D5",
],
"textColor": ["#666666"], "textColorDefault": True,
"gridColor": ["#e5e5e5"], "gridColorDefault": True,
# Editor layout + dimensions (NUMBERS, not strings)
"width": int(width), "height": int(height), "className": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": x, "y": y,
"wires": [[]],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
}
# ---------------------------------------------------------------------------
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# Tab 1 — PROCESS PLANT
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
# ---------------------------------------------------------------------------
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
def build_process_tab():
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes = []
nodes.append({
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": TAB_PROCESS, "type": "tab",
"label": "🏭 Process Plant",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"disabled": False,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"info": "EVOLV plant model: pumpingStation + machineGroupControl + 3 rotatingMachines, each with upstream and downstream pressure measurements.\n\nReceives commands via link-in nodes from the Dashboard / Demo Drivers tabs. Emits per-pump status via link-out per pump.\n\nNo UI, no demo drivers, no one-shot setup logic on this tab — those live on their own tabs so this layer can be lifted into production unchanged.",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
})
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(comment("c_process_title", TAB_PROCESS, LANE_X[2], 20,
"🏭 PROCESS PLANT — EVOLV nodes only",
"Per pump: 2 measurement sensors → rotatingMachine → output formatter → link-out to dashboard.\n"
"MGC orchestrates 3 pumps. PS observes basin (manual mode for the demo).\n"
"All cross-tab wires are link-in / link-out by named channel."
))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ---------------- Per-pump rows ----------------
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
for i, pump in enumerate(PUMPS):
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
label = PUMP_LABELS[pump]
# Each pump occupies a 4-row block, separated by SECTION_GAP from the next.
y_section = 100 + i * SECTION_GAP
nodes.append(comment(f"c_{pump}", TAB_PROCESS, LANE_X[2], y_section,
f"── {label} ──",
"Up + Dn pressure sensors register as children. "
"rotatingMachine emits state on port 0 (formatted then link-out to UI). "
"Port 2 emits registerChild → MGC."
))
# Two measurement sensors (upstream + downstream)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
for j, pos in enumerate(("upstream", "downstream")):
mid = f"meas_{pump}_{pos[0]}"
absmin, absmax = (50, 400) if pos == "upstream" else (800, 2200)
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
mid_label = f"PT-{label.split()[1]}-{'Up' if pos == 'upstream' else 'Dn'}"
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append({
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": mid, "type": "measurement", "z": TAB_PROCESS,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": mid_label,
"mode": "analog", "channels": "[]",
"scaling": False,
"i_min": 0, "i_max": 1, "i_offset": 0,
"o_min": absmin, "o_max": absmax,
"simulator": True,
"smooth_method": "mean", "count": "5",
"processOutputFormat": "process", "dbaseOutputFormat": "influxdb",
"uuid": f"sensor-{pump}-{pos}",
"supplier": "vega", "category": "sensor",
"assetType": "pressure", "model": "vega-pressure-10",
"unit": "mbar", "assetTagNumber": f"PT-{i+1}-{pos[0].upper()}",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"enableLog": False, "logLevel": "warn",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"positionVsParent": pos, "positionIcon": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"hasDistance": False, "distance": 0, "distanceUnit": "m",
"distanceDescription": "",
"x": LANE_X[1], "y": y_section + 40 + j * 50,
# Port 2 -> pump (registerChild). Ports 0/1 unused for now.
"wires": [[], [], [pump]],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
})
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# link-in for setpoint slider (from dashboard)
nodes.append(link_in(
f"lin_setpoint_{pump}", TAB_PROCESS, LANE_X[0], y_section + 60,
CH_PUMP_SETPOINT[pump],
source_out_ids=[f"lout_setpoint_{pump}_dash"],
downstream=[f"build_setpoint_{pump}"],
))
nodes.append(function_node(
f"build_setpoint_{pump}", TAB_PROCESS, LANE_X[1] + 220, y_section + 60,
f"build setpoint cmd ({label})",
"msg.topic = 'execMovement';\n"
"msg.payload = { source: 'GUI', action: 'execMovement', "
"setpoint: Number(msg.payload) };\n"
"return msg;",
outputs=1, wires=[[pump]],
))
# link-in for per-pump sequence (start/stop) commands
nodes.append(link_in(
f"lin_seq_{pump}", TAB_PROCESS, LANE_X[0], y_section + 110,
CH_PUMP_SEQUENCE[pump],
source_out_ids=[f"lout_seq_{pump}_dash"],
downstream=[pump],
))
# The pump itself
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append({
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": pump, "type": "rotatingMachine", "z": TAB_PROCESS,
"name": label,
"speed": "10",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"startup": "2", "warmup": "1", "shutdown": "2", "cooldown": "1",
"movementMode": "staticspeed",
"machineCurve": "",
"uuid": f"pump-{pump}",
"supplier": "hidrostal", "category": "pump",
"assetType": "pump-centrifugal",
"model": "hidrostal-H05K-S03R",
"unit": "m3/h",
"curvePressureUnit": "mbar", "curveFlowUnit": "m3/h",
"curvePowerUnit": "kW", "curveControlUnit": "%",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"enableLog": False, "logLevel": "warn",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"positionVsParent": "atEquipment", "positionIcon": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"hasDistance": False, "distance": 0, "distanceUnit": "m",
"distanceDescription": "",
"x": LANE_X[3], "y": y_section + 80,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"wires": [
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
[f"format_{pump}"], # port 0 process -> formatter
[], # port 1 dbase
[MGC_ID], # port 2 -> MGC for registerChild
],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
})
# Per-pump output formatter: builds a fat object with all fields.
# The dashboard dispatcher (on the UI tab) then splits it into
# plain-string payloads per ui-text widget. One link-out per pump.
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(function_node(
f"format_{pump}", TAB_PROCESS, LANE_X[4], y_section + 80,
f"format {label} port 0",
"const p = msg.payload || {};\n"
"const c = context.get('c') || {};\n"
"Object.assign(c, p);\n"
"context.set('c', c);\n"
"function find(prefix) {\n"
" for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n"
" return null;\n"
"}\n"
"const flow = find('flow.predicted.downstream.');\n"
"const power = find('power.predicted.atequipment.');\n"
"const pU = find('pressure.measured.upstream.');\n"
"const pD = find('pressure.measured.downstream.');\n"
"msg.payload = {\n"
" state: c.state || 'idle',\n"
" mode: c.mode || 'auto',\n"
" ctrl: c.ctrl != null ? Number(c.ctrl).toFixed(1) + '%' : 'n/a',\n"
" flow: flow != null ? Number(flow).toFixed(1) + ' m³/h' : 'n/a',\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
" power: power != null ? Number(power).toFixed(2) + ' kW' : 'n/a',\n"
" pUp: pU != null ? Number(pU).toFixed(0) + ' mbar' : 'n/a',\n"
" pDn: pD != null ? Number(pD).toFixed(0) + ' mbar' : 'n/a',\n"
" flowNum: flow != null ? Number(flow) : null,\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
" powerNum: power != null ? Number(power) : null,\n"
"};\n"
"return msg;",
outputs=1, wires=[[f"lout_evt_{pump}"]],
))
# link-out: one per pump → dashboard dispatcher
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_out(
f"lout_evt_{pump}", TAB_PROCESS, LANE_X[5], y_section + 80,
CH_PUMP_EVT[pump],
target_in_ids=[f"lin_evt_{pump}_dash"],
))
# ---------------- MGC ----------------
y_mgc = 100 + 3 * SECTION_GAP
nodes.append(comment("c_mgc", TAB_PROCESS, LANE_X[2], y_mgc,
"── MGC ── (orchestrates the 3 pumps via optimalcontrol)",
"Receives Qd from cmd:demand link-in. Distributes flow across pumps."
))
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
# MGC no longer receives direct Qd from the dashboard — PS drives it
# via level-based control or manual Qd forwarding. The demand_fanout
# has been replaced by: sinus → q_in → PS (levelbased), and
# slider → Qd → PS (manual mode only).
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append({
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": MGC_ID, "type": "machineGroupControl", "z": TAB_PROCESS,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": "MGC — Pump Group",
"uuid": "mgc-pump-group",
"category": "controller",
"assetType": "machinegroupcontrol",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"model": "default", "unit": "m3/h", "supplier": "evolv",
"enableLog": False, "logLevel": "warn",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"positionVsParent": "atEquipment", "positionIcon": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"hasDistance": False, "distance": 0, "distanceUnit": "m",
"distanceDescription": "",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"processOutputFormat": "process", "dbaseOutputFormat": "influxdb",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": LANE_X[3], "y": y_mgc + 80,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"wires": [
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
["format_mgc"], # port 0 → formatter
[], # port 1 dbase
[PS_ID], # port 2 → PS for registerChild
],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
})
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(function_node(
"format_mgc", TAB_PROCESS, LANE_X[4], y_mgc + 80,
"format MGC port 0",
"const p = msg.payload || {};\n"
"const c = context.get('c') || {};\n"
"Object.assign(c, p);\n"
"context.set('c', c);\n"
"function find(prefix) {\n"
" for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n"
" return null;\n"
"}\n"
"const totalFlow = find('flow.predicted.atequipment.') ?? find('downstream_predicted_flow');\n"
"const totalPower = find('power.predicted.atequipment.') ?? find('atEquipment_predicted_power');\n"
"const eff = find('efficiency.predicted.atequipment.');\n"
"msg.payload = {\n"
" totalFlow: totalFlow != null ? Number(totalFlow).toFixed(1) + ' m³/h' : 'n/a',\n"
" totalPower: totalPower != null ? Number(totalPower).toFixed(2) + ' kW' : 'n/a',\n"
" efficiency: eff != null ? Number(eff).toFixed(3) : 'n/a',\n"
" totalFlowNum: totalFlow != null ? Number(totalFlow) : null,\n"
" totalPowerNum: totalPower != null ? Number(totalPower) : null,\n"
"};\n"
"return msg;",
outputs=1, wires=[["lout_evt_mgc"]],
))
nodes.append(link_out(
"lout_evt_mgc", TAB_PROCESS, LANE_X[5], y_mgc + 80,
CH_MGC_EVT, target_in_ids=["lin_evt_mgc_dash"],
))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ---------------- PS ----------------
y_ps = 100 + 4 * SECTION_GAP
nodes.append(comment("c_ps", TAB_PROCESS, LANE_X[2], y_ps,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"── Pumping Station ── (basin model, levelbased control)",
"Receives q_in (simulated inflow) from Demo Drivers tab.\n"
"Level-based control starts/stops pumps via MGC when level crosses start/stop thresholds."
))
# link-in for simulated inflow from Drivers tab
nodes.append(link_in(
"lin_qin_at_ps", TAB_PROCESS, LANE_X[0], y_ps + 40,
"cmd:q_in", source_out_ids=["lout_qin_drivers"],
downstream=[PS_ID],
))
# link-in for manual Qd demand from Dashboard slider (only effective in manual mode)
nodes.append(link_in(
"lin_qd_at_ps", TAB_PROCESS, LANE_X[0], y_ps + 80,
"cmd:Qd", source_out_ids=["lout_demand_dash"],
downstream=["qd_to_ps_wrap"],
))
nodes.append(function_node(
"qd_to_ps_wrap", TAB_PROCESS, LANE_X[1], y_ps + 80,
"wrap slider → PS Qd",
"msg.topic = 'Qd';\n"
"return msg;",
outputs=1, wires=[[PS_ID]],
))
# link-in for PS mode toggle from Dashboard
nodes.append(link_in(
"lin_ps_mode_at_ps", TAB_PROCESS, LANE_X[0], y_ps + 120,
"cmd:ps-mode", source_out_ids=["lout_ps_mode_dash"],
downstream=[PS_ID],
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append({
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"id": PS_ID, "type": "pumpingStation", "z": TAB_PROCESS,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"name": "Pumping Station",
"uuid": "ps-basin-1",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"category": "station", "assetType": "pumpingstation",
"model": "default", "unit": "m3/s", "supplier": "evolv",
"enableLog": False, "logLevel": "warn",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"positionVsParent": "atEquipment", "positionIcon": "",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"hasDistance": False, "distance": 0, "distanceUnit": "m",
"distanceDescription": "",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"processOutputFormat": "process", "dbaseOutputFormat": "influxdb",
# === FULLY CONFIGURED PS — every field explicitly set ===
# Rule: ALWAYS configure ALL node fields. Defaults are for
# schema validation, not for realistic operation.
#
# Basin geometry: 30 m³, 4 m tall → surfaceArea = 7.5 m²
# Sized so peak sinus inflow (0.035 m³/s = 126 m³/h) takes
# ~6 min to fill from startLevel to overflow → pumps have time.
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"controlMode": "levelbased",
"basinVolume": 30,
"basinHeight": 4,
"heightInlet": 3.5,
"heightOutlet": 0.3,
"heightOverflow": 3.8,
"inletPipeDiameter": 0.3,
"outletPipeDiameter": 0.3,
# Level-based control thresholds
"startLevel": 2.0, # pumps ON above 2.0 m (50% of height)
"stopLevel": 1.0, # pumps OFF below 1.0 m (25% of height)
"minFlowLevel": 2.0, # 0% pump demand at startLevel (must match startLevel!)
"maxFlowLevel": 3.5, # 100% pump demand at this level
# Hydraulics
"refHeight": "NAP",
"minHeightBasedOn": "outlet",
"basinBottomRef": 0,
"staticHead": 12,
"maxDischargeHead": 24,
"pipelineLength": 80,
"defaultFluid": "wastewater",
"temperatureReferenceDegC": 15,
"maxInflowRate": 200,
# Safety guards
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"enableDryRunProtection": True,
"enableOverfillProtection": True,
"dryRunThresholdPercent": 5,
"overfillThresholdPercent": 95,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"timeleftToFullOrEmptyThresholdSeconds": 0,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"x": LANE_X[3], "y": y_ps + 80,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"wires": [
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
["format_ps"],
[],
],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
})
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(function_node(
"format_ps", TAB_PROCESS, LANE_X[4], y_ps + 80,
"format PS port 0",
"const p = msg.payload || {};\n"
"const c = context.get('c') || {};\n"
"Object.assign(c, p);\n"
"context.set('c', c);\n"
"function find(prefix) {\n"
" for (const k in c) { if (k.indexOf(prefix) === 0) return c[k]; }\n"
" return null;\n"
"}\n"
"const lvl = find('level.predicted.');\n"
"const vol = find('volume.predicted.');\n"
"const qIn = find('flow.measured.upstream.') || find('flow.measured.in.');\n"
"const qOut = find('flow.measured.downstream.') || find('flow.measured.out.');\n"
"// Compute derived metrics\n"
"// Basin capacity = basinVolume (config). Don't hardcode — read it once.\n"
"if (!context.get('maxVol')) context.set('maxVol', 30.0); // basinVolume from PS config\n"
"const maxVol = context.get('maxVol');\n"
"const fillPct = vol != null ? Math.min(100, Math.max(0, Math.round(Number(vol) / maxVol * 100))) : null;\n"
"const netM3h = (c.netFlow != null) ? Number(c.netFlow) * 3600 : null;\n"
"const seconds = (c.seconds != null && Number.isFinite(Number(c.seconds))) ? Number(c.seconds) : null;\n"
"const timeStr = seconds != null ? (seconds > 60 ? Math.round(seconds/60) + ' min' : Math.round(seconds) + ' s') : 'n/a';\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"msg.payload = {\n"
" direction: c.direction || 'steady',\n"
" level: lvl != null ? Number(lvl).toFixed(2) + ' m' : 'n/a',\n"
" volume: vol != null ? Number(vol).toFixed(1) + '' : 'n/a',\n"
" fillPct: fillPct != null ? fillPct + '%' : 'n/a',\n"
" netFlow: netM3h != null ? netM3h.toFixed(0) + ' m³/h' : 'n/a',\n"
" timeLeft: timeStr,\n"
" qIn: qIn != null ? (Number(qIn) * 3600).toFixed(0) + ' m³/h' : 'n/a',\n"
" qOut: qOut != null ? (Number(qOut) * 3600).toFixed(0) + ' m³/h' : 'n/a',\n"
" // Numerics for trends\n"
" levelNum: lvl != null ? Number(lvl) : null,\n"
" volumeNum: vol != null ? Number(vol) : null,\n"
" fillPctNum: fillPct,\n"
" netFlowNum: netM3h,\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"};\n"
"return msg;",
outputs=1, wires=[["lout_evt_ps"]],
))
nodes.append(link_out(
"lout_evt_ps", TAB_PROCESS, LANE_X[5], y_ps + 80,
CH_PS_EVT, target_in_ids=["lin_evt_ps_dash"],
))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ---------------- Mode broadcast (Auto/Manual to all pumps) ----------------
y_mode = 100 + 5 * SECTION_GAP
nodes.append(comment("c_mode_bcast", TAB_PROCESS, LANE_X[2], y_mode,
"── Mode broadcast ──",
"Single 'auto' / 'virtualControl' value fans out as setMode to all 3 pumps."
))
nodes.append(link_in(
"lin_mode", TAB_PROCESS, LANE_X[0], y_mode + 60,
CH_MODE,
source_out_ids=["lout_mode_dash"],
downstream=["fanout_mode"],
))
nodes.append(function_node(
"fanout_mode", TAB_PROCESS, LANE_X[1] + 220, y_mode + 60,
"fan setMode → 3 pumps",
"msg.topic = 'setMode';\n"
"return [msg, msg, msg];",
outputs=3, wires=[["pump_a"], ["pump_b"], ["pump_c"]],
))
# ---------------- Station-wide commands (start/stop/estop) ----------------
y_station = 100 + 6 * SECTION_GAP
nodes.append(comment("c_station_cmds", TAB_PROCESS, LANE_X[2], y_station,
"── Station-wide commands ── (Start All / Stop All / Emergency)",
"Each link-in carries a fully-built msg ready for handleInput; we just fan out 3-way."
))
for k, (chan, link_id, fn_name, label_suffix) in enumerate([
(CH_STATION_START, "lin_station_start", "fan_station_start", "startup"),
(CH_STATION_STOP, "lin_station_stop", "fan_station_stop", "shutdown"),
(CH_STATION_ESTOP, "lin_station_estop", "fan_station_estop", "emergency stop"),
]):
y = y_station + 60 + k * 60
nodes.append(link_in(
link_id, TAB_PROCESS, LANE_X[0], y, chan,
source_out_ids=[f"lout_{chan.replace(':', '_').replace('-', '_')}_dash"],
downstream=[fn_name],
))
nodes.append(function_node(
fn_name, TAB_PROCESS, LANE_X[1] + 220, y,
f"fan {label_suffix} → 3 pumps",
"return [msg, msg, msg];",
outputs=3, wires=[["pump_a"], ["pump_b"], ["pump_c"]],
))
return nodes
MGC_ID = "mgc_pumps"
PS_ID = "ps_basin"
# ---------------------------------------------------------------------------
# Tab 2 — DASHBOARD UI
# ---------------------------------------------------------------------------
def build_ui_tab():
nodes = []
nodes.append({
"id": TAB_UI, "type": "tab",
"label": "📊 Dashboard UI",
"disabled": False,
"info": "Every ui-* widget lives here. Inputs (sliders/switches/buttons) emit "
"via link-out; status text + charts receive via link-in. No business "
"logic on this tab.",
})
# Dashboard scaffold (page + theme + base) + groups
nodes += dashboard_scaffold()
PG = "ui_page_control" # control page is the main page
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
g_demand = "ui_grp_demand"
g_station = "ui_grp_station"
g_pump_a = "ui_grp_pump_a"
g_pump_b = "ui_grp_pump_b"
g_pump_c = "ui_grp_pump_c"
# Trend groups live on separate pages, not the control page.
PG_SHORT = "ui_page_short_trends"
PG_LONG = "ui_page_long_trends"
g_trend_short_flow = "ui_grp_trend_short_flow"
g_trend_short_power = "ui_grp_trend_short_power"
g_trend_long_flow = "ui_grp_trend_long_flow"
g_trend_long_power = "ui_grp_trend_long_power"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
g_mgc = "ui_grp_mgc"
g_ps = "ui_grp_ps"
nodes += [
ui_group(g_demand, "1. Process Demand", PG, width=12, order=1),
ui_group(g_station, "2. Station Controls", PG, width=12, order=2),
ui_group(g_mgc, "3a. MGC Status", PG, width=6, order=3),
ui_group(g_ps, "3b. Basin Status", PG, width=6, order=4),
ui_group(g_pump_a, "4a. Pump A", PG, width=4, order=5),
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_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),
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
]
nodes.append(comment("c_ui_title", TAB_UI, LANE_X[2], 20,
"📊 DASHBOARD UI — only ui-* widgets here",
"Layout: column 1 = inputs (sliders/switches/buttons) → link-outs.\n"
"Column 2 = link-ins from process → routed to text/gauge/chart widgets."
))
# ===== SECTION: Process Demand =====
y = 100
nodes.append(comment("c_ui_demand", TAB_UI, LANE_X[2], y,
"── Process Demand ──", ""))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(ui_slider(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"ui_demand_slider", TAB_UI, LANE_X[0], y + 40, g_demand,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"Manual demand (manual mode only)", "Manual demand (m³/h) — active in manual mode only",
0, 100, 5.0, "manualDemand",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
wires=["lout_demand_dash"]
))
nodes.append(link_out(
"lout_demand_dash", TAB_UI, LANE_X[1], y + 40,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"cmd:Qd", target_in_ids=["lin_qd_at_ps"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(ui_text(
"ui_demand_text", TAB_UI, LANE_X[3], y + 40, g_demand,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"Manual demand (active in manual mode)", "Manual demand",
"{{msg.payload}} m³/h"
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
# The slider value routes to PS as Qd — only effective in manual mode.
# Route is: slider → link-out cmd:Qd → process tab link-in → PS.
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ===== SECTION: Mode + Station Buttons =====
y = 320
nodes.append(comment("c_ui_station", TAB_UI, LANE_X[2], y,
"── Mode + Station-wide buttons ──", ""))
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
# Mode toggle now drives the PUMPING STATION mode (levelbased ↔ manual)
# instead of per-pump setMode. In levelbased mode, PS drives pumps
# automatically. In manual mode, the demand slider takes over.
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(ui_switch(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"ui_mode_toggle", TAB_UI, LANE_X[0], y + 40, g_station,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"Station mode",
"Station mode (Auto = level-based control · Manual = slider demand)",
on_value="levelbased", off_value="manual", topic="changemode",
wires=["lout_ps_mode_dash"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_out(
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"lout_ps_mode_dash", TAB_UI, LANE_X[1], y + 40,
"cmd:ps-mode", target_in_ids=["lin_ps_mode_at_ps"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
for k, (text, payload, color, icon, lout_id, channel) in enumerate([
("Start all pumps", '{"topic":"execSequence","payload":{"source":"GUI","action":"execSequence","parameter":"startup"}}',
"#16a34a", "play_arrow", "lout_cmd_station_startup_dash", CH_STATION_START),
("Stop all pumps", '{"topic":"execSequence","payload":{"source":"GUI","action":"execSequence","parameter":"shutdown"}}',
"#ea580c", "stop", "lout_cmd_station_shutdown_dash", CH_STATION_STOP),
("EMERGENCY STOP", '{"topic":"emergencystop","payload":{"source":"GUI","action":"emergencystop"}}',
"#dc2626", "stop_circle", "lout_cmd_station_estop_dash", CH_STATION_ESTOP),
]):
yk = y + 100 + k * 60
# The ui-button payload becomes msg.payload; we want the button to send
# a fully-formed {topic, payload} for the per-pump nodeClass to dispatch.
# ui-button can't set msg.topic from a constant payload that's an
# object directly — easier path: a small function in front of the
# link-out that wraps the button's plain payload string into the
# right shape per channel.
btn_id = f"btn_station_{k}"
wrap_id = f"wrap_station_{k}"
# Use simple payload (just the button text) and let the wrapper build
# the real msg shape.
if k == 0: # startup
wrap_code = (
"msg.topic = 'execSequence';\n"
"msg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\n"
"return msg;"
)
elif k == 1: # shutdown
wrap_code = (
"msg.topic = 'execSequence';\n"
"msg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\n"
"return msg;"
)
else: # estop
wrap_code = (
"msg.topic = 'emergencystop';\n"
"msg.payload = { source:'GUI', action:'emergencystop' };\n"
"return msg;"
)
nodes.append(ui_button(
btn_id, TAB_UI, LANE_X[0], yk, g_station,
text, text, "fired", "str",
topic=f"station_{k}", color=color, icon=icon,
wires=[wrap_id]
))
nodes.append(function_node(
wrap_id, TAB_UI, LANE_X[1] + 100, yk, f"build cmd ({text})",
wrap_code, outputs=1, wires=[[lout_id]]
))
nodes.append(link_out(
lout_id, TAB_UI, LANE_X[2], yk,
channel,
target_in_ids=[{
CH_STATION_START: "lin_station_start",
CH_STATION_STOP: "lin_station_stop",
CH_STATION_ESTOP: "lin_station_estop",
}[channel]]
))
# ===== SECTION: MGC + PS overview =====
y = 600
nodes.append(comment("c_ui_mgc_ps", TAB_UI, LANE_X[2], y,
"── MGC + Basin overview ──", ""))
nodes.append(link_in(
"lin_evt_mgc_dash", TAB_UI, LANE_X[0], y + 40,
CH_MGC_EVT, source_out_ids=["lout_evt_mgc"],
downstream=["dispatch_mgc"]
))
nodes.append(function_node(
"dispatch_mgc", TAB_UI, LANE_X[1], y + 40,
"dispatch MGC",
"const p = msg.payload || {};\n"
"return [\n"
" {payload: String(p.totalFlow || 'n/a')},\n"
" {payload: String(p.totalPower || 'n/a')},\n"
" {payload: String(p.efficiency || 'n/a')},\n"
"];",
outputs=3,
wires=[["ui_mgc_total_flow"], ["ui_mgc_total_power"], ["ui_mgc_eff"]],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(ui_text("ui_mgc_total_flow", TAB_UI, LANE_X[2], y + 40, g_mgc,
"MGC total flow", "Total flow", "{{msg.payload}}"))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(ui_text("ui_mgc_total_power", TAB_UI, LANE_X[2], y + 70, g_mgc,
"MGC total power", "Total power", "{{msg.payload}}"))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(ui_text("ui_mgc_eff", TAB_UI, LANE_X[2], y + 100, g_mgc,
"MGC efficiency", "Group efficiency", "{{msg.payload}}"))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_in(
"lin_evt_ps_dash", TAB_UI, LANE_X[0], y + 160,
CH_PS_EVT, source_out_ids=["lout_evt_ps"],
downstream=["dispatch_ps"]
))
# PS dispatcher: 10 outputs — 7 text fields + 3 trend numerics
nodes.append(function_node(
"dispatch_ps", TAB_UI, LANE_X[1], y + 160,
"dispatch PS",
"const p = msg.payload || {};\n"
"const ts = Date.now();\n"
"return [\n"
" {payload: String(p.direction || 'steady')},\n"
" {payload: String(p.level || 'n/a')},\n"
" {payload: String(p.volume || 'n/a')},\n"
" {payload: String(p.fillPct || 'n/a')},\n"
" {payload: String(p.netFlow || 'n/a')},\n"
" {payload: String(p.timeLeft || 'n/a')},\n"
" {payload: String(p.qIn || 'n/a')},\n"
" // Trend numerics\n"
" p.fillPctNum != null ? {topic: 'Basin fill', payload: p.fillPctNum, timestamp: ts} : null,\n"
" p.levelNum != null ? {topic: 'Basin level', payload: p.levelNum, timestamp: ts} : null,\n"
" p.netFlowNum != null ? {topic: 'Net flow', payload: p.netFlowNum,timestamp: ts} : null,\n"
"];",
outputs=10,
wires=[
["ui_ps_direction"],
["ui_ps_level"],
["ui_ps_volume"],
["ui_ps_fill"],
["ui_ps_netflow"],
["ui_ps_timeleft"],
["ui_ps_qin"],
# 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)
],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
# (Basin gauges live on the trend pages, not the control page —
# see the trend section below for gauge_ps_level / gauge_ps_fill.)
# PS text widgets
nodes.append(ui_text("ui_ps_direction", TAB_UI, LANE_X[2], y + 160, g_ps,
"PS direction", "Direction", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_level", TAB_UI, LANE_X[2], y + 200, g_ps,
"PS level", "Basin level", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_volume", TAB_UI, LANE_X[2], y + 240, g_ps,
"PS volume", "Basin volume", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_fill", TAB_UI, LANE_X[2], y + 280, g_ps,
"PS fill %", "Fill level", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_netflow", TAB_UI, LANE_X[2], y + 320, g_ps,
"PS net flow", "Net flow", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_timeleft", TAB_UI, LANE_X[2], y + 360, g_ps,
"PS time left", "Time to full/empty", "{{msg.payload}}"))
nodes.append(ui_text("ui_ps_qin", TAB_UI, LANE_X[2], y + 400, g_ps,
"PS Qin", "Inflow", "{{msg.payload}}"))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ===== SECTION: Per-pump panels =====
y_pumps_start = 1000
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
for i, pump in enumerate(PUMPS):
label = PUMP_LABELS[pump]
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
g = {"pump_a": g_pump_a, "pump_b": g_pump_b, "pump_c": g_pump_c}[pump]
y_p = y_pumps_start + i * SECTION_GAP * 2
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(comment(f"c_ui_{pump}", TAB_UI, LANE_X[2], y_p,
f"── {label} ──", ""))
# link-in: one fat object per pump → dispatcher splits into
# plain-string payloads per ui-text widget + numeric payloads
# for trend charts. 9 outputs total.
DISPLAY_FIELDS = [
("State", "state"),
("Mode", "mode"),
("Controller %", "ctrl"),
("Flow", "flow"),
("Power", "power"),
("p Upstream", "pUp"),
("p Downstream", "pDn"),
]
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_in(
f"lin_evt_{pump}_dash", TAB_UI, LANE_X[0], y_p + 40,
CH_PUMP_EVT[pump], source_out_ids=[f"lout_evt_{pump}"],
downstream=[f"dispatch_{pump}"],
))
# Dispatcher: takes the fat object and returns 9 outputs, each
# with a plain payload ready for a ui-text or trend chart.
nodes.append(function_node(
f"dispatch_{pump}", TAB_UI, LANE_X[1], y_p + 40,
f"dispatch {label}",
"const p = msg.payload || {};\n"
"const ts = Date.now();\n"
"return [\n"
" {payload: String(p.state || 'idle')},\n"
" {payload: String(p.mode || 'auto')},\n"
" {payload: String(p.ctrl || 'n/a')},\n"
" {payload: String(p.flow || 'n/a')},\n"
" {payload: String(p.power || 'n/a')},\n"
" {payload: String(p.pUp || 'n/a')},\n"
" {payload: String(p.pDn || 'n/a')},\n"
" p.flowNum != null ? {topic: '" + label + "', payload: p.flowNum, timestamp: ts} : null,\n"
" p.powerNum != null ? {topic: '" + label + "', payload: p.powerNum, timestamp: ts} : null,\n"
"];",
outputs=9,
wires=[
[f"ui_{pump}_{f}"] for _, f in DISPLAY_FIELDS
] + [
["trend_short_flow", "trend_long_flow"], # output 7: flowNum → both flow charts
["trend_short_power", "trend_long_power"], # output 8: powerNum → both power charts
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
],
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
# ui-text widgets
for k, (label_txt, field) in enumerate(DISPLAY_FIELDS):
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(ui_text(
f"ui_{pump}_{field}", TAB_UI, LANE_X[2], y_p + 40 + k * 40, g,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"{label} {label_txt}", label_txt,
"{{msg.payload}}" # plain string — FlowFuse-safe
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
# Setpoint slider → wrapper → link-out → process pump (cmd:setpoint-X)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(ui_slider(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"ui_{pump}_setpoint", TAB_UI, LANE_X[0], y_p + 280, g,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
f"{label} setpoint", "Setpoint % (manual mode)",
0, 100, 5.0, f"setpoint_{pump}",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
wires=[f"lout_setpoint_{pump}_dash"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_out(
f"lout_setpoint_{pump}_dash", TAB_UI, LANE_X[1], y_p + 280,
CH_PUMP_SETPOINT[pump],
target_in_ids=[f"lin_setpoint_{pump}"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# Per-pump start/stop buttons → link-out
# We need wrappers because ui-button payload must be string-typed.
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(ui_button(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"btn_{pump}_start", TAB_UI, LANE_X[0], y_p + 330, g,
f"{label} startup", "Startup", "fired", "str",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
topic=f"start_{pump}", color="#16a34a", icon="play_arrow",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
wires=[f"wrap_{pump}_start"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
nodes.append(function_node(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"wrap_{pump}_start", TAB_UI, LANE_X[1] + 100, y_p + 330,
f"build start ({label})",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"msg.topic = 'execSequence';\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"msg.payload = { source:'GUI', action:'execSequence', parameter:'startup' };\n"
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"return msg;",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
outputs=1, wires=[[f"lout_seq_{pump}_dash"]]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
nodes.append(ui_button(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"btn_{pump}_stop", TAB_UI, LANE_X[0], y_p + 380, g,
f"{label} shutdown", "Shutdown", "fired", "str",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
topic=f"stop_{pump}", color="#ea580c", icon="stop",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
wires=[f"wrap_{pump}_stop"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
nodes.append(function_node(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
f"wrap_{pump}_stop", TAB_UI, LANE_X[1] + 100, y_p + 380,
f"build stop ({label})",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"msg.topic = 'execSequence';\n"
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"msg.payload = { source:'GUI', action:'execSequence', parameter:'shutdown' };\n"
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
"return msg;",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
outputs=1, wires=[[f"lout_seq_{pump}_dash"]]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# Both start and stop wrappers feed one shared link-out
nodes.append(link_out(
f"lout_seq_{pump}_dash", TAB_UI, LANE_X[2], y_p + 355,
CH_PUMP_SEQUENCE[pump],
target_in_ids=[f"lin_seq_{pump}"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
# (Trend feed is handled by dispatcher outputs 7+8 above — no separate
# trend_split function needed.)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
# ===== Trend charts — two pages, two charts per page =====
# Short-term (10 min rolling window) and long-term (1 hour).
# Same data feed; different removeOlder settings.
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
y_charts = y_pumps_start + len(PUMPS) * SECTION_GAP * 2 + 80
nodes.append(comment("c_ui_trends", TAB_UI, LANE_X[2], y_charts,
"── Trend charts ── (feed to 4 charts on 2 pages)",
"Short-term (10 min) and long-term (1 h) trends share the same feed.\n"
"Each chart on its own page."
))
# Short-term (10 min)
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",
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",
width=12, height=8, y_axis_label="kW",
remove_older="10", remove_older_unit="60", remove_older_points="300",
order=1,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
# Long-term (1 hour)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
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",
width=12, height=8, y_axis_label="m³/h",
remove_older="60", remove_older_unit="60", remove_older_points="1800",
order=1,
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
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",
width=12, height=8, y_axis_label="kW",
remove_older="60", remove_older_unit="60", remove_older_points="1800",
order=1,
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
# ===== Basin charts + gauges (fill %, level, net flow) =====
# Gauge segment definitions (reused for both pages)
TANK_SEGMENTS = [
{"color": "#f44336", "from": 0}, # red: below stopLevel (1.0 m)
{"color": "#ff9800", "from": 1.0}, # orange: between stop and start
{"color": "#2196f3", "from": 2.0}, # blue: normal operating (startLevel)
{"color": "#ff9800", "from": 3.5}, # orange: approaching overflow
{"color": "#f44336", "from": 3.8}, # red: overflow zone (heightOverflow)
]
FILL_SEGMENTS = [
{"color": "#f44336", "from": 0},
{"color": "#ff9800", "from": 10},
{"color": "#4caf50", "from": 30},
{"color": "#ff9800", "from": 80},
{"color": "#f44336", "from": 95},
]
for suffix, remove_older, remove_points, y_off in [
("short", "10", "300", 360),
("long", "60", "1800", 540),
]:
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}_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,
order=1,
))
# 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_level,
"name": f"Basin level gauge ({suffix})",
"gtype": "gauge-tank", "gstyle": "Rounded",
"title": "Level", "units": "m",
"prefix": "", "suffix": " m",
"min": 0, "max": 4,
"segments": TANK_SEGMENTS,
"width": 2, "height": 5, "order": 2,
"icon": "", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10,
"x": LANE_X[4], "y": y_charts + y_off, "wires": [],
})
# 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_fill,
"name": f"Basin fill gauge ({suffix})",
"gtype": "gauge-34", "gstyle": "Rounded",
"title": "Fill", "units": "%",
"prefix": "", "suffix": "%",
"min": 0, "max": 100,
"segments": FILL_SEGMENTS,
"width": 2, "height": 4, "order": 3,
"icon": "water_drop", "sizeGauge": 20, "sizeGap": 2, "sizeSegments": 10,
"x": LANE_X[5], "y": y_charts + y_off, "wires": [],
})
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
return nodes
# ---------------------------------------------------------------------------
# Tab 3 — DEMO DRIVERS
# ---------------------------------------------------------------------------
def build_drivers_tab():
nodes = []
nodes.append({
"id": TAB_DRIVERS, "type": "tab",
"label": "🎛️ Demo Drivers",
"disabled": False,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"info": "Simulated inflow for the demo. A slow sinusoid generates "
"inflow into the pumping station basin, which then drives "
"the level-based pump control automatically.\n\n"
"In production, delete this tab — real inflow comes from "
"upstream measurement sensors.",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
})
nodes.append(comment("c_drv_title", TAB_DRIVERS, LANE_X[2], 20,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"🎛️ DEMO DRIVERS — simulated basin inflow",
"Sinus generator → q_in to pumpingStation. Basin fills → level-based\n"
"control starts pumps → basin drains → pumps stop → cycle repeats."
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
# Sinus inflow generator: produces a flow value (m³/s) that
# simulates incoming wastewater. Period ~120s so the fill/drain
# cycle is visible on the dashboard. Amplitude scaled so 3 pumps
# can handle the peak.
# Q_in = base + amplitude * (1 + sin(2π t / period)) / 2
# base = 0.005 m³/s (~18 m³/h) — always some inflow
# amplitude = 0.03 m³/s (~108 m³/h peak)
# period = 120 s
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
y = 100
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
nodes.append(comment("c_drv_sinus", TAB_DRIVERS, LANE_X[2], y,
"── Sinusoidal inflow generator ──",
"Produces a smooth inflow curve (m³/s) and sends to pumpingStation\n"
"via the cmd:q_in link channel. Period = 120s."
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(inject(
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"sinus_tick", TAB_DRIVERS, LANE_X[0], y + 40,
"tick (1s inflow)",
topic="sinusTick", payload="", payload_type="date",
repeat="1", wires=["sinus_fn"]
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
nodes.append(function_node(
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"sinus_fn", TAB_DRIVERS, LANE_X[1] + 220, y + 40,
"sinus inflow (m³/s)",
"// Realistic wastewater inflow profile:\n"
"// base = minimum dry-weather flow (always present)\n"
"// amplitude = peak wet-weather swing on top of base\n"
"// range = base → base+amplitude = 54 → 270 m³/h\n"
"// 1 pump handles up to ~223 m³/h, so peak needs 2 pumps.\n"
"// 3 pumps (669 m³/h) are never needed = realistic headroom.\n"
"// period = 240s (4 min) — slow enough to see pump ramp on dash.\n"
"const base = 0.015; // m³/s (~54 m³/h dry weather)\n"
"const amplitude = 0.06; // m³/s (~216 m³/h peak swing)\n"
"const period = 240; // seconds per full cycle\n"
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"const t = Date.now() / 1000; // seconds since epoch\n"
"const q = base + amplitude * (1 + Math.sin(2 * Math.PI * t / period)) / 2;\n"
"return { topic: 'q_in', payload: q, unit: 'm3/s', timestamp: Date.now() };",
outputs=1, wires=[["lout_qin_drivers"]]
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
nodes.append(link_out(
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"lout_qin_drivers", TAB_DRIVERS, LANE_X[3], y + 40,
"cmd:q_in", target_in_ids=["lin_qin_at_ps"]
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
))
return nodes
# ---------------------------------------------------------------------------
# Tab 4 — SETUP & INIT
# ---------------------------------------------------------------------------
def build_setup_tab():
nodes = []
nodes.append({
"id": TAB_SETUP, "type": "tab",
"label": "⚙️ Setup & Init",
"disabled": False,
"info": "One-shot deploy-time injects. Sets MGC scaling/mode, broadcasts "
"pumps mode = auto, and auto-starts the pumps + random demand.",
})
nodes.append(comment("c_setup_title", TAB_SETUP, LANE_X[2], 20,
"⚙️ SETUP & INIT — one-shot deploy-time injects",
"Disable this tab in production — the runtime should be persistent."
))
# Setup wires DIRECTLY to the process nodes (cross-tab via link is cleaner
# but for one-shot setups direct wiring keeps the intent obvious).
y = 100
nodes.append(inject(
"setup_mgc_scaling", TAB_SETUP, LANE_X[0], y,
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
"MGC scaling = normalized",
topic="setScaling", payload="normalized", payload_type="str",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
once=True, once_delay="1.5",
wires=["lout_setup_to_mgc"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
nodes.append(inject(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"setup_mgc_mode", TAB_SETUP, LANE_X[0], y + 60,
"MGC mode = optimalcontrol",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
topic="setMode", payload="optimalcontrol", payload_type="str",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
once=True, once_delay="1.7",
wires=["lout_setup_to_mgc"]
))
nodes.append(link_out(
"lout_setup_to_mgc", TAB_SETUP, LANE_X[1], y + 30,
"setup:to-mgc", target_in_ids=["lin_setup_at_mgc"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
y = 250
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(inject(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"setup_pumps_mode", TAB_SETUP, LANE_X[0], y,
"pumps mode = auto",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
topic="setMode", payload="auto", payload_type="str",
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
once=True, once_delay="2.0",
wires=["lout_mode_setup"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_out(
"lout_mode_setup", TAB_SETUP, LANE_X[1], y,
CH_MODE, target_in_ids=["lin_mode"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
y = 350
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
nodes.append(inject(
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
"setup_pumps_startup", TAB_SETUP, LANE_X[0], y,
"auto-startup all pumps",
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
topic="execSequence",
payload='{"source":"GUI","action":"execSequence","parameter":"startup"}',
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
payload_type="json", once=True, once_delay="4",
wires=["lout_setup_station_start"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
nodes.append(link_out(
"lout_setup_station_start", TAB_SETUP, LANE_X[1], y,
CH_STATION_START, target_in_ids=["lin_station_start"]
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
))
wip: sinus-driven pumping station demo + PS levelbased control to MGC Architecture change: demo is now driven by a sinusoidal inflow into the pumping station basin, rather than a random demand generator. The basin fills from the sinus, and PS's levelbased control should start/stop pumps via MGC when level crosses start/stop thresholds. Changes: - Demo Drivers tab: sinus generator (period 120s, base 0.005 + amp 0.03 m³/s) replaces the random demand. Sends q_in to PS via link channel. - PS config: levelbased mode, 10 m³ basin, startLevel 1.2 m / stopLevel 0.6 m. Volume-based safeties on, time-based off. - MGC scaling = normalized (was absolute) so PS's percent-based level control maps correctly. - Dashboard mode toggle now drives PS mode (levelbased ↔ manual) instead of per-pump setMode. Slider sends Qd to PS (only effective in manual). - PS code (committed separately): _controlLevelBased now calls _applyMachineGroupLevelControl + new Qd topic + forwardDemandToChildren. KNOWN ISSUE: Basin fills correctly (visible on dashboard), but pumps don't start when level exceeds startLevel. Likely cause: _pickVariant for 'level' in _controlLevelBased may not be resolving the predicted level correctly, or the safetyController is interfering despite time-threshold being 0. Needs source-level tracing of the PS tick → _safetyController → _controlLogic → _controlLevelBased path with logging enabled. To be debugged in the next session. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 08:42:22 +02:00
# (Random demand removed — sinus inflow drives the demo automatically.
# No explicit "random on" inject needed.)
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
return nodes
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
# ---------------------------------------------------------------------------
# Process tab additions: setup link-in feeding MGC
# ---------------------------------------------------------------------------
def add_setup_link_to_process(process_nodes):
"""Inject a link-in on the process tab that funnels setup msgs to MGC."""
y = 100 + 7 * SECTION_GAP
process_nodes.append(comment(
"c_setup_at_mgc", TAB_PROCESS, LANE_X[2], y,
"── Setup feeders ──",
"Cross-tab link from Setup tab → MGC scaling/mode init."
))
process_nodes.append(link_in(
"lin_setup_at_mgc", TAB_PROCESS, LANE_X[0], y + 60,
"setup:to-mgc",
source_out_ids=["lout_setup_to_mgc"],
downstream=[MGC_ID]
))
# ---------------------------------------------------------------------------
# Assemble + emit
# ---------------------------------------------------------------------------
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
def main():
refactor(examples): split pumpingstation demo across 4 concern-based tabs + add layout rule set The demo was a single 96-node tab with everything wired directly. Now 4 tabs wired only through named link-out / link-in pairs, and a permanent rule set for future Claude sessions to follow. Tabs (by concern, not by data flow): 🏭 Process Plant only EVOLV nodes (3 pumps + MGC + PS + 6 measurements) + per-node output formatters 📊 Dashboard UI only ui-* widgets, button/setpoint wrappers, trend splitters 🎛️ Demo Drivers random demand generator + state holder. Removable in production ⚙️ Setup & Init one-shot deploy-time injects (mode, scaling, auto-startup, random-on) Cross-tab wiring uses a fixed named-channel contract (cmd:demand, cmd:mode, cmd:setpoint-A, evt:pump-A, etc.) — multiple emitters can target a single link-in for fan-in, e.g. both the slider and the random generator feed cmd:demand. Bug fixes folded in: 1. Trend chart was empty / scrambled. Root cause: the trend-feeder function had ONE output that wired to BOTH flow and power charts, so each chart received both flow and power msgs and the legend garbled. Now: 2 outputs (flow → flow chart, power → power chart), one msg per output. 2. Every ui-text and ui-chart fell on the (0, 0) corner of the editor canvas. Root cause: the helper functions accepted x/y parameters but never assigned them on the returned node dict — Node-RED defaulted every widget to (0, 0) and they piled on top of each other. The dashboard render was unaffected (it lays out by group/ order), but the editor was unreadable. Fixed both helpers and added a verification step ("no node should be at (0, 0)") to the rule set. Spacing convention (now codified): - 6 lanes per tab at x = [120, 380, 640, 900, 1160, 1420] - 80 px standard row pitch, 30-40 px for tight ui-text stacks - 200 px gap between sections, with a comment header per section New rule set: .claude/rules/node-red-flow-layout.md - Tab boundaries by concern - Link-channel naming convention (cmd:/evt:/setup: prefixes) - Spacing constants - Trend-split chart pattern - Inject node payload typing pitfall (per-prop v/vt) - Dashboard widget rules (every ui-* needs x/y!) - Do/don't checklist - Link-out/link-in JSON cheat sheet - 5-step layout verification before declaring a flow done CLAUDE.md updated to point at the new rule set. Verified end-to-end on Dockerized Node-RED 2026-04-13: 168 nodes across 4 tabs, all wired via 22 link-out / 19 link-in pairs, no nodes at (0, 0), pumps reach operational ~5 s after deploy, MGC distributes random demand, trends populate per pump. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:13:27 +02:00
process_nodes = build_process_tab()
add_setup_link_to_process(process_nodes)
nodes = process_nodes + build_ui_tab() + build_drivers_tab() + build_setup_tab()
feat(examples): pumpingstation-3pumps-dashboard end-to-end demo + bump generalFunctions New top-level examples/ folder for end-to-end demos that show how multiple EVOLV nodes work together (complementing the per-node example flows under nodes/<name>/examples/). Future end-to-end demos will live as siblings. First demo: pumpingstation-3pumps-dashboard - 1 pumpingStation (basin model, manual mode for the demo so it observes rather than auto-shutting pumps; safety guards disabled — see README) - 1 machineGroupControl (optimalcontrol mode, absolute scaling) - 3 rotatingMachine pumps (hidrostal-H05K-S03R curve) - 6 measurement nodes (per pump: upstream + downstream pressure mbar, simulator mode for continuous activity) - Process demand input via dashboard slider (0-300 m3/h) AND auto random generator (3s tick, [40, 240] m3/h) — both feed PS q_in + MGC Qd - Auto/Manual mode toggle (broadcasts setMode to all 3 pumps) - Station-wide Start / Stop / Emergency-Stop buttons - Per-pump setpoint slider, individual buttons, full status text - Two trend charts (flow per pump, power per pump) - FlowFuse dashboard at /dashboard/pumping-station-demo build_flow.py is the source of truth — it generates flow.json deterministically and is the right place to extend the demo. Bumps: nodes/generalFunctions 43f6906 -> 29b78a3 Fix: childRegistrationUtils now aliases the production softwareType values (rotatingmachine, machinegroupcontrol) to the dispatch keys parent nodes check for (machine, machinegroup). Without this, MGC <-> rotatingMachine and pumpingStation <-> MGC wiring silently never matched in production even though tests passed. Demo confirms: MGC reports '3 machine(s) connected'. Verified end-to-end on Dockerized Node-RED 2026-04-13: pumps reach operational ~5s after deploy, MGC distributes random demand across them, basin tracks net flow direction, all dashboard widgets update each second. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 15:53:47 +02:00
json.dump(nodes, sys.stdout, indent=2)
sys.stdout.write("\n")
if __name__ == "__main__":
main()