Charts rendered blank because the helper was missing 15+ required
FlowFuse properties. The critical three:
- interpolation: "linear" (no line drawn without it)
- yAxisProperty: "payload" + yAxisPropertyType: "msg" (chart didn't
know which msg field to plot)
- xAxisPropertyType: "timestamp" (chart didn't know the x source)
Also: width/height must be numbers not strings, colors/textColor/
gridColor arrays must be present, and stackSeries/bins/xAxisFormat/
xAxisFormatType all need explicit values.
Fixed the ui_chart helper to include every property from the working
rotatingMachine/examples/03-Dashboard.json charts. Added the full
required-property template + gotcha list to the flow-layout rule set
(Section 4) so this class of bug is caught by reference on the next
chart build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FlowFuse ui-chart with xAxisType=time may need an explicit timestamp
on each msg for the time axis to render. Added Date.now() as
msg.timestamp on the per-pump dispatcher flow/power outputs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FlowFuse ui-text only supports {{msg.payload}} — not nested paths
like {{msg.payload.state}}. Every ui-text was showing [object Object]
because the formatter sent a fat object as msg.payload and the format
template tried to access sub-fields.
Fix: per-pump (and per-MGC, per-PS) "dispatcher" function on the
Dashboard UI tab. The dispatcher receives the fat object via one
link-in, then returns 7-9 plain-string outputs — one per ui-text
widget — each with msg.payload set to the formatted string value.
Outputs 8+9 carry numeric values (flowNum/powerNum) tagged with
msg.topic for the trend charts, wired directly to both short-term
and long-term chart nodes.
Pattern documented as the recommended approach in the rule set:
"FlowFuse ui-text receives plain strings only — use a dispatcher
function to split a fat object into per-widget outputs."
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each node repo now has a CLAUDE.md that declares its S88 hierarchy
level (Control Module / Equipment Module / Unit / Process Cell), the
associated S88 colour, and the placement lane per the superproject's
flow-layout rule set (.claude/rules/node-red-flow-layout.md).
The rule set lives in the superproject only (single source of truth).
Per-node repos reference it. When Claude Code opens a node repo, it
reads the local CLAUDE.md and knows which lane / colour / group to
use when building a multi-node demo or production flow.
Submodule pointer bumps for all 11 nodes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboard was a single page — 30+ widgets + tiny charts competing for
space. Trends were invisible or very small (width/height both "0"
meant "inherit from group" which gave near-zero chart area).
Split into 3 dashboard pages:
1. Control — Process Demand, Station Controls, MGC/Basin status,
per-pump panels (unchanged, just moved off trend groups)
2. Trends — 10 min — rolling 10-minute flow + power charts with
width=12 (full group), height=8 (tall charts), 300 max points
3. Trends — 1 hour — same layout with 60-minute window, 1800 points
All 3 pages auto-nav via the FlowFuse sidebar. Same data feed: the
per-pump trend_split function now wires to 4 charts (2 outputs × 2
pages) instead of 2.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sections 10-16 extend the existing flow-layout rule with a deterministic
lane-and-group convention anchored in the S88 hierarchy:
- 8 logical lanes: L0 inputs -> L1 adapters -> L2 CM -> L3 EM -> L4 UN
-> L5 PC -> L6 formatters -> L7 outputs. 240 px between lanes.
- Lane assignment is by S88 level, not by node name. New nodes inherit
a lane via a NODE_LEVEL registry, no rule change needed.
- Every parent + its direct children is wrapped in a Node-RED group box
coloured by the parent's S88 level (Pump A = EM blue, MGC = Unit blue,
PS = Process Cell blue, ...). Search the parent's name -> group
highlights.
- Utility clusters (mode broadcast, station-wide commands, demand
fan-out) use neutral-grey group boxes.
- Dashboard / setup / demo-driver tabs each get a variant of the rule.
- Spacing constants, place() and wrap_in_group() helpers, an 8-step
verification checklist.
Off-spec colours (settler orange, monster teal, diffuser and
dashboardAPI missing) are flagged in Section 16 as a follow-up cleanup.
The NODE_LEVEL registry already maps those nodes to their semantic S88
level regardless of what the node's own colour currently says.
Rule lives in the superproject only; per-node repos will reference it
from their own CLAUDE.md files (separate commits per submodule).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
generalFunctions: e50be2e -> 43f6906
Fixes the bug where picking a supplier and then a type left the model
dropdown stuck on "Awaiting Type Selection". Affects every node that
uses the shared assetMenu (measurement, rotatingMachine, pumpingStation,
monster, …). The chained dropdowns now use an explicit downward
cascade with no synthetic change-event dispatch, so the parent handler
can no longer wipe a child after the child was populated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
measurement: d6f8af4 -> <new>
Fixes a regression in the previous measurement editor commit where a
const Temporal Dead Zone error in oneditprepare aborted the function
before the asset / logger / position menu init ran. Menus are now
kicked off first, mode logic is guarded with try/catch and null-checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
measurement: 495b4cf -> d6f8af4
Makes Input Mode the top-level hierarchy in the editor: analog-only and
digital-only field blocks toggle visibility live based on the dropdown,
legacy nodes default to 'analog', channels JSON gets live validation,
and runtime logs an actionable warning when the payload shape doesn't
match the selected mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps:
- nodes/generalFunctions 75d16c6 -> e50be2e (permissive unit check + measurement schema additions)
- nodes/measurement f7c3dc2 -> 495b4cf (digital mode + dispatcher fix + 59 new tests + rewritten README + UI)
Wiki:
- wiki/manuals/nodes/measurement.md — new user manual covering analog and
digital modes, topic reference, smoothing/outlier methods, unit policy,
and the pre-fix dispatcher bug advisory.
- wiki/sessions/2026-04-13-measurement-digital-mode.md — session note with
findings, fix scope, test additions, and dual-mode E2E results.
- wiki/index.md — links both pages and adds the missing 2026-04-13
rotatingMachine session entry that was omitted from the earlier commit.
Status: measurement is now trial-ready in both analog and digital modes.
71/71 unit tests green (was 12), dual-mode E2E on live Dockerized
Node-RED verifies analog regression and a three-channel MQTT-style
payload (temperature/humidity/pressure) dispatching independently with
per-channel smoothing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps:
- nodes/generalFunctions 024db55 -> 75d16c6 (FSM abort recovery + schema sync)
- nodes/rotatingMachine 07af7ce -> 17b8887 (interruptible sequences, dual-curve tests, rewritten README)
Wiki:
- wiki/manuals/nodes/rotatingMachine.md — new user manual covering inputs,
outputs, state machine, supported curves, and troubleshooting.
- wiki/sessions/2026-04-13-rotatingMachine-trial-ready.md — session note
with findings, fixes, test additions, and dual-curve E2E results.
- wiki/index.md — link both and bump updated date.
Status: rotatingMachine is now trial-ready. 91/91 unit tests green, live
Docker E2E verifies shutdown/emergency-stop during ramps and prediction
behaviour across both shipped pump curves (hidrostal-H05K-S03R,
hidrostal-C5-D03R-SHN1).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Makefile: all useful targets duplicate package.json scripts, and
referenced deleted e2e files. Use npm run instead.
- .npmignore: contained only node_modules/ which npm ignores by default.
- .dockerignore: remove stale paths (manuals/, third_party/, AGENTS.md,
FUNCTIONAL_ISSUES_BACKLOG.md), add wiki/.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
12 pages covering architecture, findings, and metrics from the
rotatingMachine + machineGroupControl hardening work:
- Overview: node inventory, what works/doesn't, current scale
- Architecture: 3D pump curves, group optimization algorithm
- Findings: BEP-Gravitation proof (0.1% of optimum), NCog behavior,
curve non-convexity, pump switching stability
- Metrics: test counts, power comparison table, performance numbers
- Knowledge graph: structured YAML with all data points and provenance
- Session log: 2026-04-07 production hardening
- Tools: query.py, search.sh, lint.sh
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rotatingMachine:
- Safety fixes: async input handler, emergencyStop case fix, null guards,
listener cleanup, tick loop race condition, editor timeout
- Prediction: remove efficiency rounding (was breaking NCog/BEP), fix
variant reads, curve anomaly detection
- 43 new tests (76 total)
machineGroupControl:
- Critical: fix flowmovement unit mismatch (m³/s sent where m³/h expected,
pumps never moved from minimum)
- Fix absolute scaling comparison bug, empty Qd block, empty-machines guards
- Add marginal-cost refinement loop: reduces gap to brute-force optimum
from 2.1% to <0.1%
- 2 new test files with NCog distribution and power comparison tests
generalFunctions:
- Fix 3 anomalous power values in hidrostal-H05K-S03R curve data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- generalFunctions: fix infinite recursion in validateSchema when version string is in schema
- rotatingMachine: fix missing closing brace in emergencystop case block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tracked issues for diffuser restoration, ML module relocation,
monster architecture modernization, test code cleanup, and dashboardAPI improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Dockerfile.e2e to install EVOLV properly in Node-RED /data/
- Add measurement node E2E test flow with scaling (4-20mA to 0-5m)
- Add Grafana health check to run-e2e.sh
- Guard pumpingStation demo IIFE with require.main check
- All 10 EVOLV nodes load successfully in containerized Node-RED
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update all submodule URLs from gitea.centraal.wbd-rd.nl to gitea.wbd-rd.nl
- Add settler as proper submodule in .gitmodules
- Add agent skills, function anchors, decisions, and improvements
- Add Docker configuration and scripts
- Add manuals and third_party docs
- Update .gitignore with secrets and build artifacts
- Remove stale .tgz build artifact
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>