Files
rotatingMachine/wiki/Reference-Contracts.md
znetsixe 5ea0b0bda6 feat(state): honor sequenceAbortToken so external aborts cleanly break sequences
Consumer half of the abort-token mechanism added in generalFunctions
state.js. executeSequence captures host.state.sequenceAbortToken at
entry, then re-checks before every state transition and after the
optional ramp-down. If MGC (or any external caller) bumps the token
mid-sequence, the loop bails out cleanly — no more barge-through where
a pre-empted shutdown advances through stopping → coolingdown after a
fresh demand has already engaged the pump.

Without this the MGC rendezvous planner can't reliably re-dispatch a
pump that's mid-shutdown: the new flowmovement claims the gate, but
the old shutdown's for-loop keeps running on microtasks and steps the
FSM into idle/off underneath it.

Also: wiki regen following the same visual-first 14-section template as
the other EVOLV nodes — Reference-{Architecture,Contracts,Examples,
Limitations}.md split with _Sidebar.md index.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:44:48 +02:00

16 KiB
Raw Permalink Blame History

Reference — Contracts

code-ref

Note

Full topic contract, configuration schema, and child-registration filters for rotatingMachine. Source of truth: src/commands/index.js, src/specificClass.js configure(), and the schema at generalFunctions/src/configs/rotatingMachine.json.

For an intuitive overview, return to the Home.


Topic contract

The registry lives in src/commands/index.js. Each descriptor maps a canonical msg.topic to its handler; aliases emit a one-time deprecation warning the first time they fire.

Canonical topic Aliases Payload Unit Effect
set.mode setMode string (auto / virtualControl / fysicalControl) Switch operational mode. Each mode has its own allow-list of actions and sources.
cmd.startup any Run the configured startup sequence (default [starting, warmingup, operational]).
cmd.shutdown any Run the shutdown sequence. If currently operational, executeSequence first ramps the setpoint to 0 (interruptible).
cmd.estop emergencystop any Run the emergencystop sequence (default [emergencystop, off]). Reachable from every state.
set.setpoint execMovement {setpoint: number} control % (no units — convert has no percent measure) Move to a control-axis setpoint via state.moveTo.
set.flow-setpoint flowMovement {setpoint: number} or bare number volumeFlowRate (default m3/h) Convert to canonical m³/s, then to control % via predictCtrl.y, then state.moveTo.
data.simulate-measurement simulateMeasurement {asset: {type, unit}, value, position, childName?, childId?} type-specific Inject a virtual sensor reading. The two virtual children (dashboard-sim-upstream / -downstream) auto-handle pressure; other types use the registering child's id.
query.curves showWorkingCurves any Reply on Port 0 with the current working curves (flow / power / efficiency).
query.cog CoG any Reply on Port 0 with the centre-of-gravity (CoG) point.
child.register registerChild string (child node id) Register a measurement child with this machine. Port 2 wiring does this automatically in normal flows.
execSequence {action: "startup" | "shutdown"} Legacy umbrella: demuxes payload.action to the canonical cmd.startup / cmd.shutdown handler. Marked _legacy: true; scheduled for removal.

Mode / source / action allow-lists

A topic that survives the registry still passes through flowController.handle:

if (!host.isValidActionForMode(action, host.currentMode)) return;
if (!host.isValidSourceForMode(source, host.currentMode)) return;

Defaults from the schema:

Mode allowedActions allowedSources
auto statuscheck, execmovement, execsequence, flowmovement, emergencystop, entermaintenance parent, GUI, fysical
virtualControl statuscheck, execmovement, flowmovement, execsequence, emergencystop, exitmaintenance GUI, fysical
fysicalControl statuscheck, emergencystop, entermaintenance, exitmaintenance fysical

A rejected request logs at warn and short-circuits; nothing reaches the FSM.


Data model — getOutput() shape

Composed each tick by src/io/output.js buildOutput(). Delta-compressed: consumers see only the keys that changed.

Per-measurement keys

For every (type, variant, position) stored in MeasurementContainer, the flattened output emits:

<type>.<variant>.<position>.<childId>

Position labels are normalised to lowercase in the keys (atequipment, downstream, upstream, max, min). The trailing <childId> is:

<childId> When
default The node's own predictions (flow / power / efficiency / Ncog).
dashboard-sim-upstream / dashboard-sim-downstream The two auto-registered virtual pressure children.
The real child's general.id When a registered measurement child wrote the value.

Sample keys (operational pump, simulated pressure):

Key Type Unit Notes
flow.predicted.downstream.default number m³/h Live predicted flow.
flow.predicted.atequipment.default number m³/h Same number, equipment-side label.
flow.predicted.max.default / .min.default number m³/h Curve envelope at the current fDimension.
power.predicted.atequipment.default number kW Predicted shaft power.
pressure.measured.upstream.dashboard-sim-upstream number mbar Last simulated suction pressure.
pressure.measured.downstream.dashboard-sim-downstream number mbar Last simulated discharge pressure.
temperature.measured.atequipment.dashboard-sim-upstream number °C Default 15°C until overwritten.
atmPressure.measured.atequipment.dashboard-sim-upstream number Pa Default 101325 Pa until overwritten.

Scalar keys

Key Type Source Notes
state string host.state.getCurrentState() One of the FSM states (idle, starting, warmingup, …).
ctrl number host.state.getCurrentPosition() Control-axis position 0..100.
mode string host.currentMode auto / virtualControl / fysicalControl.
runtime number host.state.getRunTimeHours() Cumulative hours in active states.
moveTimeleft number host.state.getMoveTimeLeft() Seconds remaining on the current move (0 when idle).
maintenanceTime number host.state.getMaintenanceTimeHours() Cumulative hours in maintenance.
cog / NCog / NCogPercent number host.cog etc. CoG metric on the η curve. NCog 0..1; NCogPercent is NCog * 100, rounded to 2 dp.
effDistFromPeak number host.absDistFromPeak Absolute η distance to peak.
effRelDistFromPeak number host.relDistFromPeak Normalised 0..1; undefined when η band collapses.
predictionQuality string host.predictionHealth.quality good / warming / degraded / invalid.
predictionConfidence number host.predictionHealth.confidence 0..1, rounded to 3 dp.
predictionPressureSource string | null host.predictionHealth.pressureSource dashboard-sim or a real child id; null until pressure landed.
predictionFlags array host.predictionHealth.flags Reason codes (e.g. pressure_init_warming).
pressureDriftLevel number host.pressureDrift.level 0..3.
pressureDriftSource string | null host.pressureDrift.source Source whose drift is worst.
pressureDriftFlags array host.pressureDrift.flags nominal when no drift detected.
flowNrmse / flowLongTermNRMSD / flowImmediateLevel / flowLongTermLevel / flowDriftValid numbers / number / number / boolean host.flowDrift Only present once flowDrift != null.
powerNrmse / powerLongTermNRMSD / powerImmediateLevel / powerLongTermLevel / powerDriftValid same host.powerDrift Same.

Status badge

buildStatusBadge in io/output.js:

<mode>: <state-symbol> <ctrl%>% 💨<flow><unit> ⚡<power>kW

State symbols (per STATE_SYMBOLS map):

State Symbol Fill
off red
idle ⏸️ blue
operational ⏵️ green
starting ⏯️ yellow
warmingup 🔄 green
accelerating yellow
decelerating yellow
stopping ⏹️ yellow
coolingdown ❄️ yellow
maintenance 🔧 grey

Pressure-not-initialised states (operational, warmingup, accelerating, decelerating) override the badge to a yellow ring '<mode>: pressure not initialized' until at least one pressure source has been written.


Configuration schema — editor form to config keys

Source of truth: generalFunctions/src/configs/rotatingMachine.json plus nodeClass.buildDomainConfig.

General (config.general)

Form field Config key Default Notes
Name general.name derived: <softwareType>_<id> Re-derived in configure().
(auto-assigned) general.id null Node-RED node id.
Default unit general.unit l/s (schema) / m3/h (nodeClass) buildDomainConfig resolves uiConfig.unit via convert and overrides to a valid flow unit.
Enable logging general.logging.enabled true Master switch.
Log level general.logging.logLevel info debug / info / warn / error.

Functionality (config.functionality)

Form field Config key Default Notes
Position vs parent functionality.positionVsParent atEquipment One of atEquipment / upstream / downstream. Used in the child-register payload that goes UP to MGC / pumpingStation.
(hidden) functionality.softwareType rotatingmachine Constant.
(hidden) functionality.role RotationalDeviceController Constant.
Distance offset functionality.distance null Optional spatial offset; populated when hasDistance is enabled.
Distance unit functionality.distanceUnit m
Distance description functionality.distanceDescription "" Free-text.

Asset (config.asset)

Resolved derived metadata (supplier / category / type / allowed units) lives in generalFunctions/datasets/assetData/rotatingmachine.json keyed by asset.model. The editor's asset menu reads from that registry.

Form field Config key Default Notes
Asset UUID asset.uuid null Globally-unique identifier.
Tag code asset.tagCode null
Tag number asset.tagNumber null Legacy column.
Geolocation asset.geoLocation {x:0, y:0, z:0}
Model asset.model null Required. Resolves curve + supplier / type / allowed units via the registry.
Deployment unit asset.unit null Required. Must be a flow unit; soft-warned if not in the registry's recommended list for the model.
Curve units asset.curveUnits {pressure:'mbar', flow:'m3/h', power:'kW', control:'%'} Carried for curve normalisation.
Accuracy asset.accuracy null Optional sensor accuracy %.
(derived) asset.machineCurve {nq:{}, np:{}} Loaded from loadModelCurve(model), then normalised.

Warning

Legacy fields removed. supplier, category, and assetType are no longer node config — the registry derives them from the model. Flows saved before the AssetResolver refactor will throw a startup error with a clear migration message. Re-open the node, re-select the model from the asset menu, and save.

State times (stateConfig.time)

Set on the state machine via nodeClass.buildDomainConfig from editor fields:

Form field Config key Default (schema) Notes
Startup Time time.starting configured in s Time spent in starting before transitioning to warmingup.
Warmup Time time.warmingup configured in s Time in warmingupnon-interruptible safety.
Shutdown Time time.stopping configured in s Time in stopping.
Cooldown Time time.coolingdown configured in s Time in coolingdownnon-interruptible safety.

Movement (stateConfig.movement)

Form field Config key Default Notes
Reaction Speed movement.speed configured in %/s Controller ramp rate. E.g. 1 means 1%/s → setpoint 60 from idle reaches 60 in ~60 s.
Movement Mode movement.mode staticspeed staticspeed (linear ramp) or dynspeed (cubic ease-in-out). Both yield the same total duration; only the curve differs.
(internal) movement.maxSpeed from schema Hard cap honoured by movementManager.getNormalizedSpeed.
(internal) movement.interval from schema Inner-loop tick of the move animation (ms).

Sequences (config.sequences)

State-transition lists per sequence name. Defaults:

Sequence States
startup [starting, warmingup, operational]
shutdown [stopping, coolingdown, idle]
emergencystop [emergencystop, off]
boot [idle, starting, warmingup, operational]
entermaintenance [stopping, coolingdown, idle, maintenance]
exitmaintenance [off, idle]

Custom sequences are accepted as long as every step is a known FSM state and the transitions between them are allowed by stateConfig.allowedTransitions.

Output (config.output)

Form field Config key Default Range Notes
Process Output output.process process process / json / csv Port-0 formatter.
Database Output output.dbase influxdb influxdb / json / csv Port-1 formatter.

Mode (config.mode)

Form field Config key Default Range Notes
Mode mode.current auto auto / virtualControl / fysicalControl The active operational mode.
(defaults) mode.allowedActions.<mode> see Architecture enforced by flowController.handle
(defaults) mode.allowedSources.<mode> see Architecture enforced by flowController.handle

Unit policy

Source: src/specificClass.js lines 3641.

Quantity Canonical (internal) Output (rendered) Curve (supplier) Required-unit
Pressure Pa mbar mbar
Atmospheric pressure Pa Pa
Flow m3/s m3/h m3/h
Power W kW kW
Temperature K °C
Control %

requireUnitForTypes means MeasurementContainer rejects writes that omit unit for these types.


Child registration

Source: src/measurement/childRegistrar.js registerMeasurementChild. The registrar reads asset.type and positionVsParent from the child's config and subscribes to <type>.measured.<position> on the child's measurement emitter.

Software type Filter Wired to Side-effect
measurement asset.type='pressure', position=upstream pressureRouter.route('upstream', value, ctx) Stored as upstream pressure; refresh prediction + drift. pressureInitialization tracks readiness.
measurement asset.type='pressure', position=downstream pressureRouter.route('downstream', value, ctx) Same on the discharge side.
measurement asset.type='flow', position=* measurementHandlers.updateMeasuredFlow Stored; drift assessed against predicted.
measurement asset.type='power', position=atEquipment measurementHandlers.updateMeasuredPower Stored; drift assessed against predicted.
measurement asset.type='temperature', position=* measurementHandlers.updateMeasuredTemperature Stored; surfaced on Port 0.

Virtual pressure children — auto-registered

At startup specificClass registers two measurement-typed children:

Child id Position Default value Use
dashboard-sim-upstream upstream 0 mbar Receives data.simulate-measurement payloads with position upstream.
dashboard-sim-downstream downstream 0 mbar Same for downstream.

pressureSelector prefers a real registered child over the virtuals once one shows up — the virtuals keep listening so dashboards can still inject sim values during real-pressure outages.


Page Why
Home Intuitive overview
Reference — Architecture Code map, FSM, prediction + drift pipeline
Reference — Examples Shipped flows + debug recipes
Reference — Limitations Known issues and open questions
EVOLV — Topic Conventions Platform-wide topic rules
EVOLV — Telemetry Port 0 / 1 / 2 InfluxDB layout