A Minimal OPC-UA Client#

In this section we will build a client which reads / writes data from the server created in the last section and calls the method which the server provides. Running the client code requires a running server of course, so open a new terminal and run python server-minimal.py to start the server.

Like in the server section, we will first look at the complete code of the client before diving into the details:

client-minimal.py#
 1import asyncio
 2
 3from asyncua import Client
 4
 5url = "opc.tcp://localhost:4840/freeopcua/server/"
 6namespace = "http://examples.freeopcua.github.io"
 7
 8
 9async def main():
10
11    print(f"Connecting to {url} ...")
12    async with Client(url=url) as client:
13        # Find the namespace index
14        nsidx = await client.get_namespace_index(namespace)
15        print(f"Namespace Index for '{namespace}': {nsidx}")
16
17        # Get the variable node for read / write
18        var = await client.nodes.root.get_child(
19            f"0:Objects/{nsidx}:MyObject/{nsidx}:MyVariable"
20        )
21        value = await var.read_value()
22        print(f"Value of MyVariable ({var}): {value}")
23
24        new_value = value - 50
25        print(f"Setting value of MyVariable to {new_value} ...")
26        await var.write_value(new_value)
27
28        # Calling a method
29        res = await client.nodes.objects.call_method(f"{nsidx}:ServerMethod", 5)
30        print(f"Calling ServerMethod returned {res}")
31
32
33if __name__ == "__main__":
34    asyncio.run(main())

Connecting to the server#

To connect to the server a new Client instance is created. The client supports the same async context manager construct as we have already seen in the server, which can be used to handle the opening / closing of the connection for us.

Getting the namespace#

As all our custom objects live in a custom namespace, we need to get the namespace index to address our objects. This is done with the get_namespace_index() method. You can find out the list of all namespaces available on the server with get_namespace_array().

Read / Write Variables#

To read or write a variable of an object, we first need to get the Node of the variable. The get_child() method of the root node (which is just a regular node) is used to transform the known path to a Node.

Note

Using get_child() will perform a server request in the background to resolve the path to a node. Extensive usage of this method can create a lot of network traffic which is not strictly required if the node id is known. If you know the node id it’s better to use the get_node() method of the client. For example client.get_node("ns=2;i=2") or client.get_node(ua.NodeId(2, 2)) could be used in the example. Note that the latter call is not async!

Once we have our node object, the variable value can be read or written directly using the read_value() and write_value() methods. The read method automatically transforms the opc-ua type to a python type but the read_data_value() method can be used if the original type of the variable is of interest. The write interface is built flexible and a Variant is also accepted to specify the exact type to be used.

Calling Methods#

The method interface is similar to the interface of variables. In the example the special node client.nodes.objects, which is in fact just a shortcut to the 0:Objects node, is used to call the 2:ServerMethod on it. The call_method() must be called on the parent node of the actual method node.