How to use other node instances / objects across nodes

Hey hey,

Rewriting my post to put it simpler:

What is the best way between nodes to have a forward backward communication when they are both internally instantiated as objects?

I have written a flow variable and am pushing the objects there for communication now but perhaps there is a better way of doing this.

In my situation I have 1 pump group node which will fetch all the objects of type pump from a flow object. From there he will perform calculations on it and then send an output through the usual proces to the node itself.

The pump group node should also make decisions based on time related variables on the pump node itself. So it needs updates x amount of times per n seconds.

Im really looking for an elegant solution on this one and perhaps Im allready using the most elegant one in using the context.flow variable but just want to make sure im not overcomplicating things.

One way:

In your project.

  • Define a config node.

  • This should expose a method to allow child nodes to register a callback, and store it during runtime as you see fit in your config node instance

    registerCallback(NodeID:string, Callback:(data:unknown) => void)

  • Expose a second method

    publish(data:unknown)

Your controller/config node, should then send this to all its subscribers, when publish is called.

Child Node 1

myConfig.registerCallback(this.id,(data) => {

});

Child Node 2

myConfig.publish({...});

Controller

publish = (Data:unknown) => {
    
    subscribers.forEach((S) => S.Callback(Data)); /* S.id, S.Callback*/
}

there is also RED.events - by I tend to stay away from it.

1 Like

OK, I'm being a bit thick today after my mamoth 15 hr working day yesterday, I'm not understanding where the registerCallback function comes from? Does Node-RED expose that function? I couldn't find a reference with a quick search.

15 hr :exploding_head:

registerCallback will be a method you define in a config Node.
it can then be called by Nodes that uses it.

/* Config Node */
module.exports = (RED) => {
    const init = function(this, Config) {
        const self = this;
        RED.nodes.createNode(self, Config);
        self.config = Config;

        self.registerCallback = (...) => {
            // Store Callback
        }

        self.publish = (...) => {
            // Send message to all registered callbacks
        }
    }
}

/* Child Node */
module.exports = (RED) => {
    const init = function(this, Config) {
        const self = this;
        RED.nodes.createNode(self, Config);
        self.config = config;
        self.configNode = RED.nodes.getNode(self.config.configNode)

        self.configNode.registerCallback(...)
    }
}

I was using RED.events before I went with this approach, but as my project become more advanced - I needed more control over its behaviour

Ah, I see. That was the part I was missing. Thanks.

I use the node.js events library for this rather than a separate custom callback process. It's pretty much the same thing I suppose.

Events | Node.js v19.9.0 Documentation (nodejs.org)

1 Like

Thanks both. Interesting approaches. I was reading up on the events nodejs emitter objects.
I have another question then though. I figured that this is also how node red itself works.
Each wire id is the code on which other nodes listnen?
So my question is if I build in some sort of check that connects nodes recursivly ( i mean like 1 of the output is the other ones input ) Never ending cirkel and only emit a msg on it whenever there is a change event.
Does this make things messy in your view and should I persue the eventemitter strategy to take care of that or does it make sense for nodes to have 1 output fed back to the input of the node that is before it.

This will make the visuals a bit messy however it is a much simpler method than adding listners and defining all of the other work which is allready baked in the node red code right? Or am I going to lose time / performance this way?

Thanks again for your view on the matter.

Adding wires autonomously, checking for input/outputs sounds complicated (and messy IMO).

I still suggest using RED.events or my call back suggestion above etc etc.
its pretty trivial for RED.events

RED.events.emit('<UNIQUE-TO-YOUR-CONTRIB>:SOME-EVENT',{"Hello":"World"})
RED.events.on('<UNIQUE-TO-YOUR-CONTRIB>:SOME-EVENT',(Data) =>{
 Data.Hello // World
})

All your nodes can then use this domain <UNIQUE-TO-YOUR-CONTRIB>:xxxxx

Just tailor your needs around the RED.events API.
Flow wiring, should be kept for userland, and shouldn't be relied upon for your internal logic to function (IMO)

RED.events is not documented as being for general use however :sweat_smile: hence my suggestion or @TotallyInformation to use your own instance of the emitter

1 Like

Gotja.
Cheers once more.

I also found this.

import { EventEmitter } from "events";
import express from 'express';

const eventEmitter = new EventEmitter();

const app = express();
app.set('eventEmitter', eventEmitter);

// access it from any module of the application
console.log(app.get('eventEmitter'));

A bit like context.set but from node js.
Going to test it tomorrow. This removes the need for a config node if this works together with the emitter function.

But does that mean you have to pass app to your nodes?

I have done the following (and what @TotallyInformation does I believe)

1 - create a module that exports the emitter (below)

Then in my modules, just call the emitter methods.

Node JS will cache the instance - so the same instance will be be used/shared

const { NodeEventEmitter } = require('./events');

// Then my nodes have the following to call into
NodeEventEmitter.on
NodeEventEmitter.emit

This remove the risky method of piggy backing on RED.events or adding stuff to the express runtime

Similar but I have a module that contains a singleton class that uses the events2 library because that is both faster than the native events and it supports wildcards which is very useful. The module is reused in several of my nodes.

Hey Marcus,

I just tried it (express) and the answer is no haha.
I would have to install express module. (and then probably link it somewhere in the settings.js I presume to make it work globally throughout the modules).

So it does essientially the same thing than doing what you did.

Ill try the approach of making a new " module " with a class that calls the event emitter and load that in the different files that need them as you suggested.

1 Like

Its the best way to have you own private event emitter across your custom nodes

  • Not abusing Node RED APIs (and putting your Nodes at risk of breakage)
  • No need to get hackery with adding stuff to the runtime
  • The events module is built into Node JS these days (I can't remember from when :sweat_smile: )
  • Easy to implement
  • No need to create a unique event name prefix (more so when piggy backing on RED.events)
  • Less risk of eavesdrop (for nodes that transport sensitive info)

Yup agree. And it worked like a charm. Now I need to revisit my classes /nodes and see how all things fit together like I need them to. :stuck_out_tongue:
So thanks again!

Heres a view of v0.1 of my lab from last year before we rebuilt it inside a warehouse.
Should you ever feel the need to test some stuf on an actual pilot I would be happy to help.

At the moment we only have 1 pilot pumping station built but the point is to make a chain of these to do AIRTC (Artificial Intelligent RealTime Control ). Basically meaning we act on measurements without human intervation.

Ive managed to shave of more than 30% on normal control operatios using a self made machine efficiency algorithm which decides what machine should run in combination with eachother. It took me aprox 2 years to build the php version of it and now recoding everything to something that makes more sense :stuck_out_tongue:

4 Likes

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