Files
EVOLV/wiki/manuals/nodes/measurement.md
znetsixe 0300a76ae8
Some checks failed
CI / lint-and-test (push) Has been cancelled
docs: measurement trial-ready — digital mode + dispatcher fix + 71 tests
Bumps:
- nodes/generalFunctions  75d16c6 -> e50be2e  (permissive unit check + measurement schema additions)
- nodes/measurement       f7c3dc2 -> 495b4cf  (digital mode + dispatcher fix + 59 new tests + rewritten README + UI)

Wiki:
- wiki/manuals/nodes/measurement.md — new user manual covering analog and
  digital modes, topic reference, smoothing/outlier methods, unit policy,
  and the pre-fix dispatcher bug advisory.
- wiki/sessions/2026-04-13-measurement-digital-mode.md — session note with
  findings, fix scope, test additions, and dual-mode E2E results.
- wiki/index.md — links both pages and adds the missing 2026-04-13
  rotatingMachine session entry that was omitted from the earlier commit.

Status: measurement is now trial-ready in both analog and digital modes.
71/71 unit tests green (was 12), dual-mode E2E on live Dockerized
Node-RED verifies analog regression and a three-channel MQTT-style
payload (temperature/humidity/pressure) dispatching independently with
per-channel smoothing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:46:00 +02:00

8.8 KiB

title, node, updated, status
title node updated status
measurement — User Manual measurement 2026-04-13 trial-ready

measurement — User Manual

The measurement node is the sensor-side of every EVOLV flow. It takes raw signal data, applies offset / scaling / smoothing / outlier rejection, and publishes a conditioned value into the shared MeasurementContainer. A parent equipment node (rotatingMachine, pumpingStation, reactor, ...) subscribes automatically via the child-registration handshake on port 2.

At a glance

Item Value
Node category EVOLV
Inputs 1 (message-driven)
Outputs 3 — process / dbase / parent
Tick period 1 s
Input modes analog (default) — one scalar per msg. digital — object payload with many keys.
Smoothing methods 12 (none, mean, min, max, sd, lowPass, highPass, weightedMovingAverage, bandPass, median, kalman, savitzkyGolay)
Outlier methods 3 (zScore, iqr, modifiedZScore)

Choosing a mode

Analog — one scalar per message (PLC / 4-20 mA)

The classic pattern — what the node did before v1.1. msg.payload is a single number. The node runs one offset → scaling → smoothing → outlier pipeline and emits exactly one MeasurementContainer slot keyed by the asset's type + position.

{ "topic": "measurement", "payload": 12.34 }

Use when one Node-RED measurement node represents one physical sensor.

Digital — object payload, many channels (MQTT / IoT / JSON)

Use when one Node-RED measurement node represents one physical device that publishes multiple readings. Common shapes:

{ "topic": "measurement",
  "payload": { "temperature": 22.5, "humidity": 45, "pressure": 1013 } }
{ "topic": "measurement",
  "payload": { "co2": 618, "voc": 122, "pm25": 8 } }

Each top-level key maps to a channel with its own type, position, unit, and pipeline parameters. Unknown keys are ignored (logged at debug).

Configuration

Common (both modes)

  • Asset (menu): supplier, category, asset type (assetType), model, unit.
  • Logger (menu): log level + enable flag.
  • Position (menu): upstream / atEquipment / downstream, optional distance offset.

Analog fields

Field Meaning
Scaling enables linear interpolation from source range to process range
Source Min / Max raw input bounds (e.g. 4 / 20 for mA)
Input Offset additive bias applied before scaling
Process Min / Max mapped output bounds (e.g. 0 / 3000 for mbar)
Simulator internal random-walk source for testing
Smoothing method (dropdown)
Window smoothing window size

Digital fields

  • Input Mode: set to digital in the dropdown.
  • Channels (JSON): array of channel definitions.

Each channel:

{
  "key":      "temperature",
  "type":     "temperature",
  "position": "atEquipment",
  "unit":     "C",
  "scaling":  { "enabled": false, "inputMin": 0, "inputMax": 1, "absMin": -50, "absMax": 150, "offset": 0 },
  "smoothing": { "smoothWindow": 5, "smoothMethod": "mean" },
  "outlierDetection": { "enabled": true, "method": "zScore", "threshold": 3 }
}

scaling / smoothing / outlierDetection are optional — missing sections inherit the top-level analog-mode fields. key is the JSON field name inside msg.payload; type is the MeasurementContainer axis — any string works, not just the physical-unit-backed defaults.

Input topics

Topic Payload Effect
measurement number (analog) / object (digital) drives the pipeline
simulator toggle the internal random-walk simulator
outlierDetection toggle outlier rejection
calibrate set the offset so the current output matches Source Min (scaling on) or Process Min (scaling off). Requires a stable window — aborts if the signal is fluctuating.

Output ports

Port 0 — process

Delta-compressed payload.

Analog shape:

{ "mAbs": 4.2, "mPercent": 42, "totalMinValue": 0, "totalMaxValue": 100,
  "totalMinSmooth": 0, "totalMaxSmooth": 4.2 }

Digital shape:

{ "channels": {
    "temperature": { "key": "temperature", "type": "temperature", "position": "atEquipment",
                     "unit": "C", "mAbs": 24, "mPercent": 37,
                     "totalMinValue": 22.5, "totalMaxValue": 25.5,
                     "totalMinSmooth": 22.5, "totalMaxSmooth": 24 },
    "humidity":    { ... },
    "pressure":    { ... }
} }

Port 1 — dbase

InfluxDB line-protocol telemetry. Tags = asset metadata; fields = measurements. See InfluxDB Schema Design.

Port 2 — parent

{ topic: "registerChild", payload: <nodeId>, positionVsParent, distance } — emitted once ~200 ms after deploy so the parent equipment node registers this sensor.

Pipeline per value

  1. Outlier check (if enabled) — rejects via zScore / IQR / modifiedZScore. Rejected values never advance, don't update min/max, don't emit.
  2. Offsetvalue + scaling.offset.
  3. Scaling (if enabled) — linear interpolation from [inputMin, inputMax] to [absMin, absMax] with boundary clamping.
  4. Smoothing — current value pushed into the rolling window; the configured method produces the smoothed output.
  5. Min/Max tracking — both raw (pre-smoothing) and smoothed min/max tracked for display.
  6. Constrain — smoothed value clamped to [absMin, absMax].
  7. EmitMeasurementContainer.type(...).variant('measured').position(...).distance(...).value(out, ts, unit) triggers the event <type>.measured.<position> (lowercase) that the parent equipment subscribes to.

In digital mode, each channel runs this pipeline independently.

Smoothing methods — quick reference

Method Use case
none pass raw value through — useful for testing
mean simple arithmetic average over window
min / max worst-case / peak reporting
sd outputs standard deviation (noise indicator)
median outlier-resistant central tendency
weightedMovingAverage later samples weighted higher
lowPass EMA-style attenuation of high-frequency noise
highPass emphasises rapid changes (step detection)
bandPass lowPass + highPass - raw — band-of-interest filtering
kalman recursive noise filter, converges to steady value
savitzkyGolay polynomial smoothing over 5-point window

Outlier methods — quick reference

Method Best when
zScore signal is approximately normal; threshold = # of SDs
iqr signal is non-normal; robust to skewed distributions
modifiedZScore small samples; uses median / MAD instead of mean / SD

Historical bug fixed 2026-04-13: The dispatcher compared against camelCase keys (lowPass, zScore, ...) but the validator lowercases enum values. Result: 4 smoothing methods and 2 outlier methods were silently no-ops when chosen from the editor — they fell through to the "unknown" branch and emitted the raw last value. Review any flow deployed before 2026-04-13 that relied on these methods.

Unit policy

Unknown measurement types (anything not in the container's built-in measureMap: pressure, flow, power, temperature, volume, length, mass, energy) are accepted without unit compatibility checks. This lets digital channels use humidity (%), co2 (ppm), arbitrary IoT units. Known types still validate strictly.

Example flow (digital)

[
  { "id": "dig", "type": "measurement",
    "mode": "digital",
    "channels": "[{\"key\":\"temperature\",\"type\":\"temperature\",\"position\":\"atEquipment\",\"unit\":\"C\",\"scaling\":{\"enabled\":false,\"absMin\":-50,\"absMax\":150},\"smoothing\":{\"smoothWindow\":5,\"smoothMethod\":\"mean\"}},{\"key\":\"humidity\",\"type\":\"humidity\",\"position\":\"atEquipment\",\"unit\":\"%\",\"scaling\":{\"enabled\":false,\"absMin\":0,\"absMax\":100},\"smoothing\":{\"smoothWindow\":5,\"smoothMethod\":\"mean\"}}]",
    ...
  }
]

Testing

cd nodes/measurement
npm test

71 tests — coverage includes every smoothing method, every outlier strategy, scaling, interpolation, constrain, calibration, stability, simulation, per-channel pipelines, digital-mode dispatch, malformed-channel handling, event emits.

End-to-end benchmark scripts live in the superproject at /tmp/m_e2e_baseline.py (analog) and /tmp/m_digital_e2e.py (digital). Run against a Dockerized Node-RED stack (docker compose up -d nodered).

Production status

Trial-ready as of 2026-04-13 after the session that fixed the silent dispatcher bug and added digital mode. See session 2026-04-13 and the memory file node_measurement.md.