Pull in RBE node and push out tail

This commit is contained in:
Dave Conway-Jones 2021-04-19 13:09:13 +01:00
parent c021b4c368
commit 73063df11b
No known key found for this signature in database
GPG Key ID: 88BA2B8A411BE9FF
12 changed files with 921 additions and 7 deletions

View File

@ -60,9 +60,6 @@
"multer": "1.4.2",
"mustache": "4.2.0",
"node-red-admin": "^0.2.6",
"node-red-node-rbe": "^0.5.0",
"node-red-node-sentiment": "^0.1.6",
"node-red-node-tail": "^0.3.0",
"nopt": "5.0.0",
"oauth2orize": "1.11.0",
"on-headers": "1.0.2",

View File

@ -0,0 +1,93 @@
<script type="text/html" data-template-name="rbe">
<div class="form-row">
<label for="node-input-func"><i class="fa fa-wrench"></i> <span data-i18n="rbe.label.func"></span></label>
<select type="text" id="node-input-func" style="width:70%;">
<option value="rbe" data-i18n="rbe.opts.rbe"></option>
<option value="rbei" data-i18n="rbe.opts.rbei"></option>
<option value="deadbandEq" data-i18n="rbe.opts.deadbandEq"></option>
<option value="deadband" data-i18n="rbe.opts.deadband"></option>
<option value="narrowbandEq" data-i18n="rbe.opts.narrowbandEq"></option>
<option value="narrowband" data-i18n="rbe.opts.narrowband"></option>
</select>
</div>
<div class="form-row" id="node-bandgap">
<label for="node-input-gap">&nbsp;</label>
<input type="text" id="node-input-gap" data-i18n="[placeholder]rbe.placeholder.bandgap" style="width:95px;">
<select type="text" id="node-input-inout" style="width:54%;">
<option value="out" data-i18n="rbe.opts.out"></option>
<option value="in" data-i18n="rbe.opts.in"></option>
</select>
</div>
<div class="form-row" id="node-startvalue">
<label for="node-input-start"><i class="fa fa-thumb-tack"></i> <span data-i18n="rbe.label.start"></span></label>
<input type="text" id="node-input-start" data-i18n="[placeholder]rbe.placeholder.start" style="width:70%;">
</div>
<div class="form-row">
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
<input type="text" id="node-input-property" style="width:70%;"/>
</div>
<div class="form-row">
<label> </label>
<input type="checkbox" id="node-input-septopics" style="display:inline-block; width:20px; vertical-align:baseline;">
<span data-i18n="rbe.label.septopics"></span> <input type="text" id="node-input-topi" style="width:27%;"/>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="rbe.label.name"></span></label>
<input type="text" id="node-input-name" data-i18n="[placeholder]rbe.label.name" style="width:70%;">
</div>
</script>
<script type="text/javascript">
RED.nodes.registerType("rbe", {
color:"#E2D96E",
category: 'function',
defaults: {
name: {value:""},
func: {value:"rbe"},
gap: {value:"",validate:RED.validators.regex(/^(\d*[.]*\d*|)(%|)$/)},
start: {value:""},
inout: {value:"out"},
septopics: {value:true},
property: {value:"payload",required:true},
topi: {value:"topic",required:true}
},
inputs:1,
outputs:1,
icon: "rbe.png",
paletteLabel: "filter",
label: function() {
var ll = (this.func||"").replace("Eq","").replace("rbei",this._("rbe.rbe")).replace("rbe",this._("rbe.rbe"))||this._("rbe.rbe");
return this.name||ll||this._("rbe.rbe");
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
oneditprepare: function() {
if (this.property === undefined) {
$("#node-input-property").val("payload");
}
if (this.septopics === undefined) {
$("#node-input-septopics").prop('checked', true);
}
$("#node-input-property").typedInput({default:'msg',types:['msg']});
$("#node-input-topi").typedInput({default:'msg',types:['msg']});
//$( "#node-input-gap" ).spinner({min:0});
if ($("#node-input-inout").val() === null) {
$("#node-input-inout").val("out");
}
$("#node-input-func").on("change",function() {
if (($("#node-input-func").val() === "rbe")||($("#node-input-func").val() === "rbei")) {
$("#node-bandgap").hide();
} else {
$("#node-bandgap").show();
}
if (($("#node-input-func").val() === "narrowband")||($("#node-input-func").val() === "narrowbandEq")) {
$("#node-startvalue").show();
} else {
$("#node-startvalue").hide();
}
});
}
});
</script>

View File

@ -0,0 +1,97 @@
module.exports = function(RED) {
"use strict";
function RbeNode(n) {
RED.nodes.createNode(this,n);
this.func = n.func || "rbe";
this.gap = n.gap || "0";
this.start = n.start || '';
this.inout = n.inout || "out";
this.pc = false;
if (this.gap.substr(-1) === "%") {
this.pc = true;
this.gap = parseFloat(this.gap);
}
this.g = this.gap;
this.property = n.property || "payload";
this.topi = n.topi || "topic";
this.septopics = true;
if (n.septopics !== undefined && n.septopics === false) {
this.septopics = false;
}
var node = this;
node.previous = {};
this.on("input",function(msg) {
var topic;
try {
topic = RED.util.getMessageProperty(msg,node.topi);
}
catch(e) { }
if (msg.hasOwnProperty("reset")) {
if (node.septopics && topic && (typeof topic === "string") && (topic !== "")) {
delete node.previous[msg.topic];
}
else { node.previous = {}; }
}
var value = RED.util.getMessageProperty(msg,node.property);
if (value !== undefined) {
var t = "_no_topic";
if (node.septopics) { t = topic || t; }
if ((this.func === "rbe") || (this.func === "rbei")) {
var doSend = (this.func !== "rbei") || (node.previous.hasOwnProperty(t)) || false;
if (typeof(value) === "object") {
if (typeof(node.previous[t]) !== "object") { node.previous[t] = {}; }
if (!RED.util.compareObjects(value, node.previous[t])) {
node.previous[t] = RED.util.cloneMessage(value);
if (doSend) { node.send(msg); }
}
}
else {
if (value !== node.previous[t]) {
node.previous[t] = RED.util.cloneMessage(value);
if (doSend) { node.send(msg); }
}
}
}
else {
var n = parseFloat(value);
if (!isNaN(n)) {
if ((typeof node.previous[t] === 'undefined') && (this.func === "narrowband")) {
if (node.start === '') { node.previous[t] = n; }
else { node.previous[t] = node.start; }
}
if (node.pc) { node.gap = Math.abs(node.previous[t] * node.g / 100) || 0; }
else { node.gap = Number(node.gap); }
if ((node.previous[t] === undefined) && (node.func === "narrowbandEq")) { node.previous[t] = n; }
if (node.previous[t] === undefined) { node.previous[t] = n - node.gap - 1; }
if (Math.abs(n - node.previous[t]) === node.gap) {
if ((this.func === "deadbandEq")||(this.func === "narrowband")) {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}
}
else if (Math.abs(n - node.previous[t]) > node.gap) {
if (this.func === "deadband" || this.func === "deadbandEq") {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}
}
else if (Math.abs(n - node.previous[t]) < node.gap) {
if ((this.func === "narrowband")||(this.func === "narrowbandEq")) {
if (node.inout === "out") { node.previous[t] = n; }
node.send(msg);
}
}
if (node.inout === "in") { node.previous[t] = n; }
}
else {
node.warn(RED._("rbe.warn.nonumber"));
}
}
} // ignore msg with no payload property.
});
}
RED.nodes.registerType("rbe",RbeNode);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View File

@ -0,0 +1,40 @@
<script type="text/html" data-help-name="rbe">
<p>Report by Exception (RBE) - Daten-Weiterleitung nur bei Änderung der Nutzdaten (Payload).
Der Node kann auch blockieren oder weiterleiten, wenn die Wertänderung eine Grenze überschreitet (Totband- und Nahband-Modus).</p>
<h3>Eingangsdaten</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">number | string | (object)</span>
</dt>
<dd>Der RBE-Modus mit Prüfung auf Wertänderung akzeptiert Zahlen (numbers), Zeichenfolgen (string) und einfache Objekte (object).
Bei den anderen wertvergleichenden Modies müssen analysierbare (parseable) Zahlenwerte übergeben werden.</dd>
<dt class="optional">topic <span class="property-type">string</span>
</dt>
<dd>Wenn vorgegeben erfolgt die Auswertung separat für jedes Topic</dd>
<dt class="optional">reset<span class="property-type">any</span></dt>
<dd>Wenn gesetzt wird/werden der/die gespeicherte(n) Wert(e) rückgesetzt</dd>
</dl>
<h3>Ausgangsdaten</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">wie Eingangsdaten</span>
</dt>
<dd>Wenn Bedingung erfüllt, sind die Ausgangsdaten gleich den Eingangsdaten</dd>
</dl>
<h3>Details</h3>
<p>Im <i>RBE</i>-Modus mit Prüfung auf Wertänderung blockiert dieser Node die Datenweitergabe bis
<code>msg.payload</code> (oder die eingestellte Eigenschaft) verändert ist gegenüber dessen vorherigen Wert.
Wenn benötigt, wird der Anfangswert ignoriert, sodass beim Start nichts gesendet wird.</p>
<p>In den <i>Totband</i>-Modies werden die Eingangswerte geblockt,
<i>bis</i> die Wertänderung größer oder größer-gleich ist als &plusmn; des Bandes um den voherigen Wert.</p>
<p>In den <i>Nahband</i>-Modies werden die Eingangswerte geblockt,
<i>wenn</i> die Wertänderung größer oder größer-gleich ist als &plusmn; des Bandes um den voherigen Wert.
Dies ist beispielsweise nützlich, um Ausreißer eines fehlerhaften Sensors zu ignorieren.</p>
<p>In den Totband und Nahband-Modies müssen die Eingangswerte analysierbare (parseable) Zahlenwerte sein und
beide unterstützen auch % (prozentuale Angabe), d.h. der Node sendet nur, wenn der Eingangswert mehr als x% vom vorherigen Wert abweicht.</p>
<p>Die Totband- und Nahband-Modies erlauben den Vergleich entweder gegen den letzten gültigen Ausgangswert,
dieses zum Ignorieren von Werten außerhalb des gültigen Bereichs, oder gegen den des vorherigen Eingangswertes,
welches den Sollwert rücksetzt, was einen allmähligen Drift (Totband) oder einen eine schrittweise Veränderung (Nahband) ermöglicht.</p>
<p><b>Hinweis</b>: Dieser Node arbeitet auf per-<code>msg.topic</code>-Basis.
Dies bedeutet, dass ein einzelner rbe-Node mehrere verschiedene Topics parallel bearbeiten kann.</p>
</script>

View File

@ -1000,5 +1000,32 @@
"too-many": "Zu viele anstehende Nachrichten im <span style=\"background-color:Gainsboro\">batch</span>-Node",
"unexpected": "Unerwarteter Modus",
"no-parts": "Keine parts-Eigenschaft in Nachricht"
},
"rbe": {
"rbe": "filter",
"label": {
"func": "Modus",
"init": "Sende Anfangswert",
"start": "Startwert",
"name": "Name",
"septopics": "Modus für jedes msg.topic separat anwenden"
},
"placeholder":{
"bandgap": "z.B. 10 oder 5%",
"start": "Leer lassen, um erste empfangenen Daten zu nutzen"
},
"opts": {
"rbe": "Blockieren bis Wertänderung",
"rbei": "Blockieren bis Wertänderung (Anfangswert ignorieren)",
"deadband": "Blockieren bis Wertänderung ist größer als",
"deadbandEq": "Blockieren bis Wertänderung ist größer-gleich",
"narrowband": "Blockieren wenn Wertänderung ist größer als",
"narrowbandEq": "Blockieren wenn Wertänderung ist größer-gleich",
"in": "verglichen mit letzten Eingangswert",
"out": "verglichen mit letzten gültigen Ausgangswert"
},
"warn": {
"nonumber": "Keine Zahl gefunden in den Nutzdaten (Payload)"
}
}
}

View File

@ -0,0 +1,41 @@
<script type="text/html" data-help-name="rbe">
<p>Report by Exception (RBE) node - only passes on data if the payload has changed.
It can also block unless, or ignore if the value changes by a specified amount (Dead- and Narrowband mode).</p>
<h3>Inputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">number | string | (object)</span>
</dt>
<dd>RBE mode will accept numbers, strings, and simple objects.
Other modes must provide a parseable number.</dd>
<dt class="optional">topic <span class="property-type">string</span>
</dt>
<dd>if specified the function will work on a per topic basis. This property can be set by configuration.</dd>
<dt class="optional">reset<span class="property-type">any</span></dt>
<dd>if set clears the stored value for the specified msg.topic, or
all topics if msg.topic is not specified.</dd>
</dl>
<h3>Outputs</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">as per input</span>
</dt>
<dd>If triggered the output will be the same as the input.</dd>
</dl>
<h3>Details</h3>
<p>In RBE mode this node will block until the <code>msg.payload</code>,
(or selected property) value is different to the previous one.
If required it can ignore the intial value, so as not to send anything at start.</p>
<p>The <a href="https://en.wikipedia.org/wiki/Deadband" target="_blank">Deadband</a> modes will block the incoming value
<i>unless</i> its change is greater or greater-equal than &plusmn; the band gap away from a previous value.</p>
<p>The Narrowband modes will block the incoming value,
<i>if</i> its change is greater or greater-equal than &plusmn; the band gap away from the previous value.
It is useful for ignoring outliers from a faulty sensor for example.</p>
<p>Both in Deadband and Narrowband modes the incoming value must contain a parseable number and
both also supports % - only sends if/unless the input differs by more than x% of the original value.</p>
<p>Both Deadband and Narrowband allow comparison against either the previous valid output value, thus
ignoring any values out of range, or the previous input value, which resets the set point, thus allowing
gradual drift (deadband), or a step change (narrowband).</p>
<p><b>Note:</b> This works on a per <code>msg.topic</code> basis, though this can be changed to another property if desired.
This means that a single rbe node can handle multiple different topics at the same time.</p>
</script>

View File

@ -1001,5 +1001,32 @@
"too-many" : "too many pending messages in batch node",
"unexpected" : "unexpected mode",
"no-parts" : "no parts property in message"
},
"rbe": {
"rbe": "filter",
"label": {
"func": "Mode",
"init": "Send initial value",
"start": "Start value",
"name": "Name",
"septopics": "Apply mode separately for each "
},
"placeholder":{
"bandgap": "e.g. 10 or 5%",
"start": "leave blank to use first data received"
},
"opts": {
"rbe": "block unless value changes",
"rbei": "block unless value changes (ignore initial value)",
"deadband": "block unless value change is greater than",
"deadbandEq": "block unless value change is greater or equal to",
"narrowband": "block if value change is greater than",
"narrowbandEq": "block if value change is greater or equal to",
"in": "compared to last input value",
"out": "compared to last valid output value"
},
"warn": {
"nonumber": "no number found in payload"
}
}
}

View File

@ -0,0 +1,31 @@
<!-- Source revision: https://github.com/node-red/node-red-nodes/commit/467907776088422882076f46d85e25601449564d -->
<script type="text/html" data-help-name="rbe">
<p>Report by Exception(例外データの報告)ノード - ペイロードの値が変化した場合だけデータを送信。</p>
<p>値が指定した量変化するまでブロックすることもできます- 不感帯(deadband)モード。</p>
<h3>入力</h3> <dl class="message-properties">
<dt>payload
<span class="property-type">数値 | 文字列 | (オブジェクト)</span>
</dt>
<dd>RBEモードでは数値、文字列、シンプルなオブジェクトを受け付けます。他のモードではパース可能な数値を指定します。</dd>
<dt class="optional">topic <span class="property-type">文字列</span>
</dt>
<dd>指定すると、トピックごとに動作します。</dd>
<dt class="optional">reset<span class="property-type">任意</span></dt>
<dd>値を設定すると、保存した値をクリアします。msg.topicを指定した場合は対応する値、指定しなければ全てのトピックが対象となります。</dd>
</dl>
<h3>出力</h3>
<dl class="message-properties">
<dt>payload
<span class="property-type">入力と同じ</span>
</dt>
<dd>トリガー条件を満たした場合、入力を出力として送信。</dd>
</dl>
<h3>詳細</h3>
<p>RBEモードでは、<code>msg.payload</code>が前の値から変化するまでブロックします。</p>
<p>不感帯モードでは、入力データはパース可能な数値でなければなりません。以前の入力値に対する差分が指定値より大きな場合にのみメッセージを出力します。</p>
<p>不感帯モードでは%による指定もサポートしています。入力と前の値の差分がX%より大きな場合に出力を行います。</p>
<p>狭帯域(narrowband)モードでは、前の値に対する差分が一定値より大きな場合に入力ペイロードをブロックします。このモードは、故障したセンサから発生する外れ値を無視する時などに有用です。</p>
<p>不感帯モードと狭帯域モードでは、以前の有効出力値、もしくは、以前の入力値との比較ができます。有効出力値を用いると範囲外の値を無視することが、入力値を用いると設定点がリセットされるため漸次的変化(不感帯モード)もしくは段階的変化(狭帯域モード)が可能です。</p>
<p><b>注:</b> このノードは<code>msg.topic</code>毎に動作します。そのため、ひとつのrbeードで複数の異なるトピックを同時に扱うことができます。</p>
</script>

View File

@ -999,5 +999,30 @@
"too-many": "batchード内で保持しているメッセージが多すぎます",
"unexpected": "想定外のモード",
"no-parts": "メッセージにpartsプロパティがありません"
},
"rbe": {
"label": {
"func": "動作",
"init": "初期値を送付",
"start": "初期値",
"name": "名前"
},
"placeholder": {
"bandgap": "例:10、5%",
"start": "最初に受け取った値を用いる場合は空欄"
},
"opts": {
"rbe": "値が変化した時のみメッセージを中継",
"rbei": "値が変化した時のみメッセージを中継(初期値を無視)",
"deadband": "値が指定した変化量を超える時のみメッセージを中継",
"deadbandEq": "値が指定した変化量以上の時のみメッセージを中継",
"narrowband": "値が指定した変化量を超えない時のみメッセージを中継",
"narrowbandEq": "値が指定した変化量以上でない時のみメッセージを中継",
"in": "最後の入力値と比較",
"out": "最後の出力値と比較"
},
"warn": {
"nonumber": "ペイロードに数値が含まれていません"
}
}
}

View File

@ -40,15 +40,13 @@
"express": "4.17.1",
"fs-extra": "9.1.0",
"node-red-admin": "^0.2.6",
"node-red-node-rbe": "^0.5.0",
"node-red-node-tail": "^0.3.0",
"nopt": "5.0.0",
"semver": "7.3.5"
},
"optionalDependencies": {
"bcrypt": "3.0.6"
"bcrypt": "5.0.1"
},
"engines": {
"node": ">=8"
"node": ">=12"
}
}

View File

@ -0,0 +1,538 @@
var should = require("should");
var helper = require("node-red-node-test-helper");
var testNode = require("nr-test-utils").require("@node-red/nodes/core/function/rbe.js");
describe('rbe node', function() {
"use strict";
beforeEach(function(done) {
helper.startServer(done);
});
afterEach(function(done) {
helper.unload().then(function() {
helper.stopServer(done);
});
});
it("should be loaded with correct defaults", function(done) {
var flow = [{"id":"n1", "type":"rbe", "name":"rbe1", "wires":[[]]}];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
n1.should.have.property("name", "rbe1");
n1.should.have.property("func", "rbe");
n1.should.have.property("gap", "0");
done();
});
});
it('should only send output if payload changes - with multiple topics (rbe)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "a");
c+=1;
}
else if (c === 1) {
msg.should.have.a.property("payload", 2);
c+=1;
}
else if (c == 2) {
msg.should.have.a.property("payload");
msg.payload.should.have.a.property("b",1);
msg.payload.should.have.a.property("c",2);
c+=1;
}
else if (c == 3) {
msg.should.have.a.property("payload",true);
c+=1;
}
else if (c == 4) {
msg.should.have.a.property("payload",false);
c+=1;
}
else if (c == 5) {
msg.should.have.a.property("payload",true);
c+=1;
}
else if (c == 6) {
msg.should.have.a.property("topic","a");
msg.should.have.a.property("payload",1);
c+=1;
}
else if (c == 7) {
msg.should.have.a.property("topic","b");
msg.should.have.a.property("payload",1);
c+=1;
}
else {
c += 1;
msg.should.have.a.property("topic","c");
msg.should.have.a.property("payload",1);
done();
}
});
n1.emit("input", {payload:"a"});
n1.emit("input", {payload:"a"});
n1.emit("input", {payload:"a"});
n1.emit("input", {payload:2});
n1.emit("input", {payload:2});
n1.emit("input", {payload:{b:1,c:2}});
n1.emit("input", {payload:{c:2,b:1}});
n1.emit("input", {payload:{c:2,b:1}});
n1.emit("input", {payload:true});
n1.emit("input", {payload:false});
n1.emit("input", {payload:false});
n1.emit("input", {payload:true});
n1.emit("input", {topic:"a",payload:1});
n1.emit("input", {topic:"b",payload:1});
n1.emit("input", {topic:"b",payload:1});
n1.emit("input", {topic:"a",payload:1});
n1.emit("input", {topic:"c",payload:1});
});
});
it('should ignore multiple topics if told to (rbe)', function(done) {
var flow = [{id:"n1", type:"rbe", func:"rbe", gap:"0", septopics:false, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "a");
c+=1;
}
else if (c === 1) {
msg.should.have.a.property("payload", 2);
c+=1;
}
else if (c == 2) {
msg.should.have.a.property("payload");
msg.payload.should.have.a.property("b",1);
msg.payload.should.have.a.property("c",2);
c+=1;
}
else if (c == 3) {
msg.should.have.a.property("payload",true);
c+=1;
}
else if (c == 4) {
msg.should.have.a.property("payload",false);
c+=1;
}
else if (c == 5) {
msg.should.have.a.property("payload",true);
c+=1;
}
else if (c == 6) {
msg.should.have.a.property("topic","a");
msg.should.have.a.property("payload",1);
c+=1;
}
else {
msg.should.have.a.property("topic","a");
msg.should.have.a.property("payload",2);
done();
}
});
n1.emit("input", {topic:"a",payload:"a"});
n1.emit("input", {topic:"b",payload:"a"});
n1.emit("input", {topic:"c",payload:"a"});
n1.emit("input", {topic:"a",payload:2});
n1.emit("input", {topic:"b",payload:2});
n1.emit("input", {payload:{b:1,c:2}});
n1.emit("input", {payload:{c:2,b:1}});
n1.emit("input", {payload:{c:2,b:1}});
n1.emit("input", {topic:"a",payload:true});
n1.emit("input", {topic:"b",payload:false});
n1.emit("input", {topic:"c",payload:false});
n1.emit("input", {topic:"d",payload:true});
n1.emit("input", {topic:"a",payload:1});
n1.emit("input", {topic:"b",payload:1});
n1.emit("input", {topic:"c",payload:1});
n1.emit("input", {topic:"d",payload:1});
n1.emit("input", {topic:"a",payload:2});
});
});
it('should only send output if another chosen property changes - foo (rbe)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", property:"foo", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("foo", "a");
c+=1;
}
else if (c === 1) {
msg.should.have.a.property("foo", "b");
c+=1;
}
else {
msg.should.have.a.property("foo");
msg.foo.should.have.a.property("b",1);
msg.foo.should.have.a.property("c",2);
done();
}
});
n1.emit("input", {foo:"a"});
n1.emit("input", {payload:"a"});
n1.emit("input", {foo:"a"});
n1.emit("input", {payload:"a"});
n1.emit("input", {foo:"a"});
n1.emit("input", {foo:"b"});
n1.emit("input", {foo:{b:1,c:2}});
n1.emit("input", {foo:{c:2,b:1}});
n1.emit("input", {payload:{c:2,b:1}});
});
});
it('should only send output if payload changes - ignoring first value (rbei)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbei", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "b");
msg.should.have.a.property("topic", "a");
c+=1;
}
else if (c === 1) {
msg.should.have.a.property("payload", "b");
msg.should.have.a.property("topic", "b");
c+=1;
}
else if (c === 2) {
msg.should.have.a.property("payload", "c");
msg.should.have.a.property("topic", "a");
c+=1;
}
else {
msg.should.have.a.property("payload", "c");
msg.should.have.a.property("topic", "b");
done();
}
});
n1.emit("input", {payload:"a", topic:"a"});
n1.emit("input", {payload:"a", topic:"b"});
n1.emit("input", {payload:"a", topic:"a"});
n1.emit("input", {payload:"b", topic:"a"});
n1.emit("input", {payload:"b", topic:"b"});
n1.emit("input", {payload:"c", topic:"a"});
n1.emit("input", {payload:"c", topic:"b"});
});
});
it('should send output if queue is reset (rbe)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", "a");
c+=1;
}
else if (c === 1) {
msg.should.have.a.property("payload", "b");
c+=1;
}
else if (c === 2) {
msg.should.have.a.property("payload", "a");
c+=1;
}
else if (c === 3) {
msg.should.have.a.property("payload", "b");
c+=1;
}
else if (c === 4) {
msg.should.have.a.property("payload", "b");
c+=1;
}
else if (c === 5) {
msg.should.have.a.property("payload", "b");
c+=1;
}
else if (c === 6) {
msg.should.have.a.property("payload", "a");
c+=1;
}
else {
msg.should.have.a.property("payload", "c");
done();
}
});
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {reset:true}); // reset all
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {topic:"b", reset:""}); // reset b
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {reset:""}); // reset all
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {topic:"c"}); // don't reset a non topic
n1.emit("input", {topic:"b", payload:"b"});
n1.emit("input", {topic:"a", payload:"a"});
n1.emit("input", {topic:"c", payload:"c"});
});
});
it('should only send output if x away from original value (deadbandEq)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"deadbandEq", gap:"10", inout:"out", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c = c + 1;
if (c === 1) {
msg.should.have.a.property("payload", 0);
}
else if (c === 2) {
msg.should.have.a.property("payload", 10);
}
else if (c == 3) {
msg.should.have.a.property("payload", 20);
done();
}
});
n1.emit("input", {payload:0});
n1.emit("input", {payload:2});
n1.emit("input", {payload:4});
n1.emit("input", {payload:6});
n1.emit("input", {payload:8});
n1.emit("input", {payload:10});
n1.emit("input", {payload:15});
n1.emit("input", {payload:20});
});
});
it('should only send output if more than x away from original value (deadband)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c = c + 1;
//console.log(c,msg);
if (c === 1) {
msg.should.have.a.property("payload", 0);
}
else if (c === 2) {
msg.should.have.a.property("payload", 20);
}
else {
msg.should.have.a.property("payload", "5 deg");
done();
}
});
n1.emit("input", {payload:0});
n1.emit("input", {payload:2});
n1.emit("input", {payload:4});
n1.emit("input", {payload:"6 deg"});
n1.emit("input", {payload:8});
n1.emit("input", {payload:20});
n1.emit("input", {payload:15});
n1.emit("input", {payload:"5 deg"});
});
});
it('should only send output if more than x% away from original value (deadband)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10%", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c = c + 1;
if (c === 1) {
msg.should.have.a.property("payload", 100);
}
else if (c === 2) {
msg.should.have.a.property("payload", 111);
}
else if (c === 3) {
msg.should.have.a.property("payload", 135);
done();
}
});
n1.emit("input", {payload:100});
n1.emit("input", {payload:95});
n1.emit("input", {payload:105});
n1.emit("input", {payload:111});
n1.emit("input", {payload:120});
n1.emit("input", {payload:135});
});
});
it('should warn if no number found in deadband mode', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"deadband", gap:"10", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
});
setTimeout( function() {
c.should.equal(0);
helper.log().called.should.be.true;
var logEvents = helper.log().args.filter(function (evt) {
return evt[0].type == "rbe";
});
logEvents.should.have.length(1);
var msg = logEvents[0][0];
msg.should.have.property('level', helper.log().WARN);
msg.should.have.property('id', 'n1');
msg.should.have.property('type', 'rbe');
msg.should.have.property('msg', 'rbe.warn.nonumber');
done();
},50);
n1.emit("input", {payload:"banana"});
});
});
it('should not send output if x away or greater from original value (narrowbandEq)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"narrowbandEq", gap:"10", inout:"out", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c = c + 1;
//console.log(c,msg);
if (c === 1) {
msg.should.have.a.property("payload", 0);
}
else if (c === 2) {
msg.should.have.a.property("payload", 5);
}
else if (c === 3) {
msg.should.have.a.property("payload", 10);
done();
}
});
n1.emit("input", {payload:0});
n1.emit("input", {payload:10});
n1.emit("input", {payload:5});
n1.emit("input", {payload:15});
n1.emit("input", {payload:10});
n1.emit("input", {payload:20});
n1.emit("input", {payload:25});
});
});
it('should not send output if more than x away from original value (narrowband)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", 0);
}
else if (c === 1) {
msg.should.have.a.property("payload","6 deg");
}
else {
msg.should.have.a.property("payload", "5 deg");
done();
}
c += 1;
});
n1.emit("input", {payload:0});
n1.emit("input", {payload:20});
n1.emit("input", {payload:40});
n1.emit("input", {payload:"6 deg"});
n1.emit("input", {payload:18});
n1.emit("input", {payload:20});
n1.emit("input", {payload:50});
n1.emit("input", {payload:"5 deg"});
});
});
it('should send output if gap is 0 and input doesnt change (narrowband)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", 1);
}
else if (c === 4) {
msg.should.have.a.property("payload",1);
done();
}
c += 1;
});
n1.emit("input", {payload:1});
n1.emit("input", {payload:1});
n1.emit("input", {payload:1});
n1.emit("input", {payload:1});
n1.emit("input", {payload:0});
n1.emit("input", {payload:1});
});
});
it('should not send output if more than x away from original value (narrowband in step mode)', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"narrowband", gap:"10", inout:"in", start:"500", wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
if (c === 0) {
msg.should.have.a.property("payload", 55);
}
else if (c === 1) {
msg.should.have.a.property("payload", 205);
done();
}
c += 1;
});
n1.emit("input", {payload:50});
n1.emit("input", {payload:55});
n1.emit("input", {payload:200});
n1.emit("input", {payload:205});
});
});
});