Array Value Backfeeding Across Function Blocks

#1

Hi folks,

I'm stumped. I created a function block that is intended to take an input message and count the number of times a value in a certain bin is received. This block seems to be working correctly. As values are received the corresponding array step in the counter is updated. Note that the object "bin" is initialized in a startup node separately. Code below.

Start function node block code to initialize global object.

global.set("bin",{});
global.set("bin.labels", ['25%', '50%', '75%', '100%', '125%', '150%', '>150%']);
global.set("bin.data", [[0, 0, 0, 0, 0, 0, 0]]);
global.set("bin.series",['Series 1'])

Bin function node code to put received value count into array index that corresponds to % of full scale value.

var localcount;
var step;
var comphigh;
var complow;
var index;
var totcountloc;
var arr;

totcountloc = global.get("totalcount");
totcountloc = totcountloc+1
global.set("totalcount",totcountloc);

for (step = 25; step <= 150; step += 25) {
  comphigh = (global.get("maxtorque") * step * 0.01)
  complow = (global.get("maxtorque") * (step - 25) * 0.01)
  
  if(msg.payload > complow && msg.payload <= comphigh){
    
    arr = global.get("bin.data")
    index=step/25-1;
    localcount = arr[0][index];
    localcount++;
    arr[0][index]=localcount
    global.set("bin.data", arr)
    
    }
}

console.log(global.get("bin"));

return {payload:global.get("bin"),topic:msg.topic};

Again, everything works here. But, as soon as I add a second function node downstream to normalize the counts to 100% vs a true count the bin.data object starts to fail. (multiplied by 5 rather than dividing by total count to simplify example).

Second function block to normalize counts to 100%.

a = msg.payload.data
for(var i=0; i<a.length; i++) {
    a[i] *= 5;
}

console.log(a)

Am I doing this the hard way? Not sure why the bin.data array is being affected at all. If I remove the "Normalize Bins" node counts increment as expected. Thanks for any advice. Zach

My console ends up looking like this.
Screenshot of nodes for reference.

0 Likes

#2

JavaScript passes objects by reference. So when you set msg.payload to global.get("bin") then you are passing a reference to the array held in memory within global context. Any changes to the array later in the flow will be reflected in the version in global context.

To avoid this in, Node-RED clones message objects whenever a flow branches. But if a node is wired to just one other, it tries to optimise the flow by not cloning the message. This is what you're hitting.

To avoid this, you can explicitly clone the message to break the reference:

return RED.util.cloneMessage({payload:global.get("bin"),topic:msg.topic});
0 Likes

#3

Got it. Thank you sir, this was super helpful. Everything works as expected now.

0 Likes