add be i118n version with test

This commit is contained in:
Dave Conway-Jones 2015-07-14 21:03:07 +01:00
parent ee5ff8b010
commit 1ff66443fb
6 changed files with 97 additions and 26 deletions

View File

@ -18,6 +18,9 @@ Usage
A simple node to provide report by exception (RBE) and deadband function A simple node to provide report by exception (RBE) and deadband function
- only passes on data if it has changed. - only passes on data if it has changed.
This works on a per **msg.topic** basis. This means that a single rbe node can
handle multiple topics at the same time.
###RBE mode ###RBE mode
Outputs the **msg** if the **msg.payload** is different to the previous one. Outputs the **msg** if the **msg.payload** is different to the previous one.
@ -29,4 +32,14 @@ same value. Saves bandwidth, etc...
In deadband mode the incoming payload should contain a parseable *number* and is In deadband mode the incoming payload should contain a parseable *number* and is
output only if greater than + or - the *band gap* away from the previous output. output only if greater than + or - the *band gap* away from the previous output.
Will accept numbers, or parseable strings like "18.4 C" or "$500" The deadband value can be specified as a fixed number, or a percentage. E.g. 10
or 5% . If % mode is used then the output will only get sent if the input payload
value is equal or more than the specified % away from the previously sent value.
For example - if last sent value was 100, and deadband is set to 10% - a value
of 110 will pass - then the next value has to be 121 in order to pass (= 110 + 10% = 121).
This is mainly useful if you want to operate across multiple topics at the same
time that may have widely differing input ranges.
Will only accept numbers, or parseable strings like "18.4 C" or "$500"

View File

@ -0,0 +1,19 @@
{
"rbe": {
"label": {
"func": "Mode",
"bandgap": "Band gap",
"name": "Name"
},
"eg":{
"bandgap": "e.g. 10 or 5%"
},
"opts": {
"rbe": "RBE - report if value changed",
"deadband": "Deadband - report if changed more than",
},
"warn": {
"nonumber": "no number found in payload"
}
}
}

View File

@ -1,6 +1,6 @@
{ {
"name" : "node-red-node-rbe", "name" : "node-red-node-rbe",
"version" : "0.0.4", "version" : "0.0.7",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capability.", "description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capability.",
"dependencies" : { "dependencies" : {
}, },

View File

@ -16,19 +16,19 @@
<script type="text/x-red" data-template-name="rbe"> <script type="text/x-red" data-template-name="rbe">
<div class="form-row"> <div class="form-row">
<label for="node-input-func"><i class="fa fa-wrench"></i> Mode</label> <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:74%;"> <select type="text" id="node-input-func" style="width:74%;">
<option value="rbe">RBE - report if value changed</option> <option value="rbe" data-i18n="rbe.opts.rbe"></option>
<option value="deadband">Deadband - report if changed more than</option> <option value="deadband" data-i18n="rbe.opts.deadband></option>
</select> </select>
</div> </div>
<div class="form-row" id="node-bandgap"> <div class="form-row" id="node-bandgap">
<label for="node-input-gap"><i class="fa fa-random"></i> Band gap</label> <label for="node-input-gap"><i class="fa fa-random"></i> <span data-i18n="rbe.label.bandgap"></span></label>
<input type="text" id="node-input-gap" placeholder="e.g. 10" style="width:71%;"> <input type="text" id="node-input-gap" data-i18n="[placeholder]rbe.eg.bandgap" style="width:71%;">
</div> </div>
<div class="form-row"> <div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"/> Name</label> <label for="node-input-name"><i class="fa fa-tag"/> <span data-i18n="rbe.label.name"></span></label>
<input type="text" id="node-input-name" placeholder="Name" style="width:71%;"> <input type="text" id="node-input-name" data-i18n="[placeholder]rbe.label.name" style="width:71%;">
</div> </div>
</script> </script>
@ -38,16 +38,19 @@
different to the previous one. Works on numbers and strings.</p> different to the previous one. Works on numbers and strings.</p>
<p>In <b>deadband</b> mode the incoming payload should contain a parseable <i>number</i> and is <p>In <b>deadband</b> mode the incoming payload should contain a parseable <i>number</i> and is
output only if greater than + or - the <i>band gap</i> away from the previous output.</p> output only if greater than + or - the <i>band gap</i> away from the previous output.</p>
<p>Deadband also supports % - only sends if the input differs by more than x% of the original value.</p>
<p><b>Note:</b> This works on a per <b>msg.topic</b> basis. This means that a single rbe node can
handle multiple topics at the same time.</p>
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
RED.nodes.registerType('rbe',{ RED.nodes.registerType("rbe", {
color:"#E2D96E", color:"#E2D96E",
category: 'function', category: 'function',
defaults: { defaults: {
name: {value:""}, name: {value:""},
func: {value:"rbe"}, func: {value:"rbe"},
gap: {value:"",validate:RED.validators.regex(/^(\d*|)$/)} gap: {value:"",validate:RED.validators.regex(/^(\d*|)(%|)$/)}
}, },
inputs:1, inputs:1,
outputs:1, outputs:1,

View File

@ -1,5 +1,5 @@
/** /**
* Copyright 2014 IBM Corp. * Copyright 2014, 2015 IBM Corp.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,33 +16,40 @@
module.exports = function(RED) { module.exports = function(RED) {
"use strict"; "use strict";
function RbeNode(n) { function RbeNode(n) {
RED.nodes.createNode(this,n); RED.nodes.createNode(this,n);
this.func = n.func || "rbe"; this.func = n.func || "rbe";
this.gap = n.gap || 0; this.gap = n.gap || "0";
this.pc = false;
if (this.gap.substr(-1) === "%") {
this.pc = true;
this.gap = parseFloat(this.gap);
}
this.g = this.gap;
var node = this; var node = this;
var previous = null; node.previous = {};
this.on("input",function(msg) { this.on("input",function(msg) {
if (msg.hasOwnProperty("payload")) { if (msg.hasOwnProperty("payload")) {
var t = msg.topic || "_no_topic";
if (this.func === "rbe") { if (this.func === "rbe") {
if (msg.payload != previous) { if (msg.payload != node.previous[t]) {
previous = msg.payload; node.previous[t] = msg.payload;
node.send(msg); node.send(msg);
} }
} }
else { else {
var n = parseFloat(msg.payload); var n = parseFloat(msg.payload);
if (!isNaN(n)) { if (!isNaN(n)) {
if (previous == null) { previous = n - node.gap; } if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
if (Math.abs(n - previous) >= node.gap) { if (!node.previous.hasOwnProperty(t)) { node.previous[t] = n - node.gap; }
previous = n; if (Math.abs(n - node.previous[t]) >= node.gap) {
node.previous[t] = n;
node.send(msg); node.send(msg);
} }
} }
else { else {
node.warn("no number found in payload"); node.warn(RED._("rbe.warn.nonumber"));
} }
} }
} // ignore msg with no payload property. } // ignore msg with no payload property.

View File

@ -37,13 +37,13 @@ describe('rbe node', function() {
var n1 = helper.getNode("n1"); var n1 = helper.getNode("n1");
n1.should.have.property("name", "rbe1"); n1.should.have.property("name", "rbe1");
n1.should.have.property("func", "rbe"); n1.should.have.property("func", "rbe");
n1.should.have.property("gap", 0); n1.should.have.property("gap", "0");
done(); done();
}); });
}); });
it('should only send output if payload changes', function(done) { it('should only send output if payload changes', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:0, wires:[["n2"]] }, var flow = [{"id":"n1", "type":"rbe", func:"rbe", gap:"0", wires:[["n2"]] },
{id:"n2", type:"helper"} ]; {id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() { helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1"); var n1 = helper.getNode("n1");
@ -70,7 +70,7 @@ describe('rbe node', function() {
}); });
it('should only send output if more than x away from original value', function(done) { it('should only send output if more than x away from original value', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"gap", gap:10, wires:[["n2"]] }, var flow = [{"id":"n1", "type":"rbe", func:"gap", gap:"10", wires:[["n2"]] },
{id:"n2", type:"helper"} ]; {id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() { helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1"); var n1 = helper.getNode("n1");
@ -100,8 +100,37 @@ describe('rbe node', function() {
}); });
}); });
it('should only send output if more than x% away from original value', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"gap", 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", 100);
}
else if (c === 1) {
msg.should.have.a.property("payload", 120);
}
else {
msg.should.have.a.property("payload", 132);
done();
}
c += 1;
});
n1.emit("input", {payload:100});
n1.emit("input", {payload:95});
n1.emit("input", {payload:105});
n1.emit("input", {payload:120});
n1.emit("input", {payload:130});
n1.emit("input", {payload:132});
});
});
it('should warn if no number found in gap mode', function(done) { it('should warn if no number found in gap mode', function(done) {
var flow = [{"id":"n1", "type":"rbe", func:"gap", gap:10, wires:[["n2"]] }, var flow = [{"id":"n1", "type":"rbe", func:"gap", gap:"10", wires:[["n2"]] },
{id:"n2", type:"helper"} ]; {id:"n2", type:"helper"} ];
helper.load(testNode, flow, function() { helper.load(testNode, flow, function() {
var n1 = helper.getNode("n1"); var n1 = helper.getNode("n1");
@ -121,7 +150,7 @@ describe('rbe node', function() {
msg.should.have.property('level', helper.log().WARN); msg.should.have.property('level', helper.log().WARN);
msg.should.have.property('id', 'n1'); msg.should.have.property('id', 'n1');
msg.should.have.property('type', 'rbe'); msg.should.have.property('type', 'rbe');
msg.should.have.property('msg', 'no number found in payload'); msg.should.have.property('msg', 'rbe.warn.nonumber');
done(); done();
},50); },50);
n1.emit("input", {payload:"banana"}); n1.emit("input", {payload:"banana"});