Best way to route messages through one set of nodes?

So I'm going to create a pure Node-RED (no 3rd party nodes or libraries) Modbus TCP client, and I'd like to know if there's a better way to send messages through a common set of nodes.

Modbus TCP servers basically have a limit of just a few connections, or otherwise I'd throw all this in a subflow and reuse, but since this limit exists I need to send all messages through one TCP request node, and make sure they make their way back to the original sender.

I'm aware that I can have multiple inputs and/or outputs in a function node and route things that way, but that's messy and ugly, and I just don't like the idea. I've come up with the following pattern:

Up top would be a typical chunk of nodes using the TCP client, and down below is the client itself.

The idea is that the function node up top would add a transaction ID to the message, send through the link to the client, and receive it back into the same function node. The code in the function node would basically check if a transaction ID is present AND it was the last one sent out of that function node (will have many parts of flows using this, so need to differentiate who sent that particular message). Something like this in the "Send msg / process response" function node:

if (msg.transID > 0) {
  // response received
  if (global.get(`TransIDs['${node.id}']`) === msg.transID)) {
    // it's the one I'm looking for, process the response
  }
} else {
  // request
  const transIDs = Object.values(global.get('TransIDs') || {});
  let transID;
  do { // generate random transaction ID but make sure it's not being used
    transID = Math.floor(Math.random() * 65534) + 1;
  } while (transIDs.includes(transID));
  msg.transID = transID;
  global.set(`TransIDs['${node.id}']`, transID);
  // blah blah blah
  return msg;
}

This is probably just as messy as having multiple inputs/outputs with function nodes, but oh well. Anyway, is there a better or other alternative pattern to what I've mentioned?

1 Like

I've considered writing drivers in node-red only - I think it is perfectly doable.

Some thoughts off the top of my head...

  • Consider (if possible) creating FN3, FN4, FN15, FN16 etc FBs from sub flows. You can pass into the subflow address, length, values, connectionID etc.
    • note, if any subflow shares functionality, you can nest subflows (e.g. a common CRC calc subflow could be used insode your FN3/FN4/FN15 subflows)
  • TransactionID - I would just round robin, By the time you get back round from 1 ~ 65535 back to 1, I would consider msg 1 to be stale, timed out or complete (depends on the amount of transactions & timings but you may be able to calculate that & warn user if he will "exhaust" the available 65535 transaction IDs

Thats all I have for now.

Following.

Hi @Steve-Mcl,

Thought about doing subflows -- could do different ones for the different FCs but really it wouldn't be much different. The beauty of Modbus is that most of the common messages are constructed the same way, with FC 15 & 16 being somewhat odd.

Thinking more about it, the one change I would do to my original post is to match the transaction ID then return the msg to another node for processing (instead of processing within the same function node right after matching trans ID) -- mark with with a msg.isResponse property when it goes out or something so that way the function node can be re-used untouched. Hope that makes sense, probably illustrated better with another picture but I'm lazy.

Could do round-robin for trans ID yeah. Could then find the max of the stored transaction IDs and add 1 to get the new transaction ID.

No CRC needed for TCP, but relevant I will also be doing this for Modbus RTU (serial). @dceejay already made the change to the serial request node that I needed for this, just haven't put it all together yet.

Really don't like any of the 3rd party Modbus nodes -- either inflexible or as bloated as a constipated gorilla.

do you really mean 0.05 msgs/s ? sounds very slow - even for Modbus... 1 every 20s.

Lol, good catch. Should be 200 msgs/sec -- I typically use a 5ms delay.

I would possibly think about subflows (with one eye to the future) - as in Node-RED 1.0 you can create the subflow properties such that they look more like a custom node - (ie can have checkboxes, selects, as well as text input etc) - and custom icon / colours...
(The next part (not for 1.0) is then to allow them to be exportable as custom nodes - so may be worth considering..)

Not to mention, the ones I tried (in my early days of node-red) actually crashed node-red & had issues re-connecting.

(I should have raised issues on github but I was a bit less knowledgeable then & thought it was something I'd done!)

The new features of NR1 make subflows even more attractive :+1:

@dceejay sounds good. Need to play around with those more anyway.

In an ideal world, someday I would submit a PR for some real built-in Modbus nodes for Node-RED...IMO it should have at least Modbus TCP in, out, request nodes to go alongside everything else, since Modbus is so super common in industrial automation.

@Steve-Mcl Agreed -- and most maintainers of said packages either don't respond to issues on GitHub or dismiss them, so you didn't miss out or hurt anything but not raising the issues....

node-red-contrib-modbustcp would be good but it needs to be more focused on single requests/responses rather than creating new auto-polling listeners. Let the built-in inject nodes handle repeating things.

You could always fork it & improve it (they may accept PRs?), however, I think it will be an excellent show piece for node-red to have a comms driver written for node-red, in node-red :+1:

Have a working flow with function codes 1-6, 15, & 16 working but using ordinary JS functions stored in the global context.

I was testing and it occurred to me that the lack of a no-nonsense TCP server is also a sore spot. I was using the Modbus Server node from node-red-contrib-modbus with show errors checked...no errors or response exceptions (e.g. illegal data address, illegal function, etc.) when abused.

Will make the TCP client into subflows...and then write a TCP server too...one that allows direct register data manipulation via context instead of having to use client nodes...and throw it up on GitHub.

1 Like