One Function node instead of multiple change nodes!

I have a lot of change nodes which sets new properties to the msg.payload as shown below. I want to use function node instead of multiple change nodes which are complex to read! How can I achieve this? Please help, I am new to Node-Red!

Regards,
Mukara

I tried to implement the above requirement using function node (tested only for 2 variables), but can't get the desired output! It reacts nothing!

Any Idea how to achieve this using function node? Please advice!

Regards,
Mukara

In a function node you can use simple assignment statements like this:

msg.payload.event = "speed skating";
msg.payload.location = "Lillehammer";
msg.payload.time = 107.68;

Thank you @jbudd

I tried this below function its not working either,
Function:

msg.topic = 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"';
msg.measurement= 'ExGrafanaDBMachineStateDataModeTimeProductionStart';

msg.topic = 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"';
msg.measurement= 'ExGrafanaDBMachineStateDataModeTimeProduction';

Any other suggestions are also welcome!

Regards,
Mukara

In what sense not working?

Do you really want to set msg.topic to that long and complex string, or are you hoping to include values from somewhere else?

So that we can understand what you are doing, share some of your data and the msg you want to create from it.

When I connect this function node between time trigger node & OPC UA client node, its not returning anything like its returned with individual change node for each variable (as shown in my original first post)!

I am trying to read all the available values from opc server running on siemens PLC and store them all (on value change) in Influx DB as measurements.

In order to use OPC Ua Client node which reads & stores them all into the database server (Influxdb), I have to set the msg.topic & msg.measurement for each variable which I want to store!
Flow:
FYI From OPC UA Client node & afterwards everything works fine!

PFA flow file (.json)

Read_Multiple_Data_from_OPC_Server.json (3.7 KB)

This is what I created in order to store data from siemens opc server in influxdb!

Please feel free to advice/correct my implementations. I will be grateful for each & every tip!

Regards,
Mukara

Put debug nodes to show the entire message object at these points and compare the outputs.

Since I don't have a Siemens opc server or influxdb, or even necessarily know what they are, and you have not provided sample data, I will only point out that you probably only get one message from the function node versus two from the two change nodes.
You can send multiple messages from a function with node.send(msg);, or with return [msg1, msg2];

So this might work

msg.topic = 'a long string';
msg.payload = 'another long string';
node.send(msg);
msg.topic = 'a third long string';
msg.payload = 'yet another long string';
return msg;
1 Like

Just a note that the code as written won't work if the incoming payload isn't an object.

The safest approach is something like:

msg.payload = {} // blank object
msg.payload.event = "speed skating"
// ... etc ...
1 Like

Based on the function that you have listed i think you are missing a fundamental concept in relation to NR

Each message is a distinct entity - so in the case of the function code above - you have set the msg.topic and the msg.measurement twice. The function node will overwrite the first one of each that you set with the 2nd one - and you will get a single message spat out that has the last values - in your case

What we need is a debug node on the incoming data from the Siemens PLC showing the complete message object that comes in and we can then advise you on the best way to tackle it

Craig

It worked perfect.Thank you very much :slight_smile: @jbudd

Function Node (instead of 3 change nodes):

msg.topic = 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"';
msg.measurement = 'ExGrafanaDBMachineStateDataModeTimeProductionStart';
node.send(msg);

msg.topic = 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"';
msg.measurement = 'ExGrafanaDBMachineStateDataModeTimeProductionEnd';
node.send(msg);

msg.topic = 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"';
msg.measurement = 'ExGrafanaDBMachineStateDataModeTimeProduction';
return msg;

Debug Logs:

Debug from changes nodes as well (as you mentioned):

Thank you so much for your suggestions/Help @craigcurtin

The OPC Client node can't read out the entire structure or PLC datablock (extension object), I tried to read out the entire datablock, but it returns null value! (see below screenshot)!

So far the only possibility is that we can read each & every variables/items individually :frowning:

But here is the structure of variables from Siemens PLC via OPC Protocol:

I tried using the opc items node also, to read out the entire structure (MachineState), but I received "TypeError: Cannot read property 'length' of undefined" :frowning:

Please feel free to advice anything on this topic.

Regards,
Mukara

You may wish to note that doing it that way can occasionally catch you out since msg is a REFERENCE to the data and so making changes to it changes everywhere that the msg happens to be in use at the time of change.

While that won't probably impact you here, it can sometimes catch you out if your downstream nodes process things asynchronously.

To be really safe, this should work:

return [ [
    {
        topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"',
        measurement: 'ExGrafanaDBMachineStateDataModeTimeProductionStart',
    },
    {
        topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"',
        measurement: 'ExGrafanaDBMachineStateDataModeTimeProductionEnd',
    },
    {
        topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"',
        measurement: 'ExGrafanaDBMachineStateDataModeTimeProduction',
    }
] ]

The outer array sends to the first output port (only 1 in this case) and the inner array sends multiple independent messages.

1 Like

Thanks a lot @TotallyInformation

Can anyone tell me why my below 'for loop' program is not working?
Error:
"Function tried to send a message of type Array"

Function:

topic_array = ['NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"',
				'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"',
				'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"'
				];
				
meas_array = ['ExGrafanaDBMachineStateDataModeTimeProductionStart',
				'ExGrafanaDBMachineStateDataModeTimeProductionEnd',
				'ExGrafanaDBMachineStateDataModeTimeProduction'
				];



function append_array(topic_array, meas_array){
    var totalCount = topic_array.length ;
    const appended_array = [];
    //append dicts to array
    for (var i = 0; i < totalCount; i++){
    	appended_array.concat({topic: topic_array[i],measurement: meas_array[i]});
        }
    return appended_array;
}
    
msg= [
    [append_array(topic_array, meas_array)] 
];

return msg;

I would be grateful, If someone could help me find the mistake in above program!

Thanks in advance.

  1. append_array returns an array
  2. you set msg to [ [ the_array_returned_by_append_array ] ]

e.g. you are returning [ [ [obj, obj, obj] ] ]

Try this...

const topic_array = [
  'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"',				 
  'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"',
  'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"'
];
				
const meas_array = [
  'ExGrafanaDBMachineStateDataModeTimeProductionStart',
  'ExGrafanaDBMachineStateDataModeTimeProductionEnd',
  'ExGrafanaDBMachineStateDataModeTimeProduction'
];
const messages = topic_array.map((e,i) => { return {topic:e,measurement:meas_array[i]} })
return [messages]

That said: I assume some of this is dynamic - otherwise you would simply return an array of objects right?

Just in case: this will work...

return [
 [
  {
    topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"',
    measurement: 'ExGrafanaDBMachineStateDataModeTimeProductionStart'
  },
  {
    topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"',
    measurement: 'ExGrafanaDBMachineStateDataModeTimeProductionEnd'
  },
  {
    topic: 'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"',
    measurement: 'ExGrafanaDBMachineStateDataModeTimeProduction'
  }
 ]
]
1 Like

Thanks a lot @Steve-Mcl

it works perfect. I want to program it as a loop, because its easier if my machine has 1000 variables. I just have to define an array with 1000 elements (topic & measurment) and the msg array formation will be done by the function (future oriented), no need to form 1000 dictionary inside the array in hard coded way.

Thank you once again :slight_smile:

Best Regards,
Mukara

You can, of course, use node.send inside your loop and that is absolutely fine. Just create the output object for each send rather than reusing the incoming msg object.

1 Like

How do you mean @TotallyInformation ? Like this below one?

timestamp= new Date().getTime();

topic_array = ['NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionStart"',
				'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProductionEnd"',
				'NodeID;ns=3;s="ExGrafanaDB"."MachineState"."Data"."Mode"."TimeProduction"'
				];
				
meas_array = ['ExGrafanaDBMachineStateDataModeTimeProductionStart',
				'ExGrafanaDBMachineStateDataModeTimeProductionEnd',
				'ExGrafanaDBMachineStateDataModeTimeProduction'
				];



function append_array(topic_array, meas_array, timestamp){
    var totalCount = topic_array.length ;
    const appended_array = [];
    //append dicts to array
    for (var i = 0; i < totalCount; i++){
    	appended_array.concat({topic: topic_array[i],measurement: meas_array[i], timestamp: timestamp});
    	node.send();
        }
    return appended_array;
}

msg= append_array(topic_array, meas_array, timestamp) ;


return msg;

This function does not output anything! What is wrong here?

Yes, well, nearly. You aren't getting any output because you've not passed a msg object to the send function. Send takes the same data that return does.

thanks a lot @TotallyInformation , The below code is working perfect :slight_smile:

timestamp= new Date().getTime();

topic_array = ['NodeID;ns=3;s="ExGrafanaDb"."MachineState1"."Data"."Mode"."TimeProduction"',
                'NodeID;ns=3;s="ExGrafanaDb"."MachineState1"."Data"."Mode"."TimeProductionStart"',
                'NodeID;ns=3;s="ExGrafanaDb"."MachineState1"."Data"."Mode"."TimeProductionEnd"'
				];
				
meas_array = ['ExGrafanaDbMachineState1DataModeTimeProduction',
                'ExGrafanaDbMachineState1DataModeTimeProductionStart',
				'ExGrafanaDbMachineState1DataModeTimeProductionEnd'
				];

function append_array(topic_array, meas_array, timestamp){
    var totalCount = topic_array.length ;
    for (var i = 0; i < totalCount; i++){
    	msg= {topic: topic_array[i],measurement: meas_array[i], timestamp: timestamp};
    	node.send(msg);
        }
}
append_array(topic_array, meas_array, timestamp) ;

Regards,
Mukara

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