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%;"> <select id="node-then-type" style="width:70%;">
<option value="block" data-i18n="trigger.wait-reset"></option> <option value="block" data-i18n="trigger.wait-reset"></option>
<option value="wait" data-i18n="trigger.wait-for"></option> <option value="wait" data-i18n="trigger.wait-for"></option>
<option value="loop" data-i18n="trigger.wait-loop"></option>
</select> </select>
</div> </div>
<div class="form-row node-type-wait"> <div class="form-row node-type-duration">
<label></label> <label></label>
<input type="text" id="node-input-duration" style="text-align:end; width:70px !important"> <input type="text" id="node-input-duration" style="text-align:end; width:70px !important">
<select id="node-input-units" style="width:140px !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. <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 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> <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 <p>Or you can pass through either the <i>original msg</i> or the <i>latest msg</i> to arrive.</p>
<i>existing payload</i> will pass the complete payload object through.</p>
<p>Optionally the timer can be extended by being retriggered... or not.</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. <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> 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 <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> 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> <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> 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> <p>The blue status icon will be visible while the node is active.</p>
</script> </script>
@ -102,6 +104,9 @@
if (this.duration > 0) { if (this.duration > 0) {
return this.name|| this._("trigger.label.trigger")+" "+this.duration+this.units; 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 { else {
return this.name|| this._("trigger.label.trigger-block"); return this.name|| this._("trigger.label.trigger-block");
} }
@ -113,8 +118,14 @@
$("#node-then-type").change(function() { $("#node-then-type").change(function() {
if ($(this).val() == "block") { if ($(this).val() == "block") {
$(".node-type-wait").hide(); $(".node-type-wait").hide();
$(".node-type-duration").hide();
}
else if ($(this).val() == "loop") {
$(".node-type-wait").hide();
$(".node-type-duration").show();
} else { } else {
$(".node-type-wait").show(); $(".node-type-wait").show();
$(".node-type-duration").show();
} }
}); });
@ -127,7 +138,6 @@
var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false}; var optionNothing = {value:"nul",label:this._("trigger.output.nothing"),hasValue:false};
var optionPayload = {value:"pay",label:this._("trigger.output.existing"),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 optionOriginalPayload = {value:"pay",label:this._("trigger.output.original"),hasValue:false}
var optionLatestPayload = {value:"payl",label:this._("trigger.output.latest"),hasValue:false} var optionLatestPayload = {value:"payl",label:this._("trigger.output.latest"),hasValue:false}
@ -151,6 +161,11 @@
if (this.duration == "0") { if (this.duration == "0") {
$("#node-then-type").val("block"); $("#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 { } else {
$("#node-then-type").val("wait"); $("#node-then-type").val("wait");
} }
@ -167,6 +182,9 @@
if ($("#node-then-type").val() == "block") { if ($("#node-then-type").val() == "block") {
$("#node-input-duration").val("0"); $("#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.units = n.units || "ms";
this.reset = n.reset || ''; this.reset = n.reset || '';
this.duration = n.duration || 250; this.duration = n.duration || 250;
if (this.duration <= 0) { this.duration = 0; } if (this.duration < 0) {
else { this.loop = true;
if (this.units == "s") { this.duration = this.duration * 1000; } this.duration = this.duration * -1;
if (this.units == "min") { this.duration = this.duration * 1000 * 60; } this.extend = false;
if (this.units == "hr") { this.duration = this.duration * 1000 *60 * 60; }
} }
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.op1Templated = (this.op1type === 'str' && this.op1.indexOf("{{") != -1);
this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1); this.op2Templated = (this.op2type === 'str' && this.op2.indexOf("{{") != -1);
if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); } if ((this.op1type === "num") && (!isNaN(this.op1))) { this.op1 = Number(this.op1); }
@ -69,13 +73,14 @@ module.exports = function(RED) {
var tout = null; var tout = null;
var m2; var m2;
this.on("input", function(msg) { this.on("input", function(msg) {
if (msg.hasOwnProperty("reset") || ((node.reset !== '')&&(msg.payload == node.reset)) ) { if (msg.hasOwnProperty("reset") || ((node.reset !== '') && (msg.payload == node.reset)) ) {
clearTimeout(tout); if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null; tout = null;
node.status({}); node.status({});
} }
else { else {
if ((!tout) && (tout !== 0)) { if (((!tout) && (tout !== 0)) || (node.loop === true)) {
if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; } if (node.op2type === "pay" || node.op2type === "payl") { m2 = msg.payload; }
else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); } else if (node.op2Templated) { m2 = mustache.render(node.op2,msg); }
else if (node.op2type !== "nul") { else if (node.op2type !== "nul") {
@ -91,6 +96,13 @@ module.exports = function(RED) {
if (node.op1type !== "nul") { node.send(msg); } if (node.op1type !== "nul") { node.send(msg); }
if (node.duration === 0) { tout = 0; } 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 { else {
tout = setTimeout(function() { tout = setTimeout(function() {
if (node.op2type !== "nul") { if (node.op2type !== "nul") {
@ -108,7 +120,7 @@ module.exports = function(RED) {
node.status({fill:"blue",shape:"dot",text:" "}); node.status({fill:"blue",shape:"dot",text:" "});
} }
else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) { else if ((node.extend === "true" || node.extend === true) && (node.duration > 0)) {
clearTimeout(tout); if (tout) { clearTimeout(tout); }
if (node.op2type === "payl") { m2 = msg.payload; } if (node.op2type === "payl") { m2 = msg.payload; }
tout = setTimeout(function() { tout = setTimeout(function() {
if (node.op2type !== "nul") { if (node.op2type !== "nul") {
@ -122,14 +134,17 @@ module.exports = function(RED) {
tout = null; tout = null;
node.status({}); node.status({});
},node.duration); },node.duration);
} else { }
else {
if (node.op2type === "payl") { m2 = msg.payload; } if (node.op2type === "payl") { m2 = msg.payload; }
} }
} }
}); });
this.on("close", function() { this.on("close", function() {
if (tout) { if (tout) {
clearTimeout(tout); if (node.loop === true) { clearInterval(tout); }
else { clearTimeout(tout); }
tout = null;
} }
node.status({}); 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) { 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"} ]; {id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() { helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1"); 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) { 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"} ]; {id:"n2", type:"helper"} ];
helper.load(triggerNode, flow, function() { helper.load(triggerNode, flow, function() {
var n1 = helper.getNode("n1"); 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);
});
});
}); });