phoddie / node-red-mcu

Node-RED for microcontrollers
124 stars 18 forks source link

node.status() throws in FunctionNode #29

Closed ralphwetzel closed 1 year ago

ralphwetzel commented 1 year ago

Calling node.status() in a FunctionNode throws Exception: Node.prototype.status: get #outputs: undefined private property!.

This is a test case (JSON at the bottom of this post):

image

The FunctionNode's code does little more than calling node.status():

let on_off = context.get("on") ?? false;

on_off = !on_off;
node.status(on_off ? { fill: "yellow", shape: "dot" } : {});

context.set("on", on_off);

The reason seems to be that status() is called on the node object created in line 542 - which is not fully initialized. https://github.com/phoddie/node-red-mcu/blob/e00a3d7185bc61bf34d0b6259da9a0fbc668fd4c/nodered.js#L542-L552

As a option to solve this issue, the node object should be empowered with additional properties that forward the job to the (fully initialized) this of the FunctionNode. status() is a first candidate, but most probably not the last.

Line 551:

    }},
    status: {value: status => {
        this.status(status);
    }}

Test flow JSON:

[
    {
        "id": "da4aa1c13327d032",
        "type": "tab",
        "label": "Flow 8",
        "disabled": false,
        "info": "",
        "env": [],
        "_mcu": {
            "mcu": true
        }
    },
    {
        "id": "65ca9481ed7af9a1",
        "type": "inject",
        "z": "da4aa1c13327d032",
        "name": "3s",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "3",
        "crontab": "",
        "once": true,
        "onceDelay": "1",
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "_mcu": {
            "mcu": true
        },
        "x": 370,
        "y": 400,
        "wires": [
            [
                "56ec831b4705772f"
            ]
        ]
    },
    {
        "id": "56ec831b4705772f",
        "type": "function",
        "z": "da4aa1c13327d032",
        "name": "LED",
        "func": "let on_off = context.get(\"on\") ?? false;\n\non_off = !on_off;\nnode.status(on_off ? { fill: \"yellow\", shape: \"dot\" } : {});\n\ncontext.set(\"on\", on_off);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "// Der Code hier wird ausgeführt,\n// wenn der Node gestartet wird\ncontext.set(\"on\", false)",
        "finalize": "",
        "libs": [],
        "_mcu": {
            "mcu": true
        },
        "x": 490,
        "y": 400,
        "wires": [
            []
        ]
    }
]
phoddie commented 1 year ago

Thank you for the report & flow. I probably broke status on Function node when optimizing status recently. It should be a straightforward fix. I'll take a look.

phoddie commented 1 year ago

I believe the object created on line 542 is properly initialized. However, the subtitles of private fields in JavaScript prevent them from being accessed when called through that instance. My mistake. It was broken by my status optimization because, prior to that, the code path didn't access a private field. Your proposed fix works because it bypasses that object and calls the true instance directly. Nice find.

phoddie commented 1 year ago

Fix committed.

ralphwetzel commented 1 year ago

Confirmed. Thank you!