Loop over an array/object without split node

Hi all!

I need to loop/iterate over an array/object and do some processing for each element. But I have do do this in series (communicate with an device via serial port and store data) so I cant use the split node since it generates a bulk of messages which can't be handled by the communication procedure.

Who can help me please?

Why cant you use split node?

Maybe if you provide more info like ...

  • A demo flow with sample data (remember we dont have your source data or serial device)
  • Screenshots showing (or explaining) what you are trying to do

... we can show you how or perhpas offer a more sensible alternative.

FWIW - looping is almost always the last option (IMHO) - it is dangerous and can very easily crash node-red if your "break" conditions are not right or buggy or have edge cases.

You can use the split node in conjunction with the delay node set to rate limit and a time greater than process time, once process complete just send flush = 1 to delay node to release the next.
e.g.

[{"id":"7eb5e487ab7668c6","type":"inject","z":"366a43adb328cf95","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[1,2,3,4,5]","payloadType":"json","x":160,"y":60,"wires":[["128da9ebf14e652a"]]},{"id":"128da9ebf14e652a","type":"split","z":"366a43adb328cf95","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":310,"y":80,"wires":[["2516c585c614ca26"]]},{"id":"2516c585c614ca26","type":"delay","z":"366a43adb328cf95","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"60","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":480,"y":80,"wires":[["2e32b43911ef6645"]]},{"id":"0ded4ecceb959c0f","type":"inject","z":"366a43adb328cf95","name":"reset","props":[{"p":"reset","v":"true","vt":"bool"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":130,"y":120,"wires":[["2516c585c614ca26","2e32b43911ef6645"]]},{"id":"37b1174d8b84ce12","type":"function","z":"366a43adb328cf95","name":"procees and send flush","func":"msg.payload = msg.payload*10\n\nreturn [msg, {flush:1}];","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":670,"y":20,"wires":[["7c729254e37390c0"],["2516c585c614ca26"]]},{"id":"2e32b43911ef6645","type":"delay","z":"366a43adb328cf95","name":"simulate proccess delay","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":430,"y":20,"wires":[["37b1174d8b84ce12"]]},{"id":"7c729254e37390c0","type":"debug","z":"366a43adb328cf95","name":"debug 101","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":80,"wires":[]}]


I need to process the "1" via serial port (and wait for the answer), then process the "2" via serial port (and wait for the answer),... you got it!

In the end I need an object like { "1": { "answer": 0 }, "2": { "answer": 0 }, "3": { "answer": 0 } }

You can use a Delay node set to Rate Limit as a queue. Set the rate a bit greater than the time to process a single message.
When your answer arrives, send a message to the delay node with no msg.payload but msg.flush = 1. The next split message will be released from the queue

Another option is to inject your array into a function node, store it and pop off the first item until all items are retrieved. The function feeds out an individual item each time to the serial node. The serial node communicates it back to the function and the function reacts accordingly.
image
Yes, I put a link call node in place of the serial node because I don't have a serial node.

Inside your function, you would have a setup like this:

if(msg.topic == "inject"){  //Check to see if it's the initial inject message
    var tempArr = msg.payload;  //Setup a variable to store the array
    msg.payload = {};  //Empty msg so it's not contaminated with old data
    msg.payload = tempArr.shift();  //Shift off the first item in the array into payload
    msg.topic = "getser";  //Change the topic so you know you're looping now
    context.set('tempArr',tempArr);  //Save the remaining array for later reference
    return msg;  //Send out the number for serial processing
}
else{  //Not the initial injection
    var serObj = {};  //Setup a temporary object to store your answers
    serObj[msg.payload.number] = msg.payload.answer;  //Assign the object ID that just came back the value it just received (assuming an object came back)
    msg.payload = {};  //Empty msg to avoid old data contamination
    var temp = context.get('tempArr');  //Get the array again
    msg.payload = temp.shift();  //Get the next array item into msg
    context.set('tempArr', temp);  //Store the remaining array for later processing
    return msg;  //Send out the number for serial processing

Now, my code above is obviously missing a lot of stuff. I don't have the serial node, so I don't know how it returns the data. You would also setup the context.set() items in your function startup and not in the function itself so they were already for referencing. You would need to have a context.set() for your object of objects already setup to store the object you created in the else included in that. There's several things that would need to be added/changed and I'm not advertising it as a turnkey solution. Just one that illustrates how it would be done.

  1. Get the initial array.
  2. Setup the msg with the needed items to send to the serial node.
  3. Store the remaining array.
  4. Send the msg.
  5. Msg returns.
  6. Add the msg data to your object of objects.
  7. Retrieve the remaining array.
  8. If the array is empty, publish the data and end (may need a second function output for this)
  9. Setup the msg again with the needed items to send to the serial node (if applicable).
  10. Store the remaining array (if applicable).
  11. Send the msg.

With a little addition, this loop could loop indefinitely by resetting the array every time it published the output. That way your inject just becomes your trigger and your function just keeps responding and looping. This would allow you to have a self-refreshing dashboard of charts/graphs/etc. that your function keeps updated based off the serial data, which is where I assume you're going with this.

In any case, this is just a suggestion. If it doesn't apply, either tweak it until it does or follow advice that better suits your use condition. Enjoy!

Split your array by the split node and then buffer all messages in this node:

https://flows.nodered.org/node/node-red-contrib-simple-message-queue

The buffers output you may trigger with the message {trigger:true}
You may reset the buffer with the message {reset: true}

The Nodes Help Section will help you to get things done.

It is simpler to use a Delay node in Rate Limit mode, using flush to release the next message, as suggested earlier.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.