Optimizing Node Red Flow

@philip, @torchard, @Beno

Thank you for all your help on both my threads.

I ended up going back to multi injects that looks kind of like ladder logic. I hope that will help co-workers when they are troubleshooting. With the addition of the RBE “rung” and moving all scratchpad reads to a single read, it seems to operate acceptably. Total CPU usage is normally around 60-80%, with regular spikes up to 95+%.

I look forward to continuing to use Opto products on future projects.

-Caleb

1 Like

If you are going to set it up like that, could you consider something like this:

This way, rather than slamming all the groov nodes at once a second on the second, you spread the love by 0.5 of a second and at least give things a beat to breath?
You might even be able to go down to 0.2 seconds and get to the bottom of the chain around the same time, but at least not everything is getting hit at the exact same time.

3 Likes

@cbohon Did you get a chance to test the delay nodes idea?
I wonder what the CPU went down to using this staggered method?

As an FYI…
I have found in the past that if you fetch ‘all at once’ (triggering several PAC Read nodes at once), especially when reading tables, you can get connection errors. Daisy chaining from a single Inject node, as Beno shows in his example flow, overcame this problem.

This is on a PAC R1 and iirc the error was something like ECONN RESET

1 Like

@Beno

The delay works very well. I have 9 “rungs” in the flow, and I put in 0.1 second delay for each rung. I also slowed it down to run only every two seconds, so that gives it about 1 second to run the flow, and 1 second of buffer before the next start. I hope that helps writes all finish before the next start of the flow.

CPU usage is 20% to 60% with occasional spikes higher than that.

Thanks for all the help,
-Caleb

Thanks so much for running the test and reporting back.
Glad to see that it really did help your CPU use drop a good amount.
Time slicing is so important for most ‘code’. Good to see it put in practice in your flows.

Continuing the topic of Opto-mizing Node-Red.

Node-Red is great because its easy. The Opto22 nodes are great because its easy to get data to and from PAC or EPIC controllers.

But its also easy to get excited about that and before you know it…
Add a read node here and here and here and there and there and there……. Until… you either crashed Node-Red or rendered network connection to your controllers useless or unstable at best.

@Beno and @torchard have eluded to some best practices in this thread and the forum and I always defer to them.

These strategies or guidelines are a distillation of the Opto22 Forum knowledge base and my experiences when reading data from controllers using Node-Red.

1. Centralize reading the controller(s).

Read each data type or IO from a controller once and only in one location.
This helps to organize your interactions with the controllers. (I prefer one flow tab dedicated to this)

2. Tags in bulk.

Return all tags for a each data type needed. By using global context variables and/or link nodes all tags are available to any flow in Node-Red without hitting the plc again.
*(More on global context below)

3. One request per controller at a time.

Read each data type or IO from a controller one at a time by linking nodes in series with a small delay between requests. This helps to avoid overwhelming the controller, and minimizes the chance of timeout.

4. Request interval

The interval between data updates will depend on your application. The polling rate is set at the first inject node. Ideally your timing should allow all requests to be returned before the inject node hits again to prevent overlap.
Central Read

*Global Context is a powerful concept in Node-Red if you don’t use them consider it.

I use a function node to convert the array of objects:

[{"name":"f_Blanca_Hi_Alarm_Setpoint","value":18},
{"name":"f_Blanca_Lo_Alarm_Setpoint","value":15.5},
{"name":"f_Blanca_Tank_Level","value":16.5236397}]

to a single object of key value pairs (name:value).

{
"f_Blanca_Hi_Alarm_Setpoint":18,
"f_Blanca_Lo_Alarm_Setpoint":15.5,
"f_Blanca_Tank_Level":16.5236397
} 

Now all tags names from the strategy can be called easily in Node-Red from the object stored in the global context .
(I also add a timestamp to each global variable object just incase I need to know how old that data is).

// Data array from controller 
const array = msg.payload;

// Declare new object
var values = {};

// Timestamp
values.timestamp = new Date().getTime();

// Convert array of objects to single object of key vaule pairs
for (var i = 0; i < array.length; i++) {
    values[array[i].name] = array[i].value
    };

// Set to global context
global.set("int32_var", values);
return (msg);

This code chunk can be used for data arrays returned from a read node, just change the global context key string “int32_var” to whatever you want.

global.set("int32_var", values); 

Hope this helps someone out there, I would love to hear feedback or about the deployment strategies you use with your organization.

-G

9 Likes

Far out! @gchase, what an amazing summary of all the best tips.
Adding the context variables code sample is just gold.
Really solid advice, tips, and best practices all in one.
Thanks so much for taking the time to put all this together in one sweet post.
I will be giving this post a shout-out on our Twitter/socials for sure!

3 Likes