Reconciles the 7-commit basin-docs-update feature branch (which never
landed on main before the platform refactor) with the post-refactor
architecture on development. Each basin-docs feature ported into the
relevant concern module:
control/levelBased.js
- stopLevel Schmitt-trigger + dead-band keep-alive
- Shifted ramp (arm % → hold @ 100% → ramp down to shiftLevel)
- Linear vs log up-curve (curveType + logCurveFactor)
measurement/flowAggregator.js
- Predicted-volume overflow clamp + spill flow stream
- Cumulative overflowVolume + underflowVolume
- Hard floor at 0 + dry-run-on-transition handling
basin/thresholdValidator.js
- computeSafetyPoints exposes dryRunLevel + highVolumeSafetyLevel
- startLevel ≤ inflowLevel invariant added
measurement/calibration.js + commands/
- Manual q_out path (set.outflow / q_out alias)
safety/safetyController.js
- Accepts both legacy + new high-volume threshold names
UI:
pumpingStation.html — restored the side-panel + SVG mode-preview block,
added defaults for stopLevel/shiftLevel/shiftArmPercent/levelCurveType/
logCurveFactor/enableShiftedRamp.
src/editor/* — basin-docs' 7-file modular editor (replaces single
src/editor.js, which is deleted).
pumpingStation.js — admin endpoint serves editor/:file.
Tests: 130/130 pass (125 basic + 5 integration). Two basin-docs test
files added: nodeClass-config.test.js, basic-dashboard-flow.test.js,
shifted-ramp-end-to-end.test.js. One pre-refactor control-levelBased
test adapted to match basin-docs canonical "no-shutdown in dead zone"
behaviour.
Human-review items (see commit context):
- rampFoot = inflowLevel (matches basin-docs test); basin-docs source
used rampFoot = startLevel. Domain owner: confirm intent.
- Naming kept dual (overfillLevel + highVolumeSafetyLevel).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pumpingStation - Example Flows
Three Node-RED flows demonstrating the Phase-2 pumpingStation node on the
canonical topic API (set.mode, set.inflow, set.demand,
cmd.calibrate.volume, cmd.calibrate.level). Legacy aliases
(changemode, q_in, Qd, calibratePredictedVolume,
calibratePredictedLevel, registerChild) still work but log a
one-time deprecation warning; these fresh flows use the canonical names only.
Files
| File | Tier | Tabs | Purpose |
|---|---|---|---|
01-Basic.json |
1 | Process Plant | Single pumpingStation driven by inject nodes - no parent, no dashboard. |
02-Integration.json |
2 | Process Plant + Setup | Adds a measurement level child and a machineGroupControl parent with two rotatingMachine pumps. Demonstrates the Phase-2 parent/child handshake. |
03-Dashboard.json |
3 | Process Plant + Dashboard UI + Setup | Tier 2 plumbing plus a FlowFuse Dashboard 2.0 page with 3 charts (flow / level / volume %), text widgets, and 2 controls (mode dropdown + demand slider). |
Prerequisites
- Node-RED with the EVOLV package installed (so the
pumpingStation,measurement,machineGroupControl, androtatingMachinenode types are registered). - For
03-Dashboard.json:@flowfuse/node-red-dashboard(Dashboard 2.0).
How to load
# Drop a file into a running Node-RED instance using its Admin API.
curl -X POST -H 'Content-Type: application/json' \
--data @nodes/pumpingStation/examples/01-Basic.json \
http://localhost:1880/flows
Or in the editor: Menu -> Import -> select file -> Import. The flows import into their own tabs and can be deployed immediately.
01-Basic - what to try
- Deploy.
- Inject
set.mode = manual. - Inject
set.inflow = 60 m3/h- the basin starts filling. Watch the formatted Port 0 payload in the debug sidebar. - Inject
set.demand = 40 %- in manual mode this would feed any registered children; here there are no pump children so it is logged and shown on Port 0. - Inject
cmd.calibrate.volume = 25 m3to jump the predicted-volume integrator to half-full.
02-Integration - what to try
- Deploy. The Setup tab fires
set.mode = levelbasedto the station andset.mode = autoto the MGC. - The two pumps register with the MGC via Port 2; the MGC and the level sensor register with the station via Port 2. Watch the registration debug taps to confirm.
- The level inject pushes a 1.6 m measurement so the station sees a
non-zero starting level. Setup also seeds
set.inflow = 60 m3/h. - The station's
controlMode = levelbasedthen drives the MGC, which dispatches to Pump A / Pump B.
03-Dashboard - what to try
- Deploy.
- Open the dashboard at
http://localhost:1880/dashboard/page/pumping-station. - Use the Control mode dropdown to switch between
manual,levelbased,flowbased,none. - In manual mode, drag the Manual demand slider - the demand cascades to the MGC and on to the pumps.
- The three charts (flow, level, volume %) plot live data; the four text widgets show state, percControl, direction, and time-to-empty.
Layout conventions
These flows follow the EVOLV layout rule set in
.claude/rules/node-red-flow-layout.md:
- Tabs split by concern: Process Plant (EVOLV nodes) / Dashboard UI
(
ui-*widgets) / Setup (once-true injects). - Cross-tab wiring via named link out / link in channels:
setup:to-ps-mode,setup:to-ps-inflow,setup:to-mgc-mode,cmd:ps-mode,cmd:ps-demand,evt:flow,evt:level,evt:volpct,evt:state,evt:perc,evt:dir,evt:tempty. - Lane positions L0-L7 =
[120, 360, 600, 840, 1080, 1320, 1560, 1800], driven by each node's S88 level (Process Cell on L5, Unit on L4, Equipment on L3, Control Module on L2). - Group boxes wrap each parent + its direct children, coloured by the parent's S88 level.
Regenerating
These flows are generated from tools/build-examples.js. Edit the
generator, never the JSON, then:
node nodes/pumpingStation/tools/build-examples.js
The script writes 01-Basic.json, 02-Integration.json, and
03-Dashboard.json into this directory.