FreeOpcUa / opcua-asyncio

OPC UA library for python >= 3.7
GNU Lesser General Public License v3.0
1.12k stars 361 forks source link

Server does not support requests specifying ExpandedNodeId #1406

Open okapies opened 1 year ago

okapies commented 1 year ago

Describe the bug
Server accepts ExpandedNodeId as a parameter in a request, but it doesn't match with the node added to the server when its nodeid is NodeId (i.e. registered by using namespace index instead of namespace URI).

To Reproduce

import asyncio
from asyncua import ua, Server

async def run_server():
    server = Server()
    await server.init()

    server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
    server.set_server_name("FreeOpcUa Example Server")
    server.set_security_policy([ua.SecurityPolicyType.NoSecurity])

    idx = await server.register_namespace("http://examples.freeopcua.github.io")
    await server.nodes.objects.add_variable(
        ua.NodeId("MyVariable", idx), ua.QualifiedName("MyVariable", idx), 6.7
    )
    async with server:
        while True:
            await asyncio.sleep(0.1)

asyncio.run(run_server())
from asyncua import ua
from asyncua.sync import Client

>>> c = Client('opc.tcp://localhost:4840')
>>> c.connect()
>>> c.nodes.namespace_array.read_value()
['http://opcfoundation.org/UA/', 'urn:freeopcua:python:server', 'http://examples.freeopcua.github.io']
>>> c.get_node("ns=2;s=MyVariable").read_value()
6.7
>>> c.get_node("nsu=http://examples.freeopcua.github.io;s=MyVariable").read_value()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  ...
  File "/home/user/project/venv/lib/python3.10/site-packages/asyncua/ua/uatypes.py", line 372, in check
    raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadNodeIdUnknown: "The node id refers to a node that does not exist in the server address space."(BadNodeIdUnknown)

Expected behavior
Normalize the received requests by converting nsu and srv into ns in the Server's context.

Screenshots
n/a

Version

AndreasHeine commented 1 year ago

according to the opc ua reference: for the requests you need to use the NodeId-Format only!

the ExpandedNodeId-Format is for storing configurations/nodeid's! after client connecting you need to read the namespace array and resolve the nsu (ns-uri) to ns (ns-idx). the reason for that is that the ns index is not an constant and might change over time as models get added or changed... the only constant is the nsu (the actual unique name in the server) to identify the namespace and within the namespace the constant is the identifier (which is unique within the namespace)

AndreasHeine commented 1 year ago

e.g. for read: https://reference.opcfoundation.org/Core/Part4/v105/docs/5.10.2 https://reference.opcfoundation.org/Core/Part4/v104/docs/7.24

okapies commented 1 year ago

Yes, ns-idx is not realiable to access the nodes. Should we support ns-uri in the request like QuickOpc? It seems we need to normalize the given NodeId before reading/writing a node to the address space:

https://github.com/FreeOpcUa/opcua-asyncio/blob/fa73b116cd9ee2e9d00c9caf2e5f8784d36cdb82/asyncua/server/address_space.py#L79-L80

https://github.com/FreeOpcUa/opcua-asyncio/blob/fa73b116cd9ee2e9d00c9caf2e5f8784d36cdb82/asyncua/server/address_space.py#L107-L109

Otherwise, ExpandedNodeId should always be rejected as invalid in requests to the Server to avoid confusion.