Persist variables - Flowforge

I'm in need of help with flow-forge.

When I get the variables like this.

flowforge gives an error.

Guy Ben said it's a flowforge bug that would be fixed later.

How can I get around this problem? in short time. I want to try using the system.

@Steve-Mcl Help me Please

Use the async versions of the functions.

https://nodered.org/docs/user-guide/writing-functions#asynchronous-context-access

E.g...

let myCounter = 0
global.get('count', function(err, count) {
    if (err) {
        node.error(err, msg);
        return null
    } else {
        myCounter = count || 0;
    }
})

There are 5 variables, it will be a huge code.

Do you have an elegant solution?

I did and it doesn't work

const ip = global.get("varG_IP_conf", "persistent", function (err, count) {if (err) {node.error(err, msg);return null} else {return 0;}})
const porta = global.get("varG_Porta_conf", "persistent", function (err, count) { if (err) { node.error(err, msg); return null } else { return 0; } })
const usuario = global.get("varG_Usuario_conf", "persistent", function (err, count) { if (err) { node.error(err, msg); return null } else { return 0; } })
const senha = global.get("varG_Senha_conf", "persistent", function (err, count) { if (err) { node.error(err, msg); return null } else { return 0; } })
const protocolo = global.get("varG_Protocolo_conf", "persistent", function (err, count) { if (err) { node.error(err, msg); return null } else { return 0; } })

//const IP = global.get("varG_IP_conf", "persistent")
//const porta = global.get("varG_Porta_conf", "persistent")
//const usuario = global.get("varG_Usuario_conf", "persistent")
//const senha = global.get("varG_Senha_conf", "persistent")
//const protocolo = global.get("varG_Protocolo_conf", "persistent")

msg.broker = {
    "broker": protocolo + "://" + ip + ":" + porta,
    "username": usuario,
    "password": senha,
    "port": porta,
    "force": true
}

//node.warn(msg.broker);

msg.action = "connect"
return msg;

You need to wait for all the variables...
@Steve-Mcl is there a promise-based interface? At least then he could use an async function and keep some sanity in coding this...

E.g.:

async function doit() {
  const ip = await global.get(...)
  const porta = await global.get(...)

  ... rest of the code using the vars

  send(....)
}

doit.then(()=>{})

No but promisify can be imported in the function setup.

That is not what I said.

Look again

Your way...

Here is how you can get the global values using async...

chrome_DDNyfeqtD7

Demo flow

[{"id":"cb6e0ab3e1d7dfac","type":"inject","z":"f888b812e6b0fc73","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1220,"y":160,"wires":[["8e3c82ae3f720444"]]},{"id":"8e3c82ae3f720444","type":"function","z":"f888b812e6b0fc73","name":"async get","func":"\n/**\n * Set a value in context\n * @param {'global'|'flow'|'context'} scope - The name of the scope\n * @param {string} key - The key name to store against\n * @param {any} val - The value to store in context\n * @param {string} [store] - The name of the context store\n */\nlet asyncSetContext = (scope, key, val, store) => {\n    const theScope = globalThis[scope]\n    return new Promise((resolve, reject) => {\n        theScope.set(key, val, store, (error) => {\n            if (error) {\n                reject(error)\n            } else {\n                resolve()\n            }\n        })\n    })\n}\n\n/**\n * Get a value from context\n * @param {'global'|'flow'|'context'} scope - The name of the scope\n * @param {string} key - The key name to get\n * @param {string} [store] - The name of the context store\n */\nlet asyncGetContext = (scope, key, store) => {\n    const theScope = globalThis[scope]\n    return new Promise((resolve, reject) => {\n        theScope.get(key, store, (error, value) => {\n            if (error) {\n                reject(error)\n            } else {\n                resolve(value)\n            }\n        })\n    })\n}\n\n\nconst myGlobalVar = await asyncGetContext('global', 'myGlobalVar', 'persistent')\nconst myFlowVar = await asyncGetContext('flow', 'myFlowVar', 'memory')\nmsg.payload = {\n    myGlobalVar,\n    myFlowVar\n}\nawait asyncSetContext('global', 'myNewGlobalVar', msg.payload, 'persistent')\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1370,"y":160,"wires":[["21709d0c4e5a472c"]]},{"id":"7a12c01d8aa694fc","type":"inject","z":"f888b812e6b0fc73","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1220,"y":100,"wires":[["bd71ffd46b07639f"]]},{"id":"bd71ffd46b07639f","type":"change","z":"f888b812e6b0fc73","name":"","rules":[{"t":"set","p":"myFlowVar","pt":"flow","to":"","tot":"date"},{"t":"set","p":"#:(persistent)::myGlobalVar","pt":"global","to":"(\t    $minimum := 1;\t    $maximum := 10;\t    $round(($random() * ($maximum-$minimum)) + $minimum, 0)\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1400,"y":100,"wires":[[]]},{"id":"21709d0c4e5a472c","type":"debug","z":"f888b812e6b0fc73","name":"debug 160","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1540,"y":160,"wires":[]}]

Just the function code

/**
 * Set a value in context
 * @param {'global'|'flow'|'context'} scope - The name of the scope
 * @param {string} key - The key name to store against
 * @param {any} val - The value to store in context
 * @param {string} [store] - The name of the context store
 */
let asyncSetContext = (scope, key, val, store) => {
    const theScope = globalThis[scope]
    return new Promise((resolve, reject) => {
        theScope.set(key, val, store, (error) => {
            if (error) {
                reject(error)
            } else {
                resolve()
            }
        })
    })
}

/**
 * Get a value from context
 * @param {'global'|'flow'|'context'} scope - The name of the scope
 * @param {string} key - The key name to get
 * @param {string} [store] - The name of the context store
 */
let asyncGetContext = (scope, key, store) => {
    const theScope = globalThis[scope]
    return new Promise((resolve, reject) => {
        theScope.get(key, store, (error, value) => {
            if (error) {
                reject(error)
            } else {
                resolve(value)
            }
        })
    })
}

// get values
const myGlobalVar = await asyncGetContext('global', 'myGlobalVar', 'persistent')
const myFlowVar = await asyncGetContext('flow', 'myFlowVar', 'memory')
// set a value
await asyncSetContext('global', 'myNewGlobalVar', msg.payload, 'persistent')

msg.payload = {
    myGlobalVar,
    myFlowVar
}

return msg;

The better no-code (flow) way...

  • copy global/flow variables to the msg using a change node
  • pass the msg into a function/change/other node
  • store the result in global once processed

chrome_DeiRlaZvpL

Demo flow

[{"id":"7a12c01d8aa694fc","type":"inject","z":"f888b812e6b0fc73","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1220,"y":100,"wires":[["bd71ffd46b07639f"]]},{"id":"bd71ffd46b07639f","type":"change","z":"f888b812e6b0fc73","name":"","rules":[{"t":"set","p":"myFlowVar","pt":"flow","to":"","tot":"date"},{"t":"set","p":"#:(persistent)::myGlobalVar","pt":"global","to":"(\t    $minimum := 1;\t    $maximum := 10;\t    $round(($random() * ($maximum-$minimum)) + $minimum, 0)\t)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":1400,"y":100,"wires":[[]]},{"id":"50d1b30a387ab7ee","type":"change","z":"f888b812e6b0fc73","name":"copy flow/global to msg","rules":[{"t":"set","p":"payload","pt":"msg","to":"{}","tot":"json"},{"t":"set","p":"payload.myFlowVar","pt":"msg","to":"myFlowVar","tot":"flow"},{"t":"set","p":"payload.myGlobalVar","pt":"msg","to":"#:(persistent)::myGlobalVar","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":1410,"y":160,"wires":[["d9828892956c5f52"]]},{"id":"0e7da876a2959e73","type":"inject","z":"f888b812e6b0fc73","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1220,"y":160,"wires":[["50d1b30a387ab7ee"]]},{"id":"d9828892956c5f52","type":"function","z":"f888b812e6b0fc73","name":"use flow/global vars","func":"\nconst myGlobalVar = msg.payload.myGlobalVar\nconst myFlowVar = msg.payload.myFlowVar\nmsg.payload = {\n    myGlobalVar,\n    myFlowVar\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1390,"y":220,"wires":[["8f9ccce1a3d4963d"]]},{"id":"8f9ccce1a3d4963d","type":"change","z":"f888b812e6b0fc73","name":"myNewGlobalVar","rules":[{"t":"set","p":"myNewGlobalVar","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1390,"y":280,"wires":[["aeab6aea324bd1b4"]]}]

Just the function (could just as easily have been a no-code node like a change node!)...

const myGlobalVar = msg.payload.myGlobalVar
const myFlowVar = msg.payload.myFlowVar
msg.payload = {
    myGlobalVar,
    myFlowVar
}
return msg;

The common pitfall

Most users new to node-red (myself included) throw everything into function nodes. This doesnt make great "flow" programming. I mean you might as well just use node js and skip node-red altogether.

Canned Text...

I recommend watching this playlist: Node-RED Essentials. The videos are done by the developers of node-red. They're nice & short and to the point. You will understand a whole lot more in about 1 hour. A small investment for a lot of gain.

I appreciate the help, everything now works fine here.

And we really have a tendency to put everything inside the function node and forget that it is a lowcode flow software.

I'm still getting my head around JavaScript objects and the constant use of JSON.

I've never used so much JSON in my life.

This code was working perfectly in nod-red, when I started using flowforge I started having this difficulty.

I've done several tests here and the persistent variables scenario is confusing.

I did some tests by pressing F5 in the browser. It always keeps the data of the variables, but when the server restarts, some come back, others don't.

And the behavior is quite confusing.

I refer to using flowforge.

Using node-red is perfect.

however taking a snapshot and distributing it on the device.

There everything works perfect.

I don't know... I'm confused...

I'm trying to use flowforge.

That is because context is stored in the server (not the browser)

If a context variable is stored in the "memory" context, it will be lost after reboot
If a context variable is stored in the "persistent" context, it will be survive a reboot

It is likely down to inexperience. I would suggest learning a bit about node-red and FlowForge to get the best out of them. I will post some info below.

  • FlowForge Concepts
  • Using FlowForge
  • I also recommend watching this playlist: Node-RED Essentials. The videos are done by the developers of node-red. They're nice & short and to the point. You will understand a whole lot more in about 1 hour. A small investment for a lot of gain.

I managed to solve the problem with the help of ROB (FlowForge).

And now everything is working as expected.

Thank you team

Nice one. Would you be kind enough to share what your issue was and how you solved it?

There is an unresolved bug in FLOW-FORGE.

So the command

global.set("var", data, target) doesn't work.

But I can use node-change.

This is a flow-forge issue on node-red that works perfect.

So just use node change. And avoid using global.set object

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