Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Haxiboy committed Oct 23, 2020
0 parents commit 9c9bbb3
Show file tree
Hide file tree
Showing 5 changed files with 344 additions and 0 deletions.
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@



Advanced Looptimer for node-red



----------------------------



This project is based on **node-red-contrib-looptimer** and **node-red-contrib-looptimer2** with the need of overwrite properties via messages.

**Installation:**
> npm install node-red-contrib-looptimer-advanced
The main functionality is not changed (for now), however fix from looptimer2 has been implemented, because it did not work with other timers for example **node-red-contrib-stoptimer** and I've did some code rework for better functionality and cleaner code.





Sends the `msg` through the first output, then continues to do so in a loop, once per the timer duration specified, until a payload of `stop` or `STOP` is received, at which time the second output will automatically send a payload of `stopped`.





You can also stop the loop by specifying a maximum number of loops, which when reached, will stop the loop and timer, as well as sending a payload of `max loops reached` through the second output. Keep in mind, the first `msg` simply passes through, and is therefore not part of the loop. So if you set the max loops to `5`, you will get `6 messages`, which is 1 original message, and 5 messages from the loop.





Finally, to ensure you do not end up with an infinite loop, you can set a maximum timeout in seconds, minutes or hours, and when that time is reached, the loop and timer will also be stopped.





The properties can be overridden via `msg.loop` messages supplemented with property name. Accepted messages for loop are `msg.loop.duration` and `msg.loop.units`, for timeout are `msg.loop.maxtimeout` and `msg.loop.maxtimeoutunits`.



You can define maximum number of loops with `msg.loop.maxloops`





Setting the Max Loops and Max Timeout settings to high values can, for all intents, ensure that the loop can only be stopped by an incoming `stop` payload, however, the stability of the loop has not been tested over an extended number of hours.





**0.0.1** - Initial Release





**To Do:**

- [ ] Mustache Template Support

- [ ] Example Flows




[![Buy Me A Coffee](https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png =200x)](https://www.buymeacoffee.com/haxiboy)
Binary file added icons/looptimer_advanced.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions looptimer_advanced.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script type="text/javascript">
RED.nodes.registerType('looptimer-advanced',{
category: 'function',
color:"#33B2FF",
defaults: {
duration: {value:"5",required:true,validate:RED.validators.number()},
units: {value:"Second"},
maxloops: {value:"100",required:false,validate:RED.validators.number()},
maxtimeout: {value:"1",required:false,validate:RED.validators.number()},
maxtimeoutunits: {value:"Hour"},
name: {value:""}
},
inputs:1,
outputs:2,
icon: "looptimer_advanced.png",
label: function() {
return this.name || this.duration + " " + this.units + " Loop";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
},
outputLabels: ["original msg","stopped msg"]
});
</script>

<script type="text/x-red" data-template-name="looptimer-advanced">
<div class="form-row">
<label for="node-input-duration"><i class="fa fa-clock-o"></i> Loop Every</label>
<input type="text" id="node-input-duration" style="text-align:end; width:70px !important">
<select id="node-input-units" style="width:140px !important">
<option value="Milisecond">Miliseconds</option>
<option value="Second">Seconds</option>
<option value="Minute">Minutes</option>
<option value="Hour">Hours</option>
<option value="Day">Days</option>
</select>
</div>
<div class="form-row">
<label for="node-input-maxloops"><i class="fa fa-repeat"></i> Max Loops</label>
<input type="text" id="node-input-maxloops" style="text-align:end; width:70px !important">
</div>
<div class="form-row">
<label for="node-input-maxtimeout"><i class="fa fa-clock-o"></i> Max Timeout</label>
<input type="text" id="node-input-maxtimeout" style="text-align:end; width:70px !important">
<select id="node-input-maxtimeoutunits" style="width:140px !important">
<option value="Milisecond">Miliseconds</option>
<option value="Second">Seconds</option>
<option value="Minute">Minutes</option>
<option value="Hour">Hours</option>
<option value="Day">Days</option>
</select>
</div>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"></input>
</div>
</script>

<script type="text/x-red" data-help-name="looptimer-advanced">
<p>Sends the <code>msg</code> through the first output, then continues to do so in a loop, once per the timer duration specified, until a payload of <code>stop</code> or <code>STOP</code> is received, at which time the second output will automatically send a payload of <code>stopped</code>.
<p>You can also stop the loop by specifying a maximum number of loops, which when reached, will stop the loop and timer, as well as sending a payload of <code>max loops reached</code> through the second output. Keep in mind, the first <code>msg</code> simply passes through, and is therefore not part of the loop. So if you set the max loops to <code>5</code>, you will get <code>6 messages</code>, which is 1 original message, and 5 messages from the loop.</p>
<p>Finally, to ensure you do not end up with an infinite loop, you can set a maximum timeout in seconds, minutes or hours, and when that time is reached, the loop and timer will also be stopped.</p>
<p>Properties can be overridden via <code>msg.loop</code> messages supplemented with property name.</p>
<p>Accepted messages for loop are <code>msg.loop.duration</code> and <code>msg.loop.units</code></p>
<p>For timeout you can use <code>msg.loop.maxtimeout</code> and <code>msg.loop.maxtimeoutunits</code></p>
<p><code>NOTE:</code> Setting the Max Loops and Max Timeout settings to high values can, for all intents, ensure that the loop can only be stopped by an incoming <code>stop</code> payload, however, the stability of the loop has not been tested over an extended number of hours.</p>
</script>
179 changes: 179 additions & 0 deletions looptimer_advanced.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
module.exports = function (RED) {
"use strict";
var Looper = require('loop');

var unitMap = [
{ unit: "Millisecond", multiplier: 1, duration: 1 },
{ unit: "Second", multiplier: 1000, get duration() { return unitMap[0].duration * unitMap[1].multiplier } },
{ unit: "Minute", multiplier: 60, get duration() { return unitMap[1].duration * unitMap[2].multiplier } },
{ unit: "Hour", multiplier: 60, get duration() { return unitMap[2].duration * unitMap[3].multiplier } },
{ unit: "Day", multiplier: 24, get duration() { return unitMap[3].duration * unitMap[4].multiplier } }
]

var CalcDuration = function (unit, duration) { return (unitMap.find(o => o.unit === unit).duration * duration); }
function LoopTimer(n) {
RED.nodes.createNode(this, n);

this.units = n.units || "Second";
this.duration = n.duration || 5;
this.maxloops = n.maxloops || 100;
this.maxtimeoutunits = n.maxtimeoutunits || "Hour";
this.maxtimeout = n.maxtimeout || 1;
this.nodeduration = n.nodeduration || 1;
this.nodemaxtimeout = n.nodemaxtimeout || 1;

if (this.duration <= 0) {
this.duration = 0;
}

if (this.maxtimeout <= 0) {
this.maxtimeout = 0;
}

var node = this;
var loop = null;
var timeout = null;
var stopped = false;
var cpmsg = null;
var stopmsg = null;
var mlmsg = null;

this.on("input", function (msg) {
loop = Looper();
if (msg.loop != undefined) {

if (msg.loop.units != undefined) {
if ((msg.loop.units === "Millisecond") || (msg.loop.units === "Second") || (msg.loop.units === "Hour") || (msg.loop.units === "Day")) {
node.units = msg.loop.units;
} else {
node.status({ fill: "red", shape: "ring", text: "Invalid attribbute msg.loop.units" });
node.error('Invalid Attribbute: msg.loop.units, allowed values are Millisecond, Second, Minute, Hour, and Day');
return;
}
}


if (msg.loop.duration != undefined) {
if (typeof msg.loop.duration === 'number') {
node.duration = msg.loop.duration;
} else {
node.status({ fill: "red", shape: "ring", text: "msg.loop.units must be an Integer" });
node.error('Invalid Attribbute: msg.loop.duration, value must be an Integer');
return;
}
}

if (msg.loop.maxtimeoutunits != undefined) {
if ((msg.loop.maxtimeoutunits === "Millisecond") || (msg.loop.maxtimeoutunits === "Second") || (msg.loop.maxtimeoutunits === "Hour") || (msg.loop.maxtimeoutunits === "Day")) {
node.maxtimeoutunits = msg.loop.maxtimeoutunits;
} else {
node.status({ fill: "red", shape: "ring", text: "Invalid attribbute msg.loop.maxtimeoutunits" });
node.error('Invalid Attribbute: msg.loop.maxtimeoutunits, allowed values are Millisecond, Second, Minute, Hour, and Day');
return;
}
}


if (msg.loop.maxtimeout != undefined) {
if (typeof msg.loop.maxtimeout === 'number') {
node.maxtimeout = msg.loop.maxtimeout;
} else {
node.status({ fill: "red", shape: "ring", text: "msg.loop.maxtimeout must be an Integer" });
node.error('Invalid Attribbute: msg.loop.maxtimeout, value must be an Integer');
return;
}
}
if (msg.loop.maxloops != undefined) {
if (typeof msg.loop.maxloops === 'number') {
node.maxloops = msg.loop.maxloops;
} else {
node.status({ fill: "red", shape: "ring", text: "msg.loop.maxloops must be an Integer" });
node.error('Invalid Attribbute: msg.loop.maxloops, value must be an Integer');
return;
}
}



}
node.nodeduration = CalcDuration(node.units, node.duration);
node.nodemaxtimeout = CalcDuration(node.maxtimeoutunits, node.maxtimeout);

loop.setTimeout(node.nodemaxtimeout);
loop.setMaxLoop(node.maxloops);
node.status({});

if (stopped === false || msg._timerpass !== true) {
stopped = false;
clearTimeout(timeout);
timeout = null;
if (msg.payload == "stop" || msg.payload == "STOP") {
node.status({
fill: "red",
shape: "ring",
text: "stopped"
});
stopped = true;
stopmsg = RED.util.cloneMessage(msg);
stopmsg.payload = "stopped";
node.send([null, stopmsg]);
stopped = false;
} else {
cpmsg = RED.util.cloneMessage(msg);
node.send([cpmsg, null]);
msg._timerpass = true;
loop.run(function (next, err, data) {
node.status({
fill: "green",
shape: "dot",
text: "running"
});
data++;
timeout = setTimeout(function () {
if (stopped === false) {
cpmsg = RED.util.cloneMessage(msg);
if (data == node.maxloops) {
mlmsg = RED.util.cloneMessage(msg);
mlmsg.payload = "max loops reached";
node.send([cpmsg, mlmsg]);
node.status({
fill: "red",
shape: "ring",
text: "max loops reached"
});
timeout = null;
next("break");
} else {
node.status({});
if ((data * node.nodeduration) <= node.nodemaxtimeout) {
node.send([cpmsg, null]);
timeout = null;
if (((data + 1) * node.nodeduration) > node.nodemaxtimeout) {
next("break");
} else {
next(undefined, data);
}
} else {
timeout = null;
next("break");
}
}
} else {
timeout = null;
next("break");
}
}, node.nodeduration);
}, 0);
}
}
});
this.on("close", function () {
stopped = false;
if (timeout) {
clearTimeout(timeout);
}
node.status({});
});
}
RED.nodes.registerType("looptimer-advanced", LoopTimer);
}
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "node-red-contrib-looptimer-advanced",
"version": "0.0.1",
"description": "Sends the msg through the first output, then continues to do so in a loop, once per the duration specified, until a payload of 'stop' or 'STOP' is received. Supports override with messages!",
"main": "looptimer_advanced.js",
"scripts": {},
"dependencies": {
"loop": "2.1.2"
},
"directories": {},
"keywords": [
"node-red",
"loop",
"timer",
"advanced"
],
"author": {
"name": "Haxiboy"
},
"license": "Apache-2.0",
"node-red": {
"nodes": {
"looptimer-advanced": "looptimer_advanced.js"
}
}
}

0 comments on commit 9c9bbb3

Please sign in to comment.