Files
measurement/wiki/Reference-Contracts.md
znetsixe b0e8bbb95d docs(wiki): regenerate topic-contract AUTOGEN block via wiki-gen
Replaces the agent-written placeholder inside Reference-Contracts.md with
the authoritative table generated from src/commands/index.js. Both the
BEGIN and END markers are normalized to the canonical form used by
`@evolv/wiki-gen`.

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

14 KiB

Reference — Contracts

code-ref

Note

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

Pending full node review (2026-05). Hand-written best-effort placeholder where indicated. For an intuitive overview, return to Home.


Topic contract

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

Canonical topic Aliases Payload Unit Effect
set.simulator simulator any Toggle the built-in simulator on / off.
set.outlier-detection outlierDetection any Toggle / configure outlier detection on the measurement pipeline.
cmd.calibrate calibrate any Trigger a one-shot calibration of the measurement.
data.measurement measurement any Push a raw measurement (analog: number; digital: per-channel object).

Payload-shape rules

Mode Accepted Rejected (logs warn)
analog number; numeric string (trimmed, non-empty, parses with Number) object payload (hint: "Switch Input Mode to 'digital' …"); non-numeric string
digital object { key1: number, key2: number, &hellip; } — keys must match config.channels[*].key number (hint: "Switch Input Mode to 'analog' …"); array; any non-object

Unknown channel keys in a digital payload are collected and reported at debug level via digital payload contained unmapped keys: <list>.

Source / mode allow-lists

Note

TODO: measurement does not appear to implement a flowController-style action/source allow-list (consult src/specificClass.js); it relies on the topic registry's typeof checks. If a future hardening pass adds mode-source gating, fold the table in here.


Data model — getOutput() shape

Source: src/specificClass.js getOutput() / getDigitalOutput() and src/channel.js getOutput(). Delta-compressed by outputUtils.formatMsg: consumers see only the keys that changed.

Analog mode (Measurement.getOutput())

Key Type Unit Notes
mAbs number scaling units (asset.unit / general.unit) Latest output value after offset + scaling + smoothing. Rounded to 2 dp.
mPercent number % Output mapped to interpolation.percentMin..percentMax. Rounded to 2 dp.
totalMinValue number scaling units Rolling minimum of the post-offset, pre-smoothing values. Reported as 0 until the first sample.
totalMaxValue number scaling units Rolling maximum of the same. Reported as 0 until the first sample.
totalMinSmooth number scaling units Rolling minimum of the smoothed output. Starts at 0.
totalMaxSmooth number scaling units Rolling maximum of the smoothed output. Starts at 0.

Digital mode (Measurement.getDigitalOutput())

{
  "channels": {
    "<channel.key>": {
      "key":            "<channel.key>",
      "type":           "<channel.type>",
      "position":       "<channel.position>",
      "unit":           "<channel.unit>",
      "mAbs":           <number>,
      "mPercent":       <number>,
      "totalMinValue":  <number>,
      "totalMaxValue":  <number>,
      "totalMinSmooth": <number>,
      "totalMaxSmooth": <number>
    }
    // ... one entry per channel that has produced output
  }
}

Status badge

Measurement.getStatusBadge():

Mode Badge text Fill / shape
analog <mAbs> <unit> (e.g. 0.42 bar) green / dot
digital digital · <N> channel(s) blue / ring

The legacy source.emitter fires 'mAbs' (analog only) and is kept for the editor status badge during the refactor window — see Limitations.


Events emitted on source.measurements.emitter

The shared MeasurementContainer fires <type>.measured.<position> whenever a Channel's rounded output changes. The type / position come from:

  • analog: config.asset.type and config.functionality.positionVsParent.
  • digital: per-channel config.channels[i].type and config.channels[i].position (falls back to the node-level positionVsParent when missing).

Position labels are normalised to lowercase in the event name (upstream, downstream, atequipment). Examples:

  • pressure.measured.upstream
  • flow.measured.atequipment
  • level.measured.downstream
  • temperature.measured.atequipment

Parents subscribe through the generic child.measurements.emitter.on(eventName, &hellip;) handshake established by childRegistrationUtils (in generalFunctions).

In digital mode one input message can fan out into several events — one per channel that accepted a value on that tick.


Configuration schema — editor form to config keys

Source of truth: generalFunctions/src/configs/measurement.json plus nodeClass.buildDomainConfig. Defaults below come from the schema.

General (config.general)

Form field Config key Default Notes
Name general.name Sensor Human-readable label.
(auto-assigned) general.id null Node-RED node id.
Default unit general.unit unitless Falls back to the asset 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
Software type functionality.softwareType measurement Constant.
Role functionality.role Sensor Constant.
Position vs parent functionality.positionVsParent atEquipment One of atEquipment / upstream / downstream. Used in the child.register payload and as the suffix of the measurement event name.
Distance offset functionality.distance null Optional spatial offset; sent with child.register.

Asset (config.asset)

Form field Config key Default Notes
Asset UUID asset.uuid null Globally-unique identifier.
Tag code / number asset.tagCode / asset.tagNumber null Asset-registry identifiers.
Geolocation asset.geoLocation {x:0, y:0, z:0}
Supplier asset.supplier Unknown Free text.
Category asset.category sensor sensor / measurement.
Asset type asset.type pressure Required. Matches the type axis on MeasurementContainer and the parent's filter (e.g. flow, power, temperature).
Model asset.model Unknown Free text.
Asset unit asset.unit unitless Output unit label for the measurement event payload.
Accuracy asset.accuracy null Optional sensor accuracy.
Repeatability asset.repeatability null Optional repeatability metric.

Important

asset.type must match the exact string the parent listens for. The parent's filter is typically the bare type (flow, pressure, power, …) — a measurement configured as flow-electromagnetic will not register with a flow-only filter on its parent (see Limitations).

Scaling (config.scaling)

Form field Config key Default Notes
Scaling enabled scaling.enabled false When false, the input is passed through with only the offset applied.
Input min/max scaling.inputMin / scaling.inputMax 0 / 1 Source range; clamps the input before mapping.
Output min/max scaling.absMin / scaling.absMax 50 / 100 Target range.
Offset scaling.offset 0 Added before scaling; mutated by cmd.calibrate.

Smoothing (config.smoothing)

Form field Config key Default Notes
Window size smoothing.smoothWindow 10 >= 1. Rolling buffer length.
Method smoothing.smoothMethod mean One of none / mean / min / max / sd / median / weightedMovingAverage / lowPass / highPass / bandPass / kalman / savitzkyGolay.

Outlier detection (config.outlierDetection)

Form field Config key Default Notes
Enabled outlierDetection.enabled false Toggle with set.outlier-detection.
Method outlierDetection.method zScore One of zScore / iqr / modifiedZScore.
Threshold outlierDetection.threshold 3 Method-specific (e.g. z > 3, mz > 3.5).

Simulation (config.simulation)

Form field Config key Default Notes
Enabled simulation.enabled false When true, tick() (1000 ms) drives inputValue via Simulator.step().
Safe calibration time simulation.safeCalibrationTime 100 ms before calibration is finalised in sim mode.

Interpolation (config.interpolation)

Form field Config key Default Notes
Percent min interpolation.percentMin 0 Lower bound of the mPercent output.
Percent max interpolation.percentMax 100 Upper bound.

Calibration (config.calibration)

Form field Config key Default Notes
Stability threshold calibration.stabilityThreshold 0.01 Absolute stdDev ceiling (in scaling-units) below which the buffer is considered stable. Fits the default [50,100] range; tighten / relax for your sensor.

Mode (config.mode)

Form field Config key Default Notes
Input mode mode.current analog analog (one channel, scalar payload) or digital (N channels, object payload).

Channels (config.channels[] — digital only)

In digital mode, each entry in config.channels defines its own pipeline:

Field Required Falls back to
key yes — (skipped if missing)
type yes — (skipped if missing)
position no config.functionality.positionVsParentatEquipment
unit no config.asset.unitunitless
distance no config.functionality.distancenull
scaling no {enabled:false, inputMin:0, inputMax:1, absMin:0, absMax:1, offset:0}
smoothing no config.smoothing
outlierDetection no config.outlierDetection
interpolation no config.interpolation

Invalid entries (missing key or type) are logged and skipped. An empty config.channels[] in digital mode logs digital mode enabled but config.channels is empty; no channels will be emitted.

Asset registration (config.assetRegistration)

Used by the /measurement/asset-reg admin endpoint to register / sync the asset with the upstream asset registry. Not part of the runtime data path.

Form field Config key Default Notes
Profile / location / process ids assetRegistration.{profileId, locationId, processId} 1 Free integer ids in the asset registry.
Status assetRegistration.status actief Lifecycle status.
Child assets assetRegistration.childAssets [] List of child asset ids.

Output (config.output)

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

Unit policy

Note

TODO: measurement does not currently declare a unitPolicy block on its BaseDomain configuration (unlike rotatingMachine). The per-channel unit is carried verbatim into the MeasurementContainer write at _writeOutput. If a future hardening pass adds a unit-policy enforcement, add the canonical / output / required-unit table here. See CONTRACT.md for the current invariants.


Child registration

Source: src/specificClass.js configure (registers itself via the BaseDomain plumbing) and the standard childRegistrationUtils handshake in generalFunctions.

measurement does not accept children. It only registers itself as a child on its upstream parent.

Layer Direction Topic / event Payload
Startup (Port 2) child → parent registerChild {topic: 'registerChild', payload: <node.id>, positionVsParent, distance}
Runtime child → parent <asset.type>.measured.<positionVsParent> on child.measurements.emitter {value, timestamp, unit, distance?} (per MeasurementContainer.value())
What softwareType payload Side-effect on parent
Registration measurement Parent attaches a listener for <asset.type>.measured.<positionVsParent> on the child's measurements.emitter.
Subsequent updates event on child.measurements.emitter Parent mirrors the value into its own MeasurementContainer slot.

Position labels are normalised to lowercase in the event name (upstream, downstream, atequipment); the positionVsParent field in the register payload is sent as configured (preserves case).


Page Why
Home Intuitive overview
Reference — Architecture Code map + per-Channel pipeline + lifecycle
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