znetsixe d6f8af4395 fix(editor): make Input Mode the top-level switch, hide wrong-mode fields
Prior behaviour: the Mode dropdown existed but nothing consumed it in the
editor — analog fields (Scaling, Source Min/Max, Smoothing, …) were
always visible, and the Channels JSON editor was always visible too.
For a legacy node with no saved mode the dropdown defaulted blank so
users reported "I cant even select digital or analog".

Changes:
- Initialize the Mode <select> from node.mode with an 'analog' fallback
  for legacy nodes (safe default — matches pre-digital behaviour).
- Wrap analog-only fields and digital-only fields in labelled containers
  and toggle their display based on the selected mode. Mode change is
  live — no redeploy needed to see the right form.
- Inline hint under the Mode dropdown tells the user what payload shape
  is expected for the current mode.
- Channels JSON gets live validation — shows channel count + names on
  valid JSON, warns on missing key/type, errors on invalid JSON.
- Label function appends ' [digital]' so the node visibly differs in a
  flow from an analog sibling.
- oneditsave is mode-aware: only warns about incomplete scaling ranges
  in analog mode; in digital mode warns if the channels array is empty
  or unparseable.

Runtime friendliness:
- nodeClass node-status now shows 'digital · N channel(s)' on startup in
  digital mode, and 'digital · N/M ch updated' after each incoming msg
  so the editor has a live heartbeat even when there is no single scalar.
- When analog mode receives an object payload (or digital receives a
  number), the node logs an actionable warn suggesting the mode switch
  instead of silently dropping the message.

Explicit, not auto-detected: mode remains a deployment-time choice
because the two modes take different editor config (scaling/smoothing vs
channels map). Auto-detecting at runtime would leave the node
unconfigured in whichever mode the user hadn't anticipated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 14:00:34 +02:00
2026-01-29 10:22:20 +01:00

measurement

Node-RED custom node for sensor signal conditioning. Takes raw input — either a single scalar (analog mode) or an MQTT-style JSON object with many keys (digital mode) — and produces scaled, smoothed, outlier-filtered measurements. Part of the EVOLV wastewater-automation platform.

Registers itself on port 2 as a child of a parent equipment (rotatingMachine, pumpingStation, reactor, etc.). The parent consumes measurements via shared MeasurementContainer events.

Install

cd ~/.node-red
npm install github:gitea.wbd-rd.nl/RnD/measurement

Or pull the whole platform via the superproject. Restart Node-RED and the node appears in the palette under EVOLV.

Two input modes

Analog mode (default)

One scalar per message — the classic PLC / 4-20mA pattern.

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

The node runs one offset → scaling → smoothing → outlier pipeline and emits exactly one MeasurementContainer slot. Every existing flow built before digital mode keeps working unchanged.

Digital mode (MQTT / IoT)

One object per message, many keys:

{ "topic": "measurement",
  "payload": { "temperature": 22.5, "humidity": 45, "pressure": 1013 } }

Each key maps to its own channel with independently-configured scaling, smoothing, outlier detection, type, position, unit, and distance. A single inbound message therefore emits N MeasurementContainer slots — one per channel — so a downstream parent sees everything at once.

Pick the mode in the editor or via msg.mode. Analog is the default; digital requires populating channels (see Configuration).

Input topics

Topic Payload Effect
measurement analog mode: number or numeric string — stored as inputValue and consumed on the next tick. digital mode: object keyed by channel names. drives the pipeline
simulator toggles the simulator flag
outlierDetection toggles outlier detection
calibrate adjust the scaling offset so current output matches inputMin (scaling on) or absMin (scaling off). Requires a stable window.

Output ports

Port Label Payload
0 process analog: {mAbs, mPercent, totalMinValue, totalMaxValue, totalMinSmooth, totalMaxSmooth}. digital: {channels: {<key>: {mAbs, mPercent, ...}}}. Delta-compressed — only changed fields emit each tick.
1 dbase InfluxDB line-protocol telemetry
2 parent {topic:"registerChild", payload:<nodeId>, positionVsParent, distance} emitted once ~180ms after deploy

Configuration

Common (both modes)

  • Asset (menu): supplier, category, assetType (measurement type in the container — pressure, flow, temperature, power, or any user-defined type like humidity), model, unit.
  • Logger (menu): log level + enable flag.
  • Position (menu): upstream / atEquipment / downstream relative to parent; optional distance offset.

Analog-mode fields

Field Purpose
Scaling (checkbox) enables linear source→process interpolation
Source Min / Max input-side range (e.g. 420 mA)
Input Offset additive bias applied before scaling
Process Min / Max output-side range (e.g. 03000 mbar)
Simulator (checkbox) internal random-walk source
Smoothing one of: none, mean, min, max, sd, lowPass, highPass, weightedMovingAverage, bandPass, median, kalman, savitzkyGolay
Window sample count for the smoothing window

Digital-mode fields

  • Mode: set to digital.
  • Channels: JSON array, one entry per channel. Each entry:
{
  "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 — the node falls back to the top-level analog-mode equivalents when missing. key is the JSON field name inside msg.payload; type is the MeasurementContainer axis (can be any string — unknown types are accepted).

State and emit contract

Every channel runs the same pipeline: outlier → offset → scaling → smoothing → min/max tracking → constrain → emit. Output is rounded to two decimals. MeasurementContainer events follow the pattern <type>.<variant>.<position> all lowercase, e.g. temperature.measured.atequipment.

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. Known types still validate strictly.

Testing

cd nodes/measurement
npm test

71 tests cover every smoothing method, every outlier strategy, scaling, interpolation, constrain, calibration, stability, simulation, output-percent fallback, per-channel pipelines, digital payload dispatch, registration events, and example-flow shape.

Production status

Last reviewed 2026-04-13. See the project memory file node_measurement.md for the current verdict, benchmarks, and wishlist.

License

SEE LICENSE. Author: Rene De Ren, Waterschap Brabantse Delta R&D.

Description
No description provided
Readme 186 KiB
Languages
JavaScript 83.7%
HTML 16.3%