- 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>
25 KiB
25 KiB
Rotating Machine Function Anchor
0) Connection Map (At a Glance)
- Node type:
rotatingMachine(nodes/rotatingMachine/rotatingMachine.js:1,nodes/rotatingMachine/rotatingMachine.html:16) - Consumes parent/control topics:
setMode,execSequence,execMovement,flowMovement,emergencystop,simulateMeasurement,registerChild,showWorkingCurves,CoG(nodes/rotatingMachine/src/nodeClass.js:267) - Publishes periodic outputs:
- Output
0: process payload (nodes/rotatingMachine/src/nodeClass.js:249) - Output
1: influx payload (nodes/rotatingMachine/src/nodeClass.js:251) - Output
2: registration/control plumbing (registerChild) (nodes/rotatingMachine/src/nodeClass.js:222)
- Output
- Cross-node integrations (direct observed):
- Registered/managed by
machineGroupControlasmachine, which then commands each machine viahandleInput('parent', ...)(nodes/machineGroupControl/src/specificClass.js:50,nodes/machineGroupControl/src/specificClass.js:711,nodes/machineGroupControl/src/specificClass.js:1028) - Can be orchestrated by
pumpingStationviaexecSequenceand movement commands (nodes/pumpingStation/src/specificClass.js:296,nodes/pumpingStation/src/specificClass.js:297) - Dashboard/test flows inject
simulateMeasurementand consume process output topics (nodes/rotatingMachine/examples/basic.flow.json:380,nodes/rotatingMachine/examples/basic.flow.json:412)
- Registered/managed by
- Admin/UI endpoints:
GET /rotatingMachine/menu.jsGET /rotatingMachine/configData.js(nodes/rotatingMachine/rotatingMachine.js:17,nodes/rotatingMachine/rotatingMachine.js:27)
1) Unit Table (Anchor Starts Here)
| Signal/Field | Represents | Asset Type | Default Unit | Accepted Units | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|---|---|---|---|---|---|---|---|---|
pressure.measured.* |
pressure input (upstream/downstream) | measurement child (pressure) |
mbar |
any convertible via MeasurementContainer |
nodes/rotatingMachine/src/specificClass.js:48, nodes/rotatingMachine/src/specificClass.js:708 |
real child sensors and virtual dashboard child | pressure dimension for curve selection (predict*.fDimension) |
if missing, pressure dimension forced to minimum (0) (nodes/rotatingMachine/src/specificClass.js:552) |
flow.predicted.downstream |
predicted flow at discharge | rotating machine | general.unit from config |
convertible (m3/h, l/s, etc.) |
nodes/rotatingMachine/src/specificClass.js:53, nodes/rotatingMachine/src/specificClass.js:423 |
calcFlow() |
output formatting, status text, parent/group logic | forced 0 if non-operational or no curve (nodes/rotatingMachine/src/specificClass.js:415, nodes/rotatingMachine/src/specificClass.js:429) |
flow.predicted.atEquipment |
same flow at equipment point | rotating machine | general.unit |
convertible | nodes/rotatingMachine/src/specificClass.js:53, nodes/rotatingMachine/src/specificClass.js:424 |
calcFlow() |
efficiency calculations | forced 0 if non-operational/no curve |
power.predicted.atEquipment |
predicted power draw | rotating machine | kW |
convertible (W, kW) |
nodes/rotatingMachine/src/specificClass.js:54, nodes/rotatingMachine/src/specificClass.js:448 |
calcPower() |
efficiency calculations, status text | forced 0 if non-operational/no curve |
ctrl.predicted.atEquipment |
predicted control position for requested flow | rotating machine | unitless (%) semantic | numeric | nodes/rotatingMachine/src/specificClass.js:482 |
calcCtrl() |
flowmovement command path |
returns 0 if no curve |
temperature.measured.atEquipment |
process temp for density lookup | machine fluid context | C default, converted to K when used |
convertible | init at 15 C (nodes/rotatingMachine/src/specificClass.js:149) |
init + measurement updates | CoolProp density input | if missing conversion, efficiency can degrade silently |
atmPressure.measured.atEquipment |
atmospheric pressure for density lookup | machine fluid context | Pa |
convertible | init at 101325 Pa (nodes/rotatingMachine/src/specificClass.js:151) |
init | CoolProp density input | fallback density 1000 kg/m3 on CoolProp error (nodes/rotatingMachine/src/specificClass.js:867) |
efficiency.* |
specific flow (flow/power) |
derived metric | implicit unitless ratio | numeric | nodes/rotatingMachine/src/specificClass.js:878, nodes/rotatingMachine/src/specificClass.js:881 |
calcEfficiency() |
output and BEP distance metrics | unchanged if power/flow are zero |
specificEnergyConsumption.* |
power per flow | derived metric | implicit | numeric | nodes/rotatingMachine/src/specificClass.js:879, nodes/rotatingMachine/src/specificClass.js:882 |
calcEfficiency() |
output consumers | unchanged if power/flow zero |
nHydraulicEfficiency.* |
hydraulic-efficiency-like metric | derived metric | unitless | numeric | nodes/rotatingMachine/src/specificClass.js:884 |
calcEfficiency() |
diagnostic output | skipped if pressure/flow/power conversions unavailable |
cog, NCog, NCogPercent |
efficiency-curve peak indicators | derived curve metrics | unitless | numeric | nodes/rotatingMachine/src/specificClass.js:796, nodes/rotatingMachine/src/specificClass.js:948 |
calcCog(), getOutput() |
group optimization, dashboards | retains last computed values |
effDistFromPeak, effRelDistFromPeak |
distance from best-efficiency point | derived | unitless | numeric | nodes/rotatingMachine/src/specificClass.js:924, nodes/rotatingMachine/src/specificClass.js:962 |
calcDistanceBEP() |
output consumers | remains last computed value |
runtime, maintenanceTime, moveTimeleft, state |
movement/state telemetry | state machine | h/s/enum |
numeric/string | nodes/rotatingMachine/src/specificClass.js:943 |
state module |
output/status/parent control | depends on state module behavior |
2) Class Identity
- Runtime registration + endpoints:
nodes/rotatingMachine/rotatingMachine.js - Node-RED wrapper/routing:
nodes/rotatingMachine/src/nodeClass.js - Domain/mechanical logic:
nodes/rotatingMachine/src/specificClass.js - Editor UI/defaults:
nodes/rotatingMachine/rotatingMachine.html - Default config schema/validation rules:
nodes/generalFunctions/src/configs/rotatingMachine.json
3) Configuration Contract
| UI Field | Runtime Path | Default | Validation/Coercion | Behavior Impact | Source |
|---|---|---|---|---|---|
speed |
stateConfig.movement.speed |
1 |
Number(uiConfig.speed) |
movement progression speed | nodes/rotatingMachine/rotatingMachine.html:22, nodes/rotatingMachine/src/nodeClass.js:91 |
startup/warmup/shutdown/cooldown |
stateConfig.time.* |
0 |
Number(...) |
sequence transition durations | nodes/rotatingMachine/rotatingMachine.html:23, nodes/rotatingMachine/src/nodeClass.js:95 |
movementMode |
stateConfig.movement.mode |
staticspeed |
raw string | state movement model selection | nodes/rotatingMachine/rotatingMachine.html:27, nodes/rotatingMachine/src/nodeClass.js:92 |
unit |
config.general.unit + config.asset.unit |
UI empty, config default l/s |
direct assign then config init | base flow unit for measurements and outputs | nodes/rotatingMachine/src/nodeClass.js:50, nodes/generalFunctions/src/configs/rotatingMachine.json:18 |
model |
config.asset.model |
UI empty, config default Unknown |
direct assign | curve loading via loadCurve(model) |
nodes/rotatingMachine/src/nodeClass.js:62, nodes/rotatingMachine/src/specificClass.js:18 |
| logging fields | config.general.logging.* |
enableLog=false, logLevel=error in UI; config default enabled/info |
direct assign | runtime verbosity | nodes/rotatingMachine/rotatingMachine.html:39, nodes/rotatingMachine/src/nodeClass.js:51 |
positionVsParent |
config.functionality.positionVsParent |
UI empty, config default atEquipment |
direct assign + default in schema | registration topology to parent | nodes/rotatingMachine/src/nodeClass.js:66, nodes/generalFunctions/src/configs/rotatingMachine.json:74 |
| Mode/action/source rules | config.mode.* |
schema defaults | configUtils validation into Set semantics |
command gating | nodes/generalFunctions/src/configs/rotatingMachine.json:231, nodes/rotatingMachine/src/specificClass.js:269 |
| Sequences | config.sequences.* |
schema defaults | configUtils validation | machine state transitions | nodes/generalFunctions/src/configs/rotatingMachine.json:360, nodes/rotatingMachine/src/specificClass.js:363 |
4) Input/Output Contract
4.1 Input topics (nodeClass)
| Topic | Payload schema | Handler | Side effects |
|---|---|---|---|
registerChild |
payload=<nodeId>, optional positionVsParent |
registers child source via childRegistrationUtils |
starts measurement event wiring (nodes/rotatingMachine/src/nodeClass.js:268) |
setMode |
payload=<mode> |
setMode() |
updates command policy mode |
execSequence |
{source, action, parameter} |
handleInput() |
executes state sequence |
execMovement |
{source, action, setpoint} |
handleInput() |
moves position |
flowMovement |
{source, action, setpoint} |
handleInput() |
converts flow->ctrl then moves |
emergencystop |
{source, action} |
handleInput() |
emergency sequence attempt |
simulateMeasurement |
{type, position, value, unit, timestamp?} |
measurement update handlers | updates virtual pressure or measured values |
showWorkingCurves |
any | immediate response on output 0 | emits curve/cog debug payload |
CoG |
any | immediate response on output 0 | calls m.showCoG() (method currently not defined in specificClass) |
4.2 Output ports
| Port | Message type | Source |
|---|---|---|
0 |
formatted process message from flattened measurements + state fields | nodes/rotatingMachine/src/nodeClass.js:250 |
1 |
formatted influxdb message | nodes/rotatingMachine/src/nodeClass.js:251 |
2 |
registration to parent: {topic:'registerChild', payload:id, positionVsParent} |
nodes/rotatingMachine/src/nodeClass.js:222 |
4.3 Admin endpoints
| Endpoint | Purpose | Source |
|---|---|---|
/rotatingMachine/menu.js |
dynamic editor menu script (asset, logger, position) |
nodes/rotatingMachine/rotatingMachine.js:17 |
/rotatingMachine/configData.js |
dynamic default/config script | nodes/rotatingMachine/rotatingMachine.js:27 |
5) Mode, State, and Control Model
- Modes:
auto,virtualControl,fysicalControl(nodes/generalFunctions/src/configs/rotatingMachine.json:233) - Mode gate enforcement:
isValidActionForMode(action, mode)(nodes/rotatingMachine/src/specificClass.js:279)isValidSourceForMode(source, mode)(nodes/rotatingMachine/src/specificClass.js:269)
- Actions supported by handler:
execsequence,execmovement,flowmovement,entermaintenance,exitmaintenance,emergencystop,statuscheck(nodes/rotatingMachine/src/specificClass.js:303) - Operational states for active prediction:
operational,warmingup,accelerating,decelerating(nodes/rotatingMachine/src/specificClass.js:739) - Sequence defaults: startup/shutdown/emergencystop/maintenance flows defined in config schema (
nodes/generalFunctions/src/configs/rotatingMachine.json:365)
6) End-to-End Execution Flow
- Node registration instantiates
nodeClass, thenSpecific(Machine). Machineconstructor loads model curve, initializes predictors/state/measurements, creates virtual pressure children, and subscribes to state events.nodeClassstarts delayed child registration (output 2) and 1-second tick/status loops.- Incoming topics route through
switch(msg.topic)to mode changes, movement/sequence commands, child registration, and simulated measurements. - Child measurement events update parent measurement container and dispatch typed handlers.
- Pressure updates set predictor dimension, recompute flow/power/efficiency/CoG/BEP metrics.
- Each tick emits formatted process + influx messages.
7) Full Function Inventory
7.1 nodes/rotatingMachine/rotatingMachine.js
| Function | Purpose | Source |
|---|---|---|
| module export init | register Node-RED node type and admin endpoints | nodes/rotatingMachine/rotatingMachine.js:5 |
7.2 nodes/rotatingMachine/src/nodeClass.js
| Function | Purpose | Key effects | Source |
|---|---|---|---|
constructor |
boot wrapper lifecycle | load config, create source, start loops/handlers | nodes/rotatingMachine/src/nodeClass.js:16 |
_loadConfig |
map UI config to runtime config | builds general/asset/functionality; builds outputUtils |
nodes/rotatingMachine/src/nodeClass.js:44 |
_setupSpecificClass |
build Machine with movement/time state config |
instantiates Specific; stores on node.source |
nodes/rotatingMachine/src/nodeClass.js:77 |
_bindEvents |
placeholder | no-op currently | nodes/rotatingMachine/src/nodeClass.js:112 |
_updateNodeStatus |
compose Node-RED status icon/text | warns once for missing pressure init; includes flow/power/state | nodes/rotatingMachine/src/nodeClass.js:116 |
_registerChild |
announce self to parent | sends registerChild on output 2 after 100ms |
nodes/rotatingMachine/src/nodeClass.js:217 |
_startTickLoop |
periodic work | 1s _tick; 1s status refresh |
nodes/rotatingMachine/src/nodeClass.js:230 |
_tick |
periodic output generation | formatMsg(process) + formatMsg(influxdb) send to outputs 0/1 |
nodes/rotatingMachine/src/nodeClass.js:246 |
_attachInputHandler |
route inbound topics | dispatches all command/simulation/register/show topics | nodes/rotatingMachine/src/nodeClass.js:260 |
_attachCloseHandler |
shutdown cleanup | clears intervals | nodes/rotatingMachine/src/nodeClass.js:357 |
7.3 nodes/rotatingMachine/src/specificClass.js
| Function | Purpose | Key effects | Source |
|---|---|---|---|
constructor |
initialize machine domain object | config/curve/predictors/state/measurement/events/virtual children | nodes/rotatingMachine/src/specificClass.js:7 |
_initVirtualPressureChildren |
create simulated upstream/downstream pressure children | registers virtual measurement children | nodes/rotatingMachine/src/specificClass.js:104 |
_init |
seed base measurements and min/max flow | sets temperature, atmPressure, curve min/max | nodes/rotatingMachine/src/specificClass.js:147 |
_updateState |
enforce non-operational flow = 0 | overwrites predicted flow when inactive | nodes/rotatingMachine/src/specificClass.js:163 |
registerChild |
subscribe to child measurement events | stores real pressure child IDs, updates measurements, calls handlers | nodes/rotatingMachine/src/specificClass.js:173 |
_callMeasurementHandler |
typed measurement dispatch | pressure/flow/temp handlers + fallback | nodes/rotatingMachine/src/specificClass.js:212 |
assessDrift |
compare measured vs predicted windows | delegates to nrmse.assessDrift |
nodes/rotatingMachine/src/specificClass.js:237 |
reverseCurve |
flip x/y arrays | used for flow->ctrl predictor | nodes/rotatingMachine/src/specificClass.js:252 |
updateConfig |
apply validated config patch | merges via configUtils |
nodes/rotatingMachine/src/specificClass.js:264 |
isValidSourceForMode |
mode source gate | checks configured allowed set | nodes/rotatingMachine/src/specificClass.js:269 |
isValidActionForMode |
mode action gate | checks configured allowed set | nodes/rotatingMachine/src/specificClass.js:279 |
handleInput |
main command dispatcher | executes sequence/movement/status commands | nodes/rotatingMachine/src/specificClass.js:289 |
abortMovement |
cancel current movement | delegates to state abort if available | nodes/rotatingMachine/src/specificClass.js:345 |
setMode |
update current mode | validates against schema enum values | nodes/rotatingMachine/src/specificClass.js:351 |
executeSequence |
run state sequence | transitions through configured states; updatePosition at end | nodes/rotatingMachine/src/specificClass.js:363 |
setpoint |
move to numeric target | validates non-negative number then state.moveTo |
nodes/rotatingMachine/src/specificClass.js:394 |
calcFlow |
predict flow from current curve and ctrl position | writes predicted flow measurements | nodes/rotatingMachine/src/specificClass.js:413 |
calcPower |
predict power from current curve and ctrl position | writes predicted power measurement | nodes/rotatingMachine/src/specificClass.js:438 |
inputFlowCalcPower |
estimate power from requested flow | flow->ctrl->power chained prediction | nodes/rotatingMachine/src/specificClass.js:460 |
calcCtrl |
estimate ctrl for desired flow | writes predicted ctrl | nodes/rotatingMachine/src/specificClass.js:478 |
getMeasuredPressure |
choose pressure basis for prediction | differential preferred; then downstream; then upstream; else 0 | nodes/rotatingMachine/src/specificClass.js:496 |
_getPreferredPressureValue |
pressure source priority resolver | real child > virtual child > aggregated position value | nodes/rotatingMachine/src/specificClass.js:570 |
getPressureInitializationStatus |
pressure readiness status model | upstream/downstream/differential flags | nodes/rotatingMachine/src/specificClass.js:600 |
updateSimulatedMeasurement |
write dashboard-sim values | pressure route via virtual child; others dispatch typed handler | nodes/rotatingMachine/src/specificClass.js:617 |
handleMeasuredFlow |
reconcile measured flow availability/consistency | returns matched/single measurement or null | nodes/rotatingMachine/src/specificClass.js:644 |
handleMeasuredPower |
read measured power | returns value or null with error | nodes/rotatingMachine/src/specificClass.js:685 |
updateMeasuredTemperature |
temp update hook | currently log-only | nodes/rotatingMachine/src/specificClass.js:698 |
updateMeasuredPressure |
pressure update hook | stores pressure, recomputes pressure basis and position metrics | nodes/rotatingMachine/src/specificClass.js:703 |
updateMeasuredFlow |
flow update hook | stores measured flow if operational; mirrors predicted flow value | nodes/rotatingMachine/src/specificClass.js:718 |
_isOperationalState |
operational predicate | active states used by prediction guards | nodes/rotatingMachine/src/specificClass.js:737 |
updatePosition |
core recompute pipeline on movement/state changes | calc flow/power -> efficiency -> cog -> BEP distance | nodes/rotatingMachine/src/specificClass.js:745 |
calcDistanceFromPeak |
abs distance metric | absolute efficiency delta | nodes/rotatingMachine/src/specificClass.js:767 |
calcRelativeDistanceFromPeak |
normalized distance metric | interpolation to [0,1] | nodes/rotatingMachine/src/specificClass.js:771 |
showWorkingCurves |
debugging snapshot of current curve context | returns current curves + metrics | nodes/rotatingMachine/src/specificClass.js:779 |
calcCog |
compute peak efficiency point on current curve | updates cog, NCog, indexes, minEfficiency |
nodes/rotatingMachine/src/specificClass.js:796 |
calcEfficiencyCurve |
derive efficiency curve and peak/min | from aligned power/flow arrays | nodes/rotatingMachine/src/specificClass.js:817 |
calcFlowPower |
convenience combined prediction | calls calcFlow and calcPower |
nodes/rotatingMachine/src/specificClass.js:845 |
calcEfficiency |
compute efficiency family metrics | CoolProp density path + fallback + writes derived metrics | nodes/rotatingMachine/src/specificClass.js:854 |
updateCurve |
replace machine curve at runtime | validates config and updates predictors | nodes/rotatingMachine/src/specificClass.js:897 |
getCompleteCurve |
return full loaded curves | power+flow input curves | nodes/rotatingMachine/src/specificClass.js:910 |
getCurrentCurves |
return currently selected pressure curve slices | current flow/power curves | nodes/rotatingMachine/src/specificClass.js:916 |
calcDistanceBEP |
write BEP distance metrics | updates absDistFromPeak, relDistFromPeak |
nodes/rotatingMachine/src/specificClass.js:924 |
getOutput |
flatten and enrich output object | adds state/runtime/ctrl/mode/cog/drift/eff-distance | nodes/rotatingMachine/src/specificClass.js:936 |
8) Calculations and Capability Matrix
- Curve-backed capabilities:
- flow prediction (
nq) viapredictFlow - power prediction (
np) viapredictPower - control inversion (flow->ctrl) via reversed
nq
- flow prediction (
- Pressure basis selection order:
- real differential (
downstream - upstream) - real/virtual downstream only
- real/virtual upstream only
- fallback
0(minimum pressure behavior)
- real differential (
- Availability-first behavior:
- missing pressure does not stop operation; it degrades predictions and warns.
- CoolProp failure does not stop operation; density fallback is used.
- non-operational states force predicted flow/power to zero.
- BEP indicators:
- computes peak efficiency index and normalized CoG (
NCog) for optimization usage by parent/group controllers.
- computes peak efficiency index and normalized CoG (
9) Error Handling and Safeguards
- Invalid actions/sources are rejected by mode gates, with warnings (
nodes/rotatingMachine/src/specificClass.js:297). - Invalid setpoint (
<0or non-number) is rejected insetpoint()(nodes/rotatingMachine/src/specificClass.js:398). - Missing curve model disables predictor objects but keeps class alive (
nodes/rotatingMachine/src/specificClass.js:27). - Missing pressure initialization surfaces Node-RED warning ring status (
nodes/rotatingMachine/src/nodeClass.js:126). simulateMeasurementrejects non-finite values and unsupported types (nodes/rotatingMachine/src/nodeClass.js:312).
10) Test Evidence Matrix
| Test file | Covered behavior |
|---|---|
nodes/rotatingMachine/test/basic/constructor.basic.test.js |
constructor curve/no-curve behavior and output shape |
nodes/rotatingMachine/test/basic/mode-and-input.basic.test.js |
setMode, handleInput validation, operational-state predicate |
nodes/rotatingMachine/test/edge/error-paths.edge.test.js |
negative setpoint resilience, status failure path |
nodes/rotatingMachine/test/edge/nodeClass-routing.edge.test.js |
input-topic routing, pressure initialization status warning, curve/CoG reply routing |
nodes/rotatingMachine/test/integration/sequences.integration.test.js |
startup sequence and movement execution |
nodes/rotatingMachine/test/integration/registration.integration.test.js |
child registration and pressure event propagation |
nodes/rotatingMachine/test/integration/pressure-initialization.integration.test.js |
explicit pressure init combinations and real-vs-virtual pressure priority |
nodes/rotatingMachine/test/integration/coolprop.integration.test.js |
efficiency path with CoolProp and medium-pressure initialization behavior |
nodes/rotatingMachine/test/integration/basic-flow-dashboard.integration.test.js |
example-flow parser/wiring contracts for dashboard topics |
11) Invariants (Anchor Truth)
specificClassis the mechanical/logic source of truth;nodeClassis routing/lifecycle only.- Prediction calculations must be curve-backed when curve exists, and availability-first fallback when it does not.
- Pressure selection priority is real sensor > virtual dashboard > aggregated fallback.
- Command execution must remain mode-gated by both action and source.
- Output shape must keep process/influx separation and parent registration on output port 2.
- Operational-state gating must continue to prevent active prediction outputs in inactive states.
12) Known Gaps / Risks (Current Implementation)
nodeClassroutes topicCoGtom.showCoG(), butshowCoGis not present inspecificClass(runtime risk on that topic):nodes/rotatingMachine/src/nodeClass.js:340.handleInput('emergencystop')calls sequence"emergencyStop", but config default key is"emergencystop"(case/name mismatch risk):nodes/rotatingMachine/src/specificClass.js:327,nodes/generalFunctions/src/configs/rotatingMachine.json:381._setupSpecificClassusesmachineConfig.eneableLog(typo) for state logging config; likely not intended:nodes/rotatingMachine/src/nodeClass.js:86.- Label expression can evaluate unexpectedly because
+and||precedence are mixed:nodes/rotatingMachine/rotatingMachine.html:58.
13) Change Checklist
When changing rotatingMachine logic, update all of:
- Runtime logic in
nodes/rotatingMachine/src/specificClass.js. - Node-RED routing/lifecycle in
nodes/rotatingMachine/src/nodeClass.js. - UI defaults/fields in
nodes/rotatingMachine/rotatingMachine.html. - Config schema and mode/action/source/sequence defaults in
nodes/generalFunctions/src/configs/rotatingMachine.json. - Example flow contracts in
nodes/rotatingMachine/examples/*.flow.json. - Tests under
nodes/rotatingMachine/test/(basic, edge, integration).