diff --git a/measurement.html b/measurement.html index ec4ad7b..a80bc50 100644 --- a/measurement.html +++ b/measurement.html @@ -67,62 +67,110 @@ icon: "font-awesome/fa-sliders", label: function () { - return (this.positionIcon || "") + " " + (this.assetType || "Measurement"); + const modeTag = this.mode === 'digital' ? ' [digital]' : ''; + return (this.positionIcon || "") + " " + (this.assetType || "Measurement") + modeTag; }, oneditprepare: function() { + const node = this; + + // === Mode selector — TOP-LEVEL hierarchy === + // The Input Mode drives whether analog-pipeline fields or the + // digital channels editor are shown. Initialize the +
-
- - -
Digital mode only. One entry per payload key. See README for schema.
+ +
+
+ + +
One entry per payload key. Each channel has its own type / position / unit / scaling / smoothing / outlier detection. See README for the full schema.
+
+
-
+ +
+
+ +
+ + + Enable input scaling? +
- -
- - - Enable input scaling? -
+ +
+ + +
- -
- - -
+
+ + +
-
- - -
+ +
+ + +
- -
- - -
+ +
+ + +
+
+ + +
- -
- - -
-
- - -
+ +
+ + + Activate internal simulation? +
- -
- - - Activate internal simulation? -
+ +
+ + +
- -
- - -
- - -
- - -
Number of samples for smoothing
+ +
+ + +
Number of samples for smoothing
+

diff --git a/src/nodeClass.js b/src/nodeClass.js index df7b60f..2893b47 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -97,10 +97,18 @@ class nodeClass { */ _bindEvents() { + // Analog mode: the classic 'mAbs' event pushes a green dot with the + // current value + unit to the editor. this.source.emitter.on('mAbs', (val) => { this.node.status({ fill: 'green', shape: 'dot', text: `${val} ${this.config.general.unit}` }); }); - + + // Digital mode: summarise how many channels have ticked a value. + // This runs on every accepted channel update so the editor shows live + // activity instead of staying blank when no single scalar exists. + if (this.source.mode === 'digital') { + this.node.status({ fill: 'blue', shape: 'ring', text: `digital · ${this.source.channels.size} channel(s)` }); + } } /** @@ -168,7 +176,16 @@ class nodeClass { // digital -> object payload keyed by channel name if (this.source.mode === 'digital') { if (msg.payload && typeof msg.payload === 'object' && !Array.isArray(msg.payload)) { - this.source.handleDigitalPayload(msg.payload); + const summary = this.source.handleDigitalPayload(msg.payload); + // Summarise what actually got accepted on the node status so + // the editor shows a heartbeat per message. + const accepted = Object.values(summary).filter((s) => s.ok).length; + const total = Object.keys(summary).length; + this.node.status({ fill: 'green', shape: 'dot', + text: `digital · ${accepted}/${total} ch updated` }); + } else if (typeof msg.payload === 'number') { + // Helpful hint: the user probably configured the wrong mode. + this.source.logger?.warn(`digital mode received a number (${msg.payload}); expected an object like {key: value, ...}. Switch Input Mode to 'analog' in the editor or send an object payload.`); } else { this.source.logger?.warn(`digital mode expects an object payload; got ${typeof msg.payload}`); } @@ -180,6 +197,11 @@ class nodeClass { } else { this.source.logger?.warn(`Invalid numeric measurement payload: ${msg.payload}`); } + } else if (msg.payload && typeof msg.payload === 'object' && !Array.isArray(msg.payload)) { + // Helpful hint: the payload is object-shaped but the node is + // configured analog. Most likely the user wanted digital mode. + const keys = Object.keys(msg.payload).slice(0, 3).join(', '); + this.source.logger?.warn(`analog mode received an object payload (keys: ${keys}). Switch Input Mode to 'digital' in the editor and define channels, or feed a numeric payload.`); } } break;