0

This is probably very obvious to more experienced node red developers but here I go.

I have a node red flow. In it I have a function node. Inside the function node I have a javascript class. My javascript class in only exposing static members, extract:

class MeasurementsList {
    static INTERNAL_LIST_CONTEXT_NAME = "INTERNAL_LIST_CONTEXT_NAME";

    static get availableMeasurements() { //this is an array
        const tempMeasurementsInternalList = context.get(INTERNAL_LIST_CONTEXT_NAME);
        const measurementsInternalList = tempMeasurementsInternalList?.length ? tempMeasurementsInternalList : new Array();
        return measurementsInternalList;
    }
    ...
}

To make it very short, I want to be able to operate on the array, example: MeasurementsList .availableMeasurements.push(newMeasurementObject);. My problem is that I do not know how to make the availableMeasurements array "remain"/persistent between different msg.payload's.

This is what I want to achieve:

MeasurementsList.availableMeasurements.push(msg.payload1.newMeasurementObject);
...
MeasurementsList.availableMeasurements.push(msg.payload999.newMeasurementObject);
MeasurementsList.availableMeasurements.push(msg.payload1000.newMeasurementObject);
...

Is this possible to do in node-red with javascript? How should I deal with the context variable?


Edit 1: I refactored my former static member class to an instantiable dito, residing on the [On Start]-tab: On Start

// Code added here will be run once
// whenever the node is started.
class Measurement {
    constructor(time, device, temperatureC, maxAgeMs) {
        this.time = time;
        this.device = device;
        this.temperatureC = temperatureC;
        this.maxAgeMs = maxAgeMs;
    }
}

class MeasurementsList {

    constructor() {
        this.#measurementsList = new Array();
    }

    /**
    * @param {Measurement} measurement
    */
    refreshAndAdd(measurement) {
        this.#removeOutdatedValuesAndSortByTemperaturesAscending();
        this.#measurementsList.push(measurement);

        return {
            "maxMeasurement": this.#maxMeasurement(),
            "meanTemperatureC": this.#meanTemperatureC(),
            "minMeasurement": this.#minMeasurement()
        };
    }

    get length() {
        return this.#measurementsList.length;
    }

    #maxMeasurement() {
        const maxIndex = this.#measurementsList.length - 1;

        return this.#measurementsList[maxIndex];
    }

    #meanTemperatureC() {
        let sum = 0;

        this.#measurementsList
            .forEach((m) => {
                sum += m.temperatureC;
            });

        const mean = sum / this.#measurementsList.length;

        return mean;
    }

    #minMeasurement() {
        const minIndex = 0;

        return this.#measurementsList[minIndex];
    }

    #removeOutdatedValuesAndSortByTemperaturesAscending() {
        const currentTime = Date.now();
        this.#measurementsList = this.#measurementsList
            .filter((m) => {
                return (currentTime - m.time) < m.maxAgeMs;
            })
            .sort((m1, m2) => {
                return m1.temperatureC - m2.temperatureC
            });
    }
}

let measurementsList = new MeasurementsList();

Unfortunately there are things that I don't understand about scope? I declared and instatiated my MeasurementsList class on the [On Start]-tab to measurementsList. But the measurementsList is not accessible on the [On Message]-tab, neither is the Message-class? Did I missunderstood your answer mr @hardillb? On Message-tab Question: How should I do to access my measurementsList on the [On Message]-tab?

Hauns TM
  • 1,909
  • 2
  • 22
  • 38
  • 1
    When you create the array, you need to add it to the context so it persists and will be returned the next time you call `context.get()`. Otherwise you'll just keep creating a new array every time. – Barmar Jul 26 '23 at 15:43
  • 2
    Where exactly are you defining the Class? Is it in the "On Message" or "Start Up" tab? – hardillb Jul 26 '23 at 19:06
  • Hmm... @hardillb, actually I was doing it on [`On Message`]-tab. Is it the wrong place? – Hauns TM Jul 26 '23 at 19:10
  • "*My javascript class in only exposing static members*" - then [don't use a `class`](https://stackoverflow.com/a/29895235/1048572), but a simple object. – Bergi Jul 26 '23 at 19:41

1 Answers1

1

If you want to declare long lived objects (or in this case classes) they should be defined in the "Start Up" tab of the function node.

This is because the context of "On Message" tab is cleared out as each message arrives and holds no state.

Things declared and initialised in the "Start Up" tab will be persisted and pushed in to the context of the "On Message" tab.

Edit:

Once initialised you should add anything you want to use in the "On Message" tab by adding them to the context.

e.g.

...

let measurementsList = new MeasurementsList();

context.set('ml', measurementsList)

and at the start of "On Message"

const ml = context.get('ml')

https://nodered.org/docs/user-guide/writing-functions#running-code-on-start

hardillb
  • 54,545
  • 11
  • 67
  • 105
  • Thanks @hardillb! I'll try it! – Hauns TM Jul 26 '23 at 19:29
  • You are most probably totally correct mr but I probably don't understand the variable scope between the tabs? I updated my question (`Edit 1`) – Hauns TM Jul 26 '23 at 20:23
  • Apologies, I miss remembered, you can define and initialise an instance in "Start Up" but you will then need to stash the initialised instance of the class in the context to access them in "On Message". I'll edit the answer. – hardillb Jul 26 '23 at 20:55