diff --git a/function/smooth/17-smooth.html b/function/smooth/17-smooth.html index d7340849..1b22a635 100644 --- a/function/smooth/17-smooth.html +++ b/function/smooth/17-smooth.html @@ -32,6 +32,11 @@ +
+ + + only emit one message per most recent N values +

@@ -44,9 +49,8 @@

A simple node to provide various functions across several previous values, including max, min, mean, high and low pass filters.

Messages arriving with different msg.topic can be treated as separate streams if so configured.

Max, Min and Mean work over a specified number of previous values.

-

The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is - similar to an α of 0.1. It is analagous to an RC time constant - but there is no time component to this as the - time is based on events arriving.

+

The High and Low pass filters use a smoothing factor. The higher the number the more the smoothing. E.g. a value of 10 is similar to an α of 0.1. It is analagous to an RC time constant - but there is no time component to this as the time is based on events arriving.

+

Enabling the Reduce option causes the node to only emit one message per N values (available for the Max, Min and Mean functions). E.g. if set to Mean over 10 values, there will only be one outgoing message per 10 incoming ones.

If msg.reset is received (with any value), all the counters and intermediate values are reset to an initial state.

Note: This only operates on numbers. Anything else will try to be made into a number and rejected if that fails.

@@ -61,7 +65,8 @@ action: {value:"mean"}, count: {value:"10",required:true,validate:RED.validators.number()}, round: {value:""}, - mult: {value:"single"} + mult: {value:"single"}, + reduce: {value:false} }, inputs: 1, outputs: 1, @@ -87,10 +92,12 @@ if ((a === "high") || ( a === "low" )) { $("#node-over").html("with a smoothing factor of "); $("#node-over2").html(""); + $("#row-input-reduce").hide(); } else { $("#node-over").html("over the most recent "); $("#node-over2").html(" values"); + $("#row-input-reduce").show(); } }); $("#node-input-action").change(); diff --git a/function/smooth/17-smooth.js b/function/smooth/17-smooth.js index 87b76c9c..c34c04aa 100644 --- a/function/smooth/17-smooth.js +++ b/function/smooth/17-smooth.js @@ -9,6 +9,7 @@ module.exports = function(RED) { if (this.round == "true") { this.round = 0; } this.count = Number(n.count); this.mult = n.mult || "single"; + this.reduce = n.reduce || false; this.property = n.property || "payload"; var node = this; var v = {}; @@ -16,6 +17,7 @@ module.exports = function(RED) { this.on('input', function (msg) { var value = RED.util.getMessageProperty(msg,node.property); var top = msg.topic || "_my_default_topic"; + var reduce = node.reduce; if (this.mult === "single") { top = "a"; } if ((v.hasOwnProperty(top) !== true) || msg.hasOwnProperty("reset")) { @@ -26,15 +28,18 @@ module.exports = function(RED) { v[top].pop = 0; v[top].old = null; v[top].count = this.count; + v[top].iter = 0; } if (value !== undefined) { var n = Number(value); if (!isNaN(n)) { + v[top].iter++; if ((node.action === "low") || (node.action === "high")) { if (v[top].old == null) { v[top].old = n; } v[top].old = v[top].old + (n - v[top].old) / v[top].count; if (node.action === "low") { value = v[top].old; } else { value = n - v[top].old; } + reduce = false; } else { v[top].a.push(n); @@ -61,10 +66,13 @@ module.exports = function(RED) { if (node.round !== false) { value = Math.round(value * Math.pow(10, node.round)) / Math.pow(10, node.round); } - RED.util.setMessageProperty(msg,node.property,value); - node.send(msg); + if (reduce == false || v[top].iter == v[top].count) { + v[top].iter = 0; + RED.util.setMessageProperty(msg,node.property,value); + node.send(msg); + } } - else { node.log("Not a number: "+value); } + else { node.log("Not a number: " + value); } } // ignore msg with no payload property. }); } diff --git a/test/function/smooth/17-smooth_spec.js b/test/function/smooth/17-smooth_spec.js index 33188765..ac7c7fc7 100644 --- a/test/function/smooth/17-smooth_spec.js +++ b/test/function/smooth/17-smooth_spec.js @@ -316,5 +316,79 @@ describe('smooth node', function() { n1.emit("input", {payload:9, topic:"B"}); }); }); - + it("should reduce the number of messages by averaging if asked", function(done) { + var flow = [{"id":"n1", "type":"smooth", action:"mean", count:"5", reduce:"true", 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; + if (c === 1) { msg.should.have.a.property("payload", 3); } + else if (c === 2) { msg.should.have.a.property("payload", 6); done(); } + else if (c > 2) { done(new Error("should not emit more than two messages.")); } + }); + n1.emit("input", {payload:1}); + n1.emit("input", {payload:2}); + n1.emit("input", {payload:3}); + n1.emit("input", {payload:4}); + n1.emit("input", {payload:5}); + n1.emit("input", {payload:6}); + n1.emit("input", {payload:7}); + n1.emit("input", {payload:8}); + n1.emit("input", {payload:9}) +; n1.emit("input", {payload:0}); + }); + }); + it("should reduce the number of messages by max value, if asked", function(done) { + var flow = [{"id":"n1", "type":"smooth", action:"max", count:"5", reduce:"true", 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; + if (c === 1) { msg.should.have.a.property("payload", 5); } + else if (c === 2) { msg.should.have.a.property("payload", 9); done(); } + else if (c > 2) { done(new Error("should not emit more than two messages.")); } + }); + n1.emit("input", {payload:1}); + n1.emit("input", {payload:2}); + n1.emit("input", {payload:3}); + n1.emit("input", {payload:4}); + n1.emit("input", {payload:5}); + n1.emit("input", {payload:6}); + n1.emit("input", {payload:7}); + n1.emit("input", {payload:8}); + n1.emit("input", {payload:9}); + n1.emit("input", {payload:0}); + }); + }); + it("should reduce the number of messages by min value, if asked", function(done) { + var flow = [{"id":"n1", "type":"smooth", action:"min", count:"5", reduce:"true", 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; + if (c === 1) { msg.should.have.a.property("payload", 1); } + else if (c === 2) { msg.should.have.a.property("payload", 0); done(); } + else if (c > 2) { done(new Error("should not emit more than two messages.")); } + }); + n1.emit("input", {payload:1}); + n1.emit("input", {payload:2}); + n1.emit("input", {payload:3}); + n1.emit("input", {payload:4}); + n1.emit("input", {payload:5}); + n1.emit("input", {payload:6}); + n1.emit("input", {payload:7}); + n1.emit("input", {payload:8}); + n1.emit("input", {payload:9}); + n1.emit("input", {payload:0}); + }); + }); });