The previous oneditprepare ran applyMode(initialMode) early in the function, which called validateChannelsJson(), which referenced const declarations (channelsArea, channelsHint) that were declared later in the same function. JavaScript hoists const into the Temporal Dead Zone, so accessing them before the declaration line throws a ReferenceError. That uncaught throw aborted the rest of oneditprepare — including the waitForMenuData() call that initialises the asset / logger / position menu placeholders. Symptom for the user: opening a measurement node in the editor showed Mode + analog fields but the asset menu was empty. Fixes: 1. Move waitForMenuData() to the very top of oneditprepare so the shared menu init is independent of any later mode-block work. Even if the mode logic ever throws again, the asset / logger / position menus still render. 2. Resolve every DOM reference (modeSelect, analogBlock, digitalBlock, modeHint, channelsArea, channelsHint) at the top of the function before any helper that touches them is invoked. validateChannelsJson and applyMode now read closed-over names that are guaranteed to be initialised. 3. Guard applyMode(initialMode) with try/catch as defense in depth and add null-checks on every DOM reference. A future template change that drops one of the IDs will only no-op rather than break the editor. No runtime change. 71/71 tests still green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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 likehumidity), model, unit. - Logger (menu): log level + enable flag.
- Position (menu):
upstream/atEquipment/downstreamrelative 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. 4–20 mA) |
Input Offset |
additive bias applied before scaling |
Process Min / Max |
output-side range (e.g. 0–3000 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.