FreeOpcUa / opcua-asyncio

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

OPC UA and PowerFactory interaction #1038

Closed SoerenLohr closed 1 year ago

SoerenLohr commented 2 years ago

Hello, We were trying to utilize the FreeOpcUa library to create a server that interfaces with a DIgSILENT PowerFactory ("PF") instance. Reading and writing values are functional, but reading has to be forced by the PF script. The standard reading mechanism in PF only returns values if it detects changes in the OPC server, which is a behavior that we want to keep. We also tried running the PF example with the ProSys OPC UA Simulation Server, where the desired behavior is functional. The fact that it works with the ProSys server leads us to believe that there are issues in the configuration of the FreeOpcUa server.

We were wondering if you have any idea on how to change the code in order to replicate the behavior of the ProSys server.

Below is the code that is used to create the server:

import os
from random import random
import asyncio
import pandas as pd
from asyncua import ua, Server

type_map = {
    2: ua.VariantType.Int32,
    4: ua.VariantType.Float
}   

class SubHandler(object):

    """
    Subscription Handler. To receive events from server for a subscription
    """

    def datachange_notification(self, node, val, data):
        print("Python: New data change event", node, val)

    def event_notification(self, event):
        print("Python: New event", event)

async def main():

    # now setup our server
    server = Server()
    await server.init()
    server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/")
    server.set_server_name("FreeOpcUa Example Server")
    # set all possible endpoint policies for clients to connect through
    server.set_security_policy([
                ua.SecurityPolicyType.NoSecurity,
                ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt,
                ua.SecurityPolicyType.Basic256Sha256_Sign])

    # setup our own namespace
    uri = "http://examples.freeopcua.github.io"
    idx = await server.register_namespace(uri)

    # load powerfactory configuration into OPC server
    with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'OPCServerCfg.csv'), 'r') as f:
        setup = pd.read_csv(f, skiprows=[0])
    objs = server.get_objects_node()
    fold = await objs.add_object(idx, 'PF')
    for index, row in setup.iterrows():
        var = await fold.add_variable(idx, row[1], row[4], type_map[row[3]])
        await var.set_writable()

    # starting!
    async with server:
        handler = SubHandler()
        sub = await server.create_subscription(500, handler)
        myvar = await fold.get_child(f"{idx}:BUS_4_LNE_230KV_LINE_1_P")
        handle = await sub.subscribe_data_change(myvar)
        while True:
            await asyncio.sleep(1)

if __name__ == "__main__":
    asyncio.run(main())
AndreasHeine commented 2 years ago

you never write the variables... no variable value change no notification in the subhandler!

SoerenLohr commented 2 years ago

Excuse me, I should have mentioned this. We usually set the variable values by an external browser like UaExpert. We also tried out a version where the server periodically modifies a value. The subhandler fires, but PF does not register anything (as opposed to when using the ProSys Server).

AndreasHeine commented 2 years ago

so PF connects to the OPC UA Server and makes a subscription but does not get notified?

SoerenLohr commented 2 years ago

Well, I am not exactly sure how PF handles this behind the scenes, but this is how the OPC works from a user perspective:

Now, if I start the server and execute the script in the PF example, the script sends data once. On subsequent runs (restarting the script), nothing gets sent, so I guess there is some kind of database in PF that is compared to, then only the deviating values are sent. Nothing gets read, however. there is an option to force the ("synchronous") reading of values in the read value method, but then it reads the values all the time. Again, when I connect to the ProSys server, the procedure described above is working as intended. Is it possible that I need specific settings for this to function? When I go to the "Address Space" of the ProSys server, I see Alias and Server objects. Does the server created by the code already contain these objects or do I need to specify them?

AndreasHeine commented 2 years ago

Don't know anything about PF!

When I go to the "Address Space" of the ProSys server, I see Alias and Server objects. Does the server created by the code already contain these objects or do I need to specify them?

the server has the UA Standard Nodes included!

Could you check if the Nodes on your Python OPC UA Server are accessible with "UAExpert"? (https://www.unified-automation.com/de/produkte/entwicklerwerkzeuge/uaexpert.html)
Just to check if there is everything running properly!

But the Server Supports Read/Write and the Subscription out of the box!

otherwise we would need a wireshark trace from both setups to figure out whats going on...

schroeder- commented 2 years ago

Maybe PF has some trace capabilities, which can give a glue what is not working.

oroulet commented 2 years ago

one thing is sure is that that PF stuff adds its own set of concepts and strange naming so nobody else can understand what they do ;-) Good luck

SoerenLohr commented 2 years ago

Thank you for the answers. I doubt that PF has any network tracing capabilities since it's a power engineering simulation software.

I have compared the elements in the FreeOpcUa Server and the ProSys Server explicitly and noticed some differences.

In the FreeOpcUa Server, the following folders are missing, which are included in the ProSys Server by default:

Additionally, each variable in the FreeOpcUa Server has the following properties that are deviating from the ProSys Server:

I was wondering if these deviances may be the reason, what do you think? If I look at the deviances, it seems to me like it has something to do with simulation capabilities. If you think this has nothing to do with it, I will create the wireshark traces.

AndreasHeine commented 2 years ago

i bet the missing stuff is part of NamespaceIndex 1 or higher -> so its Prosys specific which of course is not part of the FreeOpcUa Server or OPC UA Core Namespace with NamespaceIndex 0!

SoerenLohr commented 2 years ago

Yes, you are correct, those new objects are specified with ns > 0. Nevertheless, do you reckon this could be the reason? The example also has an OPC DA link and a guide to set up the example using the "MatrikonOPC Server for Simulation". The guide for the UA version does not specify a guide to set up an OPC server. They claim there are no free UA servers available that are easily configurable. Perhaps I need to do extra configuration to make it a "simulation" server?

AndreasHeine commented 2 years ago

Please no questions about "OPC DA" (OPC Classic - DCOM stuff - its a completely different technologie... and not part of this Library) we only can help with OPC UA -> Unified Architecture

Matrikon and some others mix these terms which confuses a lot...

If PF tries to access Data from a SimulationVariable which not exist in you FreeOpcUa Server for sure it will not work!