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:
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.