FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.35k stars 658 forks source link

Example of opc-ua methods with access to nodes #1551

Open ninagestalt opened 2 weeks ago

ninagestalt commented 2 weeks ago

Goal

Create an opc-ua method that has access to the nodes. My main goal is to return a list of nodes (e..g all nodes in one folder) with one server call, instead of one for each node.

What I've tried

I found this example of opc-ua methods that does work for me: https://github.com/ifak-prototypes/opcua_examples/tree/main/src/MethodsServerSimple

But I want to access the nodes.

Minimal non-working example

I created a minimal non-working example of a server that exposes two nodes (Node1, Node2) and a method sum().

from opcua import ua, Server

def sum_method(parent):
    val1 = node1.get_value()
    val2 = node2.get_value()
    return [ua.Variant(val1 + val2, ua.VariantType.Double)]

if __name__ == "__main__":
    server = Server()
    server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
    uri = "http://example.opcua.server"
    idx = server.register_namespace(uri)

    objects = server.get_objects_node()

    # Add two nodes with initial values
    node1 = objects.add_variable(idx, "Node1", 5.5)
    node2 = objects.add_variable(idx, "Node2", 10.5)
    node1.set_writable()
    node2.set_writable()

    # Add a method to sum the two nodes
    objects.add_method(idx, "sum", sum_method, [], [ua.VariantType.Double])

    # Start the server
    server.start()
    try:
        input("Server is running. Press Enter to stop...")
    finally:
        server.stop()

and the client code to call the method:

from opcua import Client

if __name__ == "__main__":
    client = Client("opc.tcp://localhost:4840/freeopcua/server/")

    try:
        # Connect to the server
        client.connect()
        print("Client connected")

        # Access the root node and browse to the Objects node
        objects_node = client.get_objects_node()

        # Find the method node by browsing or by known ID/path
        sum_method = objects_node.get_child(["0:MyObject", "0:sum"])

        # Call the method, no input arguments
        result = sum_method.call()
        print(f"The sum of the two node values is: {result}")

    finally:
        # Close the connection
        client.disconnect()
        print("Client disconnected")

and the error:

opcua.ua.uaerrors._auto.BadNoMatch: "The requested operation has no match to return."(BadNoMatch)

My questions

AndreasHeine commented 2 weeks ago

guess your method should return a tuple instead of a list!?

see: https://github.com/FreeOpcUa/opcua-asyncio/blob/master/examples%2Fserver-extension-object-as-method-argument.py

ninagestalt commented 2 weeks ago

@AndreasHeine I tried writing a client for this example but still get similar errors as before..