1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

let trigger node set repeated outputs

This commit is contained in:
Dave Conway-Jones 2017-02-07 20:11:34 +00:00
parent f2235dacdc
commit 1841fc18fa
3 changed files with 73 additions and 18 deletions

View File

@ -25,9 +25,10 @@
<select id="node-then-type" style="width:70%;">
<option value="block" data-i18n="trigger.wait-reset"></option>
<option value="wait" data-i18n="trigger.wait-for"></option>
<option value="loop" data-i18n="trigger.wait-loop"></option>
</select>
</div>
<div class="form-row node-type-wait">
<div class="form-row node-type-duration">
<label></label>
<input type="text" id="node-input-duration" style="text-align:end; width:70px !important">
<select id="node-input-units" style="width:140px !important">
@ -67,16 +68,17 @@
<p>The two output states can be specified as can the duration of the timer.
Either output can be set to a value, or templated from the inbound
<code>msg</code> using mustache syntax. <pre>The payload is {{payload}}</pre></p>
<p>If the <code>msg.payload</code> is an object then setting the output to
<i>existing payload</i> will pass the complete payload object through.</p>
<p>Or you can pass through either the <i>original msg</i> or the <i>latest msg</i> to arrive.</p>
<p>Optionally the timer can be extended by being retriggered... or not.</p>
<p>By setting the first output to <i>nothing</i>, and selecting extend timer - a watchdog timer can be created.
No output will happen as long as repeated inputs occur within the timeout period.</p>
<p>Setting the timer to 0 creates an "infinite" timeout - the first output will happen but the second
never will, and neither can the first be retriggered - so a true one shot.</p>
<p>If a <code>msg.reset</code> property is present, or the <code>msg.payload</code>
matches the optional reset value, any timeout currently in progress
matches the optional reset value, any timeout or repeat currently in progress
will be cleared and the second output will not happen.</p>
<p>The node can be set to repeat the input <code>msg</code> at regular intervals until the input changes,
or the node is reset.</p>
<p>The blue status icon will be visible while the node is active.</p>
</script>
@ -102,6 +104,9 @@
if (this.duration > 0) {
return this.name|| this._("trigger.label.trigger")+" "+this.duration+this.units;
}
if (this.duration < 0) {
return this.name|| this._("trigger.label.trigger-loop")+" "+(this.duration * -1)+this.units;
}
else {
return this.name|| this._("trigger.label.trigger-block");
}
@ -113,8 +118,14 @@
$("#node-then-type").change(function() {
if ($(this).val() == "block") {
$(".node-type-wait").hide();
$(".node-type-duration").hide();
}
else if ($(this).val() == "loop") {
$(".node-type-wait").hide();
$(".node-type-duration").show();
} else {
$(".node-type-wait").show();
$(".node-type-duration").show();
}
});
@ -127,7 +138,6 @@
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),hasValue:false}
var optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false}
var optionLatestPayload = {value:"payl",label:this._("trigger.output.latest"),hasValue:false}
@ -151,6 +161,11 @@
if (this.duration == "0") {
$("#node-then-type").val("block");
}
else if ((this.duration * 1) < 0) {
$("#node-then-type").val("loop");
this.duration = this.duration * -1;
$("#node-input-duration").val(this.duration);
} else {
$("#node-then-type").val("wait");
}
@ -167,6 +182,9 @@
if ($("#node-then-type").val() == "block") {
$("#node-input-duration").val("0");
}
if ($("#node-then-type").val() == "loop") {
$("#node-input-duration").val($("#node-input-duration").val() * -1);
}
}
});

View File

@ -48,12 +48,16 @@ module.exports = function(RED) {
this.units = n.units || "ms";
this.reset = n.reset || '';
this.duration = n.duration || 250;
if (this.duration <= 0) { this.duration = 0; }
else {
if (this.duration < 0) {
this.loop = true;
this.duration = this.duration * -1;
this.extend = false;
}
if (this.units == "s") { this.duration = this.duration * 1000; }
if (this.units == "min") { this.duration = this.duration * 1000 * 60; }
if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; }
}
this.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1);
this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
@ -69,13 +73,14 @@ module.exports = function(RED) {
var tout = null;
var m2;
this.on("input", function(msg) {
if (msg.hasOwnProperty("reset") || ((node.reset !== '')&&(msg.payload == node.reset)) ) {
clearTimeout(tout);
if (msg.hasOwnProperty("reset") || ((node.reset !== '') && (msg.payload == node.reset)) ) {
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
node.status({});
}
else {
if ((!tout) && (tout !== 0)) {
if (((!tout) && (tout !== 0)) || (node.loop === true)) {
if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; }
else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); }
else if (node.op2type !== "nul") {
@ -91,6 +96,13 @@ module.exports = function(RED) {
if (node.op1type !== "nul") { node.send(msg); }
if (node.duration === 0) { tout = 0; }
else if (node.loop === true) {
if (tout) { clearInterval(tout); }
if (node.op1type !== "nul") {
var msg2 = RED.util.cloneMessage(msg);
tout = setInterval(function() { node.send(msg2); },node.duration);
}
}
else {
tout = setTimeout(function() {
if (node.op2type !== "nul") {
@ -108,7 +120,7 @@ module.exports = function(RED) {
node.status({fill:"blue",shape:"dot",text:" "});
}
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
clearTimeout(tout);
if (tout) { clearTimeout(tout); }
if (node.op2type === "payl") { m2 = msg.payload; }
tout = setTimeout(function() {
if (node.op2type !== "nul") {
@ -122,14 +134,17 @@ module.exports = function(RED) {
tout = null;
node.status({});
},node.duration);
} else {
}
else {
if (node.op2type === "payl") { m2 = msg.payload; }
}
}
});
this.on("close", function() {
if (tout) {
clearTimeout(tout);
if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
}
node.status({});
});

View File

@ -356,7 +356,7 @@ describe('trigger node', function() {
});
it('should be able to set infinite timeout, and clear timeout', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:-5, wires:[["n2"]] },
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", duration:0, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
@ -378,7 +378,7 @@ describe('trigger node', function() {
});
it('should be able to set infinite timeout, and clear timeout by message', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:-5, wires:[["n2"]] },
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:0, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
@ -399,4 +399,26 @@ describe('trigger node', function() {
});
});
it('should be able to set a repeat, and clear loop by reset', function(done) {
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", reset:"boo", duration:-25, wires:[["n2"]] },
{id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var c = 0;
n2.on("input", function(msg) {
c += 1;
msg.should.have.a.property("payload", "foo");
});
n1.emit("input", {payload:"foo"}); // trigger
setTimeout( function() {
n1.emit("input", {reset:true}); // reset
},90);
setTimeout( function() {
c.should.equal(4); // should send foo 4 times.
done();
},150);
});
});
});