Function node, how to send text out 5 times to debug node?

Hi,

If I receive a number greater than 80, output the same message 5 times, this goes to a queue and speech.

if (msg.payload >= 80) 
{

   for (let i = 0; i < 5; i++) 
   {
      var Text_to_speak = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ..... ";

      msg.payload = Text_to_speak;

      return msg;
   }

}

Thanks

use node.send(msg)

also, you should clone the msg before updating to avoid "by reference" weirdness that you can fall foul of in async systems.

if (msg.payload >= 80) {
   for (let i = 0; i < 5; i++) {
      const m = RED.util.cloneMessage(msg)
      m.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ..... "
      node.send(m)
   }
}

It is all in the docs.

HI Steve,

Thank you for that and as I would expect from you it does work :slightly_smiling_face:

One thing I have noticed lately, code that I wrote a few years ago and then I come back to it today, I can no longer understand the more technical words, so I try and keep all my code as logical and simple as possible :+1:

So i have modified your code a little and it does seem to work, is there anything wrong with doing it this way ?

Thanks for your help

if (msg.payload >= 80) 
{
   for (let i = 0; i < 5; i++) 
   {
      msg.text = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ..... "
      msg.payload = msg.text
      node.send(msg)
   }
}

At first glance no. but, since msg is an object, and NodeJS/Node-RED is highly asynchronous, there is a risk that when you alter msg.payload that the other 4 are altered (if they are still in-flight). That is what as known as "by reference"

essentially an object (like msg) is sent by reference (think C/C++ pointer to an object) so changing anything in msg leaves you vulnerable to side effects - that's why i added the RED.util.cloneMessage


If you want clarity later on, add comments...

if (msg.payload >= 80) {
   // loop 5 times, to generate 5 messages
   for (let i = 0; i < 5; i++) {
      // clone msg into a NEW separate variable `m` so that we dont alter any in-flight messages
      const m = RED.util.cloneMessage(msg)
      // update the payload of the clone
      m.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ..... "
      // send the clone msg
      node.send(m)
   }
}
1 Like

Since @questuk is sending the same message 5 times, wouldn't this be simpler?

if (msg.payload >= 80) 
{
   msg.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ..... "
   for (let i = 0; i < 5; i++) 
   {
      node.send(msg)
   }
}

HI Steve,

Thanks for that, I mainly write Arduino code and nearly every line has comments :+1:

When I saw your code, I instantly looked online to understand and add my comments, but I couldn't find much understandable info.

Thanks for updating me on that and the reasons why, I now understand it.

Thanks

Yes, but I assume something will be changed (better to be safe than have a hard to track bug)

Since node.send() does not clone the message and since objects are stored by reference, all the messages would point to the same object in memory, therefore if a later node were, for example, to do
msg.payload = 7
then that would affect the payload of all 5 messages (since they all point to the same object in memory).

Yes, I see that if these messages are passed on beyond whatever speaks the message, for example to send an email, changes made to message 1 after speaking will also affect messages 2 - 4 still in the speech queue.

I assume that these five (ie 5 instances of this single) message are entirely seperate from any other msg object flying around Node-red.

Hi,

Yes it is meant as a warning that there is a possible leak in the Loft and is announced on a speaker.

I also have sensors that monitor temperature in most rooms.
As an example 2 rooms could both get excessive temperature do to a fire in one of them.
So I would have 2 warnings that would both announce over the speaker which room has the excessive heat.

So in this example, I guess the 2 rooms messages would get mixed up as both Colin and Steve mentioned?

But with the code that they have given me, one warning would complete then the other warning after that and then if still excessive heat they would continue to announce without getting mixed up.

I think that is how it would now operate ?

Thanks :+1:

Not if those were as a result of a new message passed into your function.

I strongly recommend using clone() in cases like this, even if you think it is not necessary. The saving in not doing so is insignificant (unless you have messages containing large objects such as video, images or files) and it can be a nightmare trying to track down bugs caused in this way as they can be timing dependent and the symptom may not be seen till much further down the flow.

Hi Colin,

This is my code now...

if (msg.payload >= 80)
{

   for (let i = 0; i < 5; i++) 
   {
                                                                                            
      const text = RED.util.cloneMessage(msg)                                               // Clone msg into a NEW separate variable `text` so that we don't alter any in-flight messages.                                                                            
      text.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ... " // Update the payload of the clone.
      node.send(text)                                                                       // Send the clone msg.

   }
}

I think this has everything you mentioned.

Thanks

One of the most important ways of documenting code so that you will understand it when you come to look at it later is giving variables meaningful names. After the above line you have an object called text, but it isn't the text, the text in is text.payload. It is a message so I would call it something like msg2.

Also you are executing text.payload = ... every time round the loop, which is pointless, move that outside the loop.

if (msg.payload >= 80)
{
   msg.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ... "
   for (let i = 0; i < 5; i++) 
   {
      const msg2 = RED.util.cloneMessage(msg)                                               // Clone msg into a NEW separate variable `msg2` so that we don't alter any in-flight messages.                                                                            
      node.send(msg2)                                                                       // Send the clone msg.

   }
}

In fact there is no need for msg2 at all, inside the loop just use
node.send(RED.util.cloneMessage(msg)) // clone the message and send the clone

1 Like

Hi Colin,

I have adjusted the function as you stated all works ok.

if (msg.payload >= 80)
{
   msg.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ... "
   
for (let i = 0; i < 5; i++) 
   {
      node.send(RED.util.cloneMessage(msg)) // clone the message and send the clone

   }
}

That is not what the docs say

The Function node will clone every message object you pass to node.send to ensure there is no unintended modification of message objects that get reused in the function. Before Node-RED 1.0, the Function node would not clone the first message passed to node.send, but would clone the rest.

The Function can request the runtime to not clone the first message passed to node.send by passing in false as a second argument to the function. It would do this if the message contains something that is not otherwise cloneable, or for performance reasons to minimise the overhead of sending messages:

node.send(msg,false);

@E1cid

Hi, Yes I read that earlier today, so I am a bit confused :thinking:

You should be able, according to the docs just use node.send(msg,false)

Scrap that just use node.send(message). Tired, had a long day.
no need for RED.util.cloneMessage()`.

Actually I do want it to clone the first message only, so it would be ...

node.send(msg);

?

No problem have a restful evening :slightly_smiling_face:

1 Like

Hi all,

So it looks like this is the final code
Tested from 2 different rooms each sending a different warning, almost sent at the same time.
All announced correctly and each room fully completed speech, before announcing the other room warning.


if (msg.payload >= 80)
{
   msg.payload = "Humidity exceeded ... in the ,,, LOFT ... Module ,, Twenty Five ... "
   
   for (let i = 0; i < 5; i++) 
   {
      node.send(msg); // clone the message and send the clone

   }
}