Files
measurement/CONTRACT.md
znetsixe b990f67df1 P3 wave 1: extract measurement simulator/calibration/commands + CONTRACT
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>
2026-05-10 20:32:26 +02:00

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 by outputUtils.formatMsg(..., 'process') from getOutput() (analog) or getDigitalOutput() (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.upstream
  • flow.measured.atequipment
  • level.measured.downstream
  • temperature.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.