NodeId’s and Nodes#

The NodeId Class#

asyncua.ua.uatypes.NodeId objects are used as unique ids for Node’s and are therefore used on the server and client side to access specific nodes. The two classes NodeId and Node should not be confused: The NodeId is the unique identifier of an actual Node. While the NodeId is used to identify a specific node, the Node object can be used to access the underlying data. To learn more about the Node class, head over to The Node Class.

A NodeId contains two main pieces of information that allow a unique mapping in the opc-ua address space: The Identifier and the NamespaceIndex. In addition there is the NodeIdType attribute which is used to specify which opc-ua type is used for the Identifier. In addition to the NodeId class, there is also the a ExpandedNodeId which adds the NamespaceUri and ServerIndex attributes to make the ID unique across different servers and namespaces.

Creating NodeId’s#

As already mentioned above, the NodeId class supports different types for the Identifier. The type is handled automatically on class instantiation and there is usually no need to set the type manually. Creating new NodeIds usually looks like this:

>>> from asyncua import ua
>>> ua.NodeId(1, 2)  # Integer Identifier
NodeId(Identifier=1, NamespaceIndex=2, NodeIdType=<NodeIdType.FourByte: 1>)
>>> ua.NodeId('Test', 2)  # String Identifier
NodeId(Identifier='Test', NamespaceIndex=2, NodeIdType=<NodeIdType.String: 3>)
>>> ua.NodeId(b'Test', 2)  # Bytes Identifier
NodeId(Identifier=b'Test', NamespaceIndex=2, NodeIdType=<NodeIdType.ByteString: 5>)

A NodeId can also be built from a single string:

>>> ua.NodeId.from_string('ns=2;i=4')
NodeId(Identifier=4, NamespaceIndex=2, NodeIdType=<NodeIdType.Numeric: 2>)
>>> ua.NodeId.from_string('ns=2;s=Test')
NodeId(Identifier='Test', NamespaceIndex=2, NodeIdType=<NodeIdType.String: 3>)

The input string must be in the format <key>=<val>;[<key>=<val>], or in words: it must be a list of key-value pairs, separated by semicolons. The following keys are supported:

ns

The ns key will map to the Namespace of the NodeId

i, s, g, b

These keys will map to the Identifier of the NodeId. The character specifies the type: Numeric, String, Guid or Bytes.

srv, nsu:

If one of this keys is set, a ExpandedNodeId will be returned and the ServerIndex and NamespaceUri will be set.

What else?#

The NodeId class is actually just a normal UA data-type like other objects as QualifiedName or Variant are, with some additional logic to make it easier to work with.

The Node Class#

The Node class is a central part used on the server and client. On the server side nodes are created and configured as well as read and written. On the client side we can browse through the nodes, access and manipulate their values. Nodes should not be confused with NodeId: Each node has a NodeId an can be accessed through it. NodeId uniquely identifies the Node within the server.

The Node class exposes a wide range of the OPC-UA protocol for easy access, however, to fully optimize your code you will need to use lower level functions. Beside that, for many usecases the Node class might be the right thing to use for simpler usecases and makes it certainly easier to get started with OPC-UA.

Accessing Nodes#

As mentioned above, the Node class provides access to a lot of functionality, on the server and client side. Therefore, both, the server and client, provide a get_node method: asyncua.client.client.Client.get_node() & asyncua.server.server.Server.get_node(). These functions can be used to get a node by it’s NodeId, for example:

>>> client.get_node("ns=2;i=2")
Node(NodeId(Identifier=2, NamespaceIndex=2, NodeIdType=<NodeIdType.Numeric: 2>))

Note that using get_node does not check if the node actually exists! The method just creates a new node which later can be used to query data.

Note

As a rule of thumb: If the method is synchronous, there is no communication between server and client. In such cases only input validation is performed.

The node now can be used to read / write / … data from the server:

>>> node = client.get_node("ns=2;i=2")
>>> name = (await node.read_browse_name()).Name
>>> value = (await node.read_value())
>>> print(f"{name} = {value}")
MyVariable = 16.59999
>>> await node.write_value(5.0)  # Must use 5.0, see note below
>>> value = (await node.read_value())
>>> print(f"{name} = {value}")
MyVariable = 5.0

Writing values using write_value() can be tricky in some cases as the method converts the python type to a OPC-UA datatype. In the example above we explicitly need to pass in a float to enforce a conversion to a Double. If 5 is passed in, the value will be sent as a VariantType.Int64, which would result in a error as the sent datatype does not match the expected type on the server side.

Todo

If there is ever a section which goes into more detail, add a link!

The node object can also be used to browse to other nodes. There are several methods available as shown in the following short example:

>>> # Get the parent of a node
>>> parent = await node.get_parent()
>>> print(parent)
Node(NodeId(Identifier=1, NamespaceIndex=2, NodeIdType=<NodeIdType.FourByte: 1>))

>>> # Get all children of a node
>>> await parent.get_children()
[Node(NodeId(Identifier=2, NamespaceIndex=2, NodeIdType=<NodeIdType.FourByte: 1>))]

>>> # Get a specific child (by NodeId) of a node
>>> await parent.get_child("2:MyVariable")
Node(NodeId(Identifier=2, NamespaceIndex=2, NodeIdType=<NodeIdType.FourByte: 1>))

Note that in the last example we use the browse path of child as argument to get_child(). With the same method it’s also possible to access a child several levels deeper than the current node:

>>> await c.nodes.objects.get_child(['2:MyObject', '2:MyVariable'])
Node(NodeId(Identifier=2, NamespaceIndex=2, NodeIdType=<NodeIdType.FourByte: 1>))

Here we start at the objects node an traverse via MyObject to MyVariable. Always keep in mind that browsing through the nodes will create network traffic and server load. If you already know the NodeId using get_node() should be preferred. You might also consider caching NodeIds which you found through browsing to reduce the traffic.