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
- 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
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
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",
"version" : "0.0.4",
"version" : "0.0.7",
"description" : "A Node-RED node that provides report-by-exception (RBE) and deadband capability.",
"dependencies" : {
},

View File

@ -16,19 +16,19 @@
<script type="text/x-red" data-template-name="rbe">
<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%;">
<option value="rbe">RBE - report if value changed</option>
<option value="deadband">Deadband - report if changed more than</option>
<option value="rbe" data-i18n="rbe.opts.rbe"></option>
<option value="deadband" data-i18n="rbe.opts.deadband></option>
</select>
</div>
<div class="form-row" id="node-bandgap">
<label for="node-input-gap"><i class="fa fa-random"></i> Band gap</label>
<input type="text" id="node-input-gap" placeholder="e.g. 10" style="width:71%;">
<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" data-i18n="[placeholder]rbe.eg.bandgap" style="width:71%;">
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"/> Name</label>
<input type="text" id="node-input-name" placeholder="Name" style="width:71%;">
<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" data-i18n="[placeholder]rbe.label.name" style="width:71%;">
</div>
</script>
@ -38,16 +38,19 @@
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
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 type="text/javascript">
RED.nodes.registerType('rbe',{
RED.nodes.registerType("rbe", {
color:"#E2D96E",
category: 'function',
defaults: {
name: {value:""},
func: {value:"rbe"},
gap: {value:"",validate:RED.validators.regex(/^(\d*|)$/)}
gap: {value:"",validate:RED.validators.regex(/^(\d*|)(%|)$/)}
},
inputs: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");
* you may not use this file except in compliance with the License.
@ -16,33 +16,40 @@
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.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 previous = null;
node.previous = {};
this.on("input",function(msg) {
if (msg.hasOwnProperty("payload")) {
var t = msg.topic || "_no_topic";
if (this.func === "rbe") {
if (msg.payload != previous) {
previous = msg.payload;
if (msg.payload != node.previous[t]) {
node.previous[t] = msg.payload;
node.send(msg);
}
}
else {
var n = parseFloat(msg.payload);
if (!isNaN(n)) {
if (previous == null) { previous = n - node.gap; }
if (Math.abs(n - previous) >= node.gap) {
previous = n;
if (node.pc) { node.gap = (node.previous[t] * node.g / 100) || 0; }
if (!node.previous.hasOwnProperty(t)) { node.previous[t] = n - node.gap; }
if (Math.abs(n - node.previous[t]) >= node.gap) {
node.previous[t] = n;
node.send(msg);
}
}
else {
node.warn("no number found in payload");
node.warn(RED._("rbe.warn.nonumber"));
}
}
} // ignore msg with no payload property.

View File

@ -37,13 +37,13 @@ describe('rbe node', function() {
var n1 = helper.getNode("n1");
n1.should.have.property("name", "rbe1");
n1.should.have.property("func", "rbe");
n1.should.have.property("gap", 0);
n1.should.have.property("gap", "0");
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"} ];
helper.load(testNode, flow, function() {
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) {
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"} ];
helper.load(testNode, flow, function() {
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) {
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"} ];
helper.load(testNode, flow, function() {
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('id', 'n1');
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();
},50);
n1.emit("input", {payload:"banana"});