Node Red setInterval causing stale readings on redeploy - setinterval

I'm tinkering around with Node-Red and have made a my first simple custom node which adds to a counter each time an input is received.
The node also contains a call to setInterval which after ever 5 seconds calls node.send() with the counter value in the msg.payload.
module.exports = function(RED) {
function MyNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var count = 0;
this.on('input', function(msg) {
count++;
});
setInterval(function() {
var msg =
{
payload: { countTotal : count }
};
node.send(msg);
return;
}, 5000); // 5 Second Timer
}
RED.nodes.registerType("my-node", MyNode);
}
If I deploy the flow the node is behaving as expected. However if I redeploy the flow I seem to be receiving two calls from the node.send().
One which is the correct value and the other is the last/stale value that was current before the redeploy.
For example:
Deploy and click input 6 times before 5 seconds
05 seconds: countTotal = 6
10 seconds: countTotal = 6
15 seconds: countTotal = 6
Re-deploy and click input 3 times before 5 seconds
05 seconds: countTotal = 3
05 seconds: countTotal = 6 [Old Stale reading]
If I restart node-red then it starts behaving as I'd expect.
My questions
Is this the correct approach to having a timer within a node, if not what should I be doing? If yes, why is the stale data coming through?
Many thanks.
UPDATE
Off the back off the answer from knolleary I've tried the following change
module.exports = function(RED) {
function MyNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var count = 0;
var timer = setInterval(function() {
var msg = { payload: { countTotal : count }};
node.send(msg);
return;
}, 5000); // 5 Second Timer
this.on('input', function(msg) {
count++;
});
this.on('close', function(msg) {
clearTimeout(timer);
});
}
RED.nodes.registerType("my-node", MyNode);
}
...however now when a the flow is re-deployed, it completely stops!!

You are creating a timer each time a node is created, but at no time are you clearing the timer when the node is 'closed'. That is why you are getting multiple timers firing each time you deploy the node.
You need to add a handler for the close event, and tidy up any resources the node is using or has created - such as the timer.
http://nodered.org/docs/creating-nodes/node-js#closing-the-node

Related

clearInterval on "OFF" payload

I am having newbie difficulties implementing clearInterval with a certain payload.
The
if(msg.payload.state === "OFF")
works, but the timer doesn't stop.
Amongst many resources, I have seen how to stop setInterval in JS in node-red..?, node.js: how to use setInterval and clearInterval?, clearInterval doesn't clearInterval & clearInterval On Image Slider Plugin
I did have a similar function, "dominos" that I used to make sure I was parsing the "OFF" payload.
I have included a lot of commented code to show what I had tried. Sorry about the mess!
Is it an issue with my syntax?
Is the code logical?
var input = msg.payload.state;
let timer = 0;
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
function xmas() { // REPEATS!
node.send({payload:red});
setTimeout(function(){
node.send({payload:green});
}, 1500);
}
// repeat with 3 second interval
timer = setInterval(() => xmas(), 3000);
if (msg.payload.state === "OFF") {
timer = clearInterval();
}
The important thing to remember about a function node is that it's state is totally reset for each incoming message. This means that timer will ALWAYS be reset to zero by the let timer = 0;
If you want to store any state between messages then you need to use the context.
Also the way you are using clearInterval() will never work, you need to pass the reference to the timer object to this function for it to do anything useful.
The following function node should do what I think you intended.
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
if (msg.payload.state != "OFF") {
var timer = setInteval(()=>{
node.send({payload: red});
setTimeout(() => {
node.send({payload: green});
}, 1500);
}, 3000);
context.set("timer",timer);
} else {
var timer = context.get("timer");
clearInterval(timer);
}

Dynamic time interval to call a function node.js

I have divided a day on 6 parts and each part of a day has its own interval. My function must run in this intervals.
for example 12:00pm - 15:00pm interval is 10 min, so function must be called every 10 min, but 15:00pm - 20:00pm interval is 2 min so from 15:01pm interval must be changed from 10min to 2min.
Need a suggestion how to build this kind of timer.
I can get intervals from mongoDB or local json file.
I guess I have to check what time is it and get an interval (from mongoDB or json file) for that time, then pass it to setInterval() or Cron job scheduler.
Tryed this way but every time im passing new interval last intervals are still working: If interval is 2 min and im changing it to 5 min, function is called twice: every 2 min and every 5 min in both setInterval() and Cron
const test = (min) => {
console.log(min);
var intervale = setInterval(() => {
console.log('firing', min, new Date());
}, min);
}
const test = (min) => {
cron.schedule(`0 */${min} * * * *`, () => {
console.log('firing cron', new Date())
});
}
thank you
Can you do something like this? This uses the EventEmitter model and fires an event based on the custom Interval.
const eventemitter_cls = require("events").EventEmitter;
let eventemitter = new eventemitter_cls();
intervalName = "randomintervals"
let sumOfTwoNumbers = () => {
console.log(`hello world ${(new Date()).getMinutes()} ${(new Date()).getSeconds()}` );
// After you are done with your function, again you need to emit the event based on the time interval
setTimeout(() => {
eventemitter.emit(intervalName)
}, getTimeInterval());
}
let getTimeInterval = () => {
let currentTime = new Date();
// put your complicated interval schedule here
if(currentTime.getHours() >= 10 && currentTime.getHours() < 11){
return 5000;
}
else if (currentTime.getHours() > 12 && currentTime.getHours() < 15) {
return 2000;
}
else{
return 1000;
}
}
// give your function name here. the function will be called whenever someone emits intervalName
eventemitter.on(intervalName, sumOfTwoNumbers);
eventemitter.emit(intervalName);
I think It's still firing because there is no clearInterval telling it to stop.

How to run asynchronous tasks synchronous?

I'm developing an app with the following node.js stack: Express/Socket.IO + React. In React I have DataTables, wherein you can search and with every keystroke the data gets dynamically updated! :)
I use Socket.IO for data-fetching, so on every keystroke the client socket emits some parameters and the server calls then the callback to return data. This works like a charm, but it is not garanteed that the returned data comes back in the same order as the client sent it.
To simulate: So when I type in 'a', the server responds with this same 'a' and so for every character.
I found the async module for node.js and tried to use the queue to return tasks in the same order it received it. For simplicity I delayed the second incoming task with setTimeout to simulate a slow performing database-query:
Declaration:
const async = require('async');
var queue = async.queue(function(task, callback) {
if(task.count == 1) {
setTimeout(function() {
callback();
}, 3000);
} else {
callback();
}
}, 10);
Usage:
socket.on('result', function(data, fn) {
var filter = data.filter;
if(filter.length === 1) { // TEST SYNCHRONOUSLY
queue.push({name: filter, count: 1}, function(err) {
fn(filter);
// console.log('finished processing slow');
});
} else {
// add some items to the queue
queue.push({name: filter, count: filter.length}, function(err) {
fn(data.filter);
// console.log('finished processing fast');
});
}
});
But the way I receive it in the client console, when I search for abc is as follows:
ab -> abc -> a(after 3 sec)
I want it to return it like this: a(after 3sec) -> ab -> abc
My thought is that the queue runs the setTimeout and then goes further and eventually the setTimeout gets fired somewhere on the event loop later on. This resulting in returning later search filters earlier then the slow performing one.
How can i solve this problem?
First a few comments, which might help clear up your understanding of async calls:
Using "timeout" to try and align async calls is a bad idea, that is not the idea about async calls. You will never know how long an async call will take, so you can never set the appropriate timeout.
I believe you are misunderstanding the usage of queue from async library you described. The documentation for the queue can be found here.
Copy pasting the documentation in here, in-case things are changed or down:
Creates a queue object with the specified concurrency. Tasks added to the queue are processed in parallel (up to the concurrency limit). If all workers are in progress, the task is queued until one becomes available. Once a worker completes a task, that task's callback is called.
The above means that the queue can simply be used to priorities the async task a given worker can perform. The different async tasks can still be finished at different times.
Potential solutions
There are a few solutions to your problem, depending on your requirements.
You can only send one async call at a time and wait for the first one to finish before sending the next one
You store the results and only display the results to the user when all calls have finished
You disregard all calls except for the latest async call
In your case I would pick solution 3 as your are searching for something. Why would you use care about the results for "a" if they are already searching for "abc" before they get the response for "a"?
This can be done by giving each request a timestamp and then sort based on the timestamp taking the latest.
SOLUTION:
Server:
exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {
socket.on('result', function(data, fn) {
var filter = data.filter;
var counter = data.counter;
if(filter.length === 1 || filter.length === 5) { // TEST SYNCHRONOUSLY
setTimeout(function() {
fn({ filter: filter, counter: counter}); // return to client
}, 3000);
} else {
fn({ filter: filter, counter: counter}); // return to client
}
});
});
}
Client:
export class FilterableDataTable extends Component {
constructor(props) {
super();
this.state = {
endpoint: "http://localhost:3001",
filters: {},
counter: 0
};
this.onLazyLoad = this.onLazyLoad.bind(this);
}
onLazyLoad(event) {
var offset = event.first;
if(offset === null) {
offset = 0;
}
var filter = ''; // filter is the search character
if(event.filters.result2 != undefined) {
filter = event.filters.result2.value;
}
var returnedData = null;
this.state.counter++;
this.socket.emit('result', {
offset: offset,
limit: 20,
filter: filter,
counter: this.state.counter
}, function(data) {
returnedData = data;
console.log(returnedData);
if(returnedData.counter === this.state.counter) {
console.log('DATA: ' + JSON.stringify(returnedData));
}
}
This however does send unneeded data to the client, which in return ignores it. Somebody any idea's for further optimizing this kind of communication? For example a method to keep old data at the server and only send the latest?

Error Handling in a Recursive setTimeout Function in Node.js

I'm building my first node.js application on my Raspberry Pi which I am using to control an air conditioner via LIRC. The following code is called when you want to increase the temperature of the AC unit. It sends a LIRC command every 250 milliseconds depending on how many degrees you want to increase it by. This code works as expected.
var iDegrees = 5;
var i = 0;
var delay = 250 // The delay in milliseconds
function increaseTemperatureLoop(){
i++;
//lirc_node.irsend.send_once("ac", "INCREASE", function() {});
console.log(i);
// Call the fucntion/loop again after the delay if we still need to increase the temperature
if (i <= iDegrees){
timer = setTimeout(increaseTemperatureLoop, delay);
}
else {
res.json({"message": "Success"});
}
}
// Start the timer to call the recursive function for the first time
var timer = setTimeout(increaseTemperatureLoop, delay);
I'm having a hard time working with the asynchronous nature of node.js. Once my recursive function is done, I return my json to the browser as shown in the code above. By habit, I feel like I should return the json in a line of code after my initial function call like below but obviously that wouldn't wait for all of the LIRC calls to be successful - it seems silly to have it inside of the function:
var timer = setTimeout(increaseTemperatureLoop, delay);
res.json({"message": "Success"});
What if I have a bunch of other stuff to do after my LIRC sends are done but before I want to send my json back to the browser? Or what if that block of code throws an error...
My second question is, how do I properly wrap the LIRC call in a try/catch and then if there is an error, stop the recursive calls, pass the error back up, and then pass this back to the browser along with the actual error message:
res.json({"message": "Failed"});
For track end of the cycle execution task, you can use a callback.
In order to know whether completed all routine tasks, you can use the task queue.
Monitor and report bugs to the top - it is possible with the help of
three of the same callback.
In general, it is desirable to wrap everything into a single object.
Some example for reflection:
var lircTasks = function __self (){
if (typeof __self.tasks === "undefined") __self.tasks = 0;
__self.func = {
increaseTemperature: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_TEMPERATURE", function() {});
},
increaseFanPower: function() {
// lirc_node.irsend.send_once("ac", "INCREASE_FANPOWER", function() {});
}
}
var fab = function () {
__self.tasks++;
this.i = 0;
this.args = arguments[0];
this.callback = arguments[1];
this.run = function __ref(taskName) {
if (taskName) this.taskName = taskName;
if (this.i<this.args.deg) {
try {
__self.func[this.taskName]();
} catch(e) {
__self.tasks--;
this.callback( {message: "error", error: e, taskName: this.taskName, task: this.args, tasks: __self.tasks} );
}
this.i++;
setTimeout( __ref.bind(this), this.args.delay );
} else {
__self.tasks--;
this.callback({message:"complete", taskName: this.taskName, task: this.args, tasks: __self.tasks});
}
}
}
if ((arguments.length === 2) && (typeof arguments[1] === "function") && arguments[0].deg>0 && arguments[0].delay>=0) {
return new fab(arguments[0], arguments[1]);
}
}
function complete(e) {
console.log(e);
if (e.tasks === 0) console.log({message: "Success"});
}
lircTasks( {deg: 10, delay:100, device: "d1" }, complete ).run("increaseTemperature");
lircTasks( {deg: 20, delay:150, device: "d2" }, complete ).run("increaseTemperature");
lircTasks( {deg: 5, delay:100, device: "d3" }, complete ).run("increaseFanPower");

How to play back time-related events

To get familiar with Node.js I'm working on a little project where users can play a piano in their browser and all connected users get to hear the keys played. For this I'm using Express with Socket.io.
So far so good: communication works and sounds are heard by all users. My next challenge is to let a user record him playing the piano and then play it back to all connected users.
The recording itself is rather straightforward:
user.socket.on('keyDown', function (message) {
user.socket.broadcast.emit('keyDownReceived', JSON.stringify(message));
if (user.isRecording) {
var key = message.key;
var time = currentTime() - user.recordingStartTime;
user.recording.timeOffsets.push(time);
user.recording.keys.push(key);
}
});
Every time a key is pressed, I fill two arrays. One with the time offsets for each key since the record button was pressed and one with the key. This later gets saved to MongoDB.
My question is how I would go about playing this back to the user. My initial idea was to do the following:
user.socket.on('playRecording', function () {
user.playRecordingStartTime = currentTime();
var interval = setInterval(function () {
var time = currentTime() - user.playRecordingStartTime;
var index = user.recording.timeOffsets.indexOf(time);
if (index != -1) {
var message = {key: user.recording.keys[index]};
user.socket.broadcast.emit('keyDownReceived', JSON.stringify(message));
}
}, 1);
});
So essentially I'm firing a timer every millisecond and doing a reverse lookup in the array to see if a key has been pressed at that time. As you might imagine, this is not ideal and often times keys are skipped because the execution takes too long.
How should such a problem be tackled in Node.js?
Edit: As suggested by Steven, I could just use setTimeout. How embarrassing
user.socket.on('playRecording', function () {
user.recording.timeOffsets.forEach(function (offset, index) {
setTimeout(function () {
var message = {key: user.recording.keys[index]};
user.socket.emit('playKey', JSON.stringify(message));
user.socket.broadcast.emit('keyDownReceived', JSON.stringify(message));
}, offset);
});
});
In your specific case I would recommend looping over the notearray you are playing and creating a new setTimeOut() with each time interval of the including note.
As an example:
var arrNotes = [{ note: "do", time: 1500 }, { note: "re", time: 2500 }],
length = arrNotes.length,
i = 0;
for (var i = 0; i < length; i++){
(function () {
var t = i;
setTimeout(function () {
playNote(arrNotes[t].note);
}, arrNotes[t].time);
})();
}
function playNote(note) {
console.log(note);
}

Resources