mikakaraila / node-red-contrib-opcua

A Node-RED node to communicate OPC UA. Uses node-opcua library.
Other
214 stars 196 forks source link

Problems with dynamic endpoint with client node #532

Open wjomo opened 1 year ago

wjomo commented 1 year ago

Hi Mika, thanks for your great work with the opc ua nodes.

I have updated my nodes to v0.2.298 now I have problems with injecting the client endpoint by a message. Before I did this update the dynamic endpoint works fine.

With a function node I create the following message:

msg.topic="Reconnect";
msg.action="reconnect";
msg.OpcUaEndpoint = {
    credentials: {},
    endpoint: 'opc.tcp://192.168.178.47:4840',
    securityPolicy: 'None',
    securityMode: 'None',
    login: false,
    user: undefined,
    password: undefined 
}

return msg;

If I send that object to the client node the debug shows the following message and the client node says "no client":

debug

My test-flow for the client node looks like this:

[
    {
        "id": "7e4f9c47d4e1a753",
        "type": "tab",
        "label": "OPCUA Client",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "0e365bdec943e6bc",
        "type": "OpcUa-Client",
        "z": "7e4f9c47d4e1a753",
        "endpoint": "372390d2881694df",
        "action": "readmultiple",
        "deadbandtype": "a",
        "deadbandvalue": 1,
        "time": 10,
        "timeUnit": "s",
        "certificate": "n",
        "localfile": "",
        "localkeyfile": "",
        "securitymode": "None",
        "securitypolicy": "None",
        "folderName4PKI": "",
        "name": "",
        "x": 580,
        "y": 220,
        "wires": [
            [
                "732e86a774c0d77c"
            ]
        ]
    },
    {
        "id": "3f52d3f55916ba0f",
        "type": "OpcUa-Item",
        "z": "7e4f9c47d4e1a753",
        "item": "ns=4;s=Counter",
        "datatype": "Int32",
        "value": "",
        "name": "Counter",
        "x": 340,
        "y": 220,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "20029b75e3d401d8",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "Read",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payloadType": "date",
        "x": 110,
        "y": 220,
        "wires": [
            [
                "3f52d3f55916ba0f"
            ]
        ]
    },
    {
        "id": "c7a740fcf016b957",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "Disconnect",
        "props": [
            {
                "p": "action",
                "v": "disconnect",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 180,
        "y": 300,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "732e86a774c0d77c",
        "type": "debug",
        "z": "7e4f9c47d4e1a753",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 820,
        "y": 220,
        "wires": []
    },
    {
        "id": "5b8e67c5e10e31f4",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "Connect",
        "props": [
            {
                "p": "action",
                "v": "connect",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 220,
        "y": 360,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "8fbe8f2d7c8a2898",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "Reconnect",
        "props": [
            {
                "p": "action",
                "v": "reconnect",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 260,
        "y": 420,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "3871298c436152a3",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "start",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 150,
        "y": 80,
        "wires": [
            [
                "c48b3002adb8850f"
            ]
        ]
    },
    {
        "id": "04dc66ddfba759e5",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "Clear",
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "clearitems",
        "payload": "",
        "payloadType": "str",
        "x": 270,
        "y": 480,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "c48b3002adb8850f",
        "type": "trigger",
        "z": "7e4f9c47d4e1a753",
        "name": "",
        "op1": "1",
        "op2": "0",
        "op1type": "str",
        "op2type": "str",
        "duration": "-5",
        "extend": false,
        "overrideDelay": false,
        "units": "s",
        "reset": "",
        "bytopic": "all",
        "topic": "topic",
        "outputs": 1,
        "x": 400,
        "y": 80,
        "wires": [
            [
                "e4113f852933b126"
            ]
        ]
    },
    {
        "id": "e4113f852933b126",
        "type": "change",
        "z": "7e4f9c47d4e1a753",
        "name": "",
        "rules": [
            {
                "t": "delete",
                "p": "payload",
                "pt": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "readmultiple",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 650,
        "y": 80,
        "wires": [
            [
                "0e365bdec943e6bc"
            ]
        ]
    },
    {
        "id": "8387f80e2c58986a",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "stop",
        "props": [
            {
                "p": "reset",
                "v": "true",
                "vt": "bool"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "x": 150,
        "y": 140,
        "wires": [
            [
                "c48b3002adb8850f"
            ]
        ]
    },
    {
        "id": "381c575d88521fc2",
        "type": "function",
        "z": "7e4f9c47d4e1a753",
        "name": "LocalServer endpoint",
        "func": "msg.topic=\"Reconnect\";\nmsg.action=\"reconnect\";\nmsg.OpcUaEndpoint = {\n    credentials: {},\n    endpoint: 'opc.tcp://192.168.178.47:4840',\n    securityPolicy: 'None',\n    securityMode: 'None',\n    login: false,\n    user: undefined,\n    password: undefined \n}\n\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 340,
        "y": 560,
        "wires": [
            [
                "0e365bdec943e6bc",
                "3d3d7a3f998e908f"
            ]
        ]
    },
    {
        "id": "1c13b8f6745fc601",
        "type": "inject",
        "z": "7e4f9c47d4e1a753",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "str",
        "x": 90,
        "y": 560,
        "wires": [
            [
                "381c575d88521fc2"
            ]
        ]
    },
    {
        "id": "3d3d7a3f998e908f",
        "type": "debug",
        "z": "7e4f9c47d4e1a753",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 610,
        "y": 560,
        "wires": []
    },
    {
        "id": "372390d2881694df",
        "type": "OpcUa-Endpoint",
        "endpoint": "opc.tcp://0.0.0.0:4840/",
        "secpol": "None",
        "secmode": "None",
        "none": true,
        "login": false,
        "usercert": false,
        "usercertificate": "",
        "userprivatekey": ""
    }
]

I hope that you can help me with that problem.

Thanks for your help an have an nice day.

Best regards

mikakaraila commented 1 year ago

I expect it has something to do with the error message. I will take a look...

wjomo commented 1 year ago

Hi Mika, thanks for your answer.

I have tested a little bit and tried to send msg.action = connect and msg.topic = Connect in my function node for the dynamic endpoint. After that the client node was connected with the endpoint.

But I have a problem and I don t know what is the best way to solve that.

In my application I am using two client nodes witch are connected to external OPC-UA servers witch are running on two different PLCs. Now its the problem when an machine is switched off during active reading my opc client gets an error. If the machine is switch on the opc client should automatically reconnect to the OPC Server on the PLC.

Now I dont know what is the best way to close the session when the external OPC-UA Server is not reachable without error messages and build up an connection to the server, when the PLC is started?

I hope you can give me some advices how I could do that.

Thanks for your help an have an nice day

Best Regards

mikakaraila commented 1 year ago

There are two cases client will give warning: "No session to close!"

First one will come if client will start to reconnect and there is no active session (you case most probably). Client will loop reconnect.

Second one comes if you send action "disconnect" and there is no active session.

My proposal is to send action disconnect then client should not continue to reconnect server.

wjomo commented 1 year ago

Hi Mika, thanks for the advice.

I do some test with connect, disconnect the OPC UA client node.

My test procedure was as follows:

  1. Active reading with client node
  2. Connection to server interrupted (PLC switched off)
  3. msg.action = disconnect sent to client

After that I get following debug messages: debug_bei_disconnect

With the first error message from client node I detect the broken connection, then I sent the msg.action= disconnect.

The client node throws that error three times. After that many of errors will shown with the same payload: " Client node error on: read Client 2 error: ["read Client 2 error at active reading: Invalid Channel Bad ConnectionClosed"]"

After that errors, the client node changed the status to "disconnected"

When the client node gets the status of "disconnected" I switched on the PLC with the OPC Server and sent the msg.action=connect to the client node, then the connection is established again.

What does the high number of error messages mean? Did I controlled something wrong?

Do you prefer another workaround for detecting the lost connection, disconnect an connect the client node when a connection is possible?

The syslog entry from my node-red device shows the following messages:

log_eintrag_syslog_bei_disconnect

Thanks for your help an have an nice day.

Best regards

mikakaraila commented 1 year ago

Normally connection break will be handled by node-opcua. But in this case as server is most probably restarted it cannot re-connect as server forces it to re-create connection & session. Your workaround is perhaps the only way to get client connected again if normal node-opcua re-connect is not working.

kbucsit22 commented 10 months ago

I go also this problem, using credentials dynamically or by using program to change the opc endpoint,. I got always alarm. this is what I do: msg.topic="Reconnect"; msg.action="reconnect"; msg.OpcUaEndpoint = { credentials: {}, endpoint: 'opc.tcp://192.168.178.47:4840', securityPolicy: 'None', securityMode: 'None', login: true, userName: 'admin', password: 'admin' }

return msg;

mikakaraila commented 10 months ago

Try: credentials: { user: 'admin', password: 'admin'},

This seems to be coded a different way than it should be...

wjomo commented 10 months ago

Hi kbucsit22,

i pass the following Object to the client node and I am able to get a connection.

msg.topic="Reconnect";
msg.action="reconnect";
  opcUAEndpoint: {
    credentials: {},
    endpoint: "opc.tcp://xxx.xxx.xxx.xxx:4840",
    securityPolicy: "None",
    securityMode: "None",
    login: true,
    user: "admin",
    password: "password"
  }

I think Mika is correct in his assumption.

Best Regards

kbucsit commented 10 months ago

Thank you sir, now I got it connected,. What about if I needed to disconnect it, what command I need to use?

wjomo commented 10 months ago

I think you have to send:

msg.action = "disconnect";
msg.topic = "disconnect";

and to establish a connection:

msg.action = "connect";
msg.topic = "connect";

Best regards

kbucsit22 commented 10 months ago

I think you have to send:

msg.action = "disconnect";
msg.topic = "disconnect";

and to establish a connection:

msg.action = "connect";
msg.topic = "connect";

Best regards

@mikakaraila @wjomo Thank you for your big help, now I can place the IP to dashboard and user can connect or disconnect..

RudolfTheRedNode commented 7 months ago

opc.tcp://xxx.xxx.xxx.xxx:4840

Why this does not work in docker? is it because its a linux environment?

mikakaraila commented 7 months ago

I am not Docker expert but you should open port that it is visible on host and Docker application.