src/simulation/simulator.js random-walk generator (was simulateInput inline)
src/calibration/calibrator.js calibrate + isStable + evaluateRepeatability,
using generalFunctions/stats. NB: isStable
tautology preserved verbatim — see
OPEN_QUESTIONS.md 2026-05-10 for the bug.
src/commands/ registry + handlers (canonical names from start)
CONTRACT.md inputs/outputs/events surface
77 basic tests pass (62 pre-refactor + 15 new across the three new files).
specificClass.js / nodeClass.js untouched — integration is P3 wave 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.0 KiB
measurement — Contract
Hand-maintained for Phase 3; the ## Inputs table is generated from
src/commands/index.js (see Phase 9 generator). Keep ≤ 80 lines.
Inputs (msg.topic on Port 0)
| Canonical | Aliases (deprecated) | Payload | Effect |
|---|---|---|---|
set.simulator |
simulator |
none (payload ignored) | Toggles source.toggleSimulation() — flips config.simulation.enabled. |
set.outlier-detection |
outlierDetection |
none (payload ignored) | Toggles source.toggleOutlierDetection() — flips config.outlierDetection.enabled. |
cmd.calibrate |
calibrate |
none | Calls source.calibrate() — captures the current input as the zero/reference offset. |
data.measurement |
measurement |
mode-dependent — see below | Pushes a sensor reading into the pipeline. Analog: numeric scalar (number or numeric string) → source.inputValue. Digital: object payload keyed by channel name → source.handleDigitalPayload(payload). Wrong shape for the configured mode logs a helpful warning suggesting the other mode. |
Aliases log a one-time deprecation warning the first time they fire.
Outputs (msg.topic on Port 0/1/2)
- Port 0 (process):
msg.topic = config.general.name. Payload built byoutputUtils.formatMsg(..., 'process')fromgetOutput()(analog) orgetDigitalOutput()(digital). Delta-compressed — only changed fields are emitted. - Port 1 (InfluxDB telemetry): same shape as Port 0, formatted with the
'influxdb'formatter. - Port 2 (registration): at startup the node sends one
{ topic: 'registerChild', payload: <node.id>, positionVsParent, distance }to its parent.
Events emitted by source.measurements.emitter
The MeasurementContainer fires <type>.measured.<position> whenever a
matching series receives a new value. The type / position labels are set
from config.asset.type and config.functionality.positionVsParent
(analog), or per-channel from config.channels[*] (digital). Examples:
pressure.measured.upstreamflow.measured.atequipmentlevel.measured.downstreamtemperature.measured.atequipment
Position labels are always lowercase in the event name. Parents subscribe
through the generic child.measurements.emitter.on(eventName, ...) handshake
established by childRegistrationUtils.
In digital mode one input message can fan out into several events — one per channel that accepted a value on that tick.
The legacy internal source.emitter also fires 'mAbs' with the current
scaled absolute value (analog mode only). This is deprecated in favour of
measurements.emitter and kept only for the editor status badge during the
refactor window.
Children registered by this node
None — measurement is a leaf in the S88 hierarchy (Control Module). It
registers itself as a child of an upstream parent (rotatingMachine,
pumpingStation, reactor, monster, …) but does not accept its own children.
Registration goes via Port 2 at startup and is keyed off
positionVsParent / distance in the node's UI config.