Ergebnis 1 bis 3 von 3

Thema: [Python] OPCUA Copy Server

  1. #1
    Freier Denker
    Registriert seit
    Jul 2013
    Beiträge
    547

    [Python] OPCUA Copy Server

    Hallo zusammen,

    ich habe einen OPCUA-Server (OrigServer) zu dem ich subscriben kann. Die Daten gehören rechtlich mir / meiner Firma und wir möchten diese weiteren Menschen zur Verfügung stellen, jedoch möchten wir den Zugriff auf den eigentlichen Server (OrigServer) beschränken.

    Unsere Idee: Wir haben einen Server (ServerExtern) für unsere externen Zugreifer, auf dem wir einen OPCUA Server betreiben. Dieser subscribed alle Nodes auf dem OrigServer und published sie NEU.

    Da es sich dabei um einige Tausend Nodes handelt, würden wir das gerne mit einem Skript automatisieren.
    Ich habe die Doku inzwischen fleißig studiert. Ich habe auf einen Code-Schnippsel gehofft, bei dem ich einmal eine Liste aller Nodes ausgeben lassen kann und diese auf dem neuen Server "einfach" importieren.
    Das scheint es nicht zu geben, oder habt ihr da etwa auf die Schnelle gefunden?

    Derzeit sehe ich nur die Möglichkeit durch jede Node zu Iterieren und jeden Datentyp, Namen, Identifier usw. zu extrahieren und NEU zu erstellen. Hierfür dann noch die zugehörigen Callbacks zu registieren, damit alle Änderungen übergeben werden scheint relativ umständlich zu sein.
    Geht das auch einfacher oder ist mein Ansatz hier generell umständlich und ich habe etwas übersehen?

    P.S.: Externe Zugreifer sollen zwar auf dem ExternServer schreiben können, jedoch sollen diese Änderungen NICHT an OrigServer übergeben werden. An dieser Stelle soll nur eine lesende Verbindung bestehen.

    Über jegliche Tipps und Hinweise bin ich glücklich!

  2. #2
    Freier Denker

    (Threadstarter)


    Registriert seit
    Jul 2013
    Beiträge
    547

    Re: [Python] OPCUA Copy Server

    Derzeit bin ich soweit einige Daten von einem beliebigen OPCUA-Server auszulesen. Dazu habe ich exemplarisch das folgende Skript gebaut. Ich bin mir aktuell noch nicht sicher, ob ich damit alle notwendigen Daten auslese um die Nodes in gleicher Art und Weise zu replizieren.
    Insbesondere die (numerische) NodeId könnte mir Probleme bereiten. Ich überlege aktuell, ob ich diese einfach ignorieren kann und nur die allgemeine Struktur erhalten möchte.

    Code (Python):
    1. import logging
    2. from opcua import Client
    3. from opcua import Server as opcuaServer
    4. from opcua import ua
    5. import socket
    6. import threading
    7. import time
    8.  
    9. if __name__ == "__main__":
    10.     logging.basicConfig(level=logging.WARN)
    11.     address = "opc.tcp://127.0.0.1:4840"
    12.     client = Client(address)
    13.     try:
    14.         client.connect()
    15.         # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects
    16.         root = client.get_root_node()
    17.         print("Root node is: ", root)
    18.         objects = client.get_objects_node()
    19.         print("Objects node is: ", objects)
    20.  
    21.         # Node objects have methods to read and write node attributes as well as browse or populate address space
    22.         children = root.get_children()
    23.         print("Children of root are: ", children)
    24.        
    25.         def recursive_print_children_browse_names(c):
    26.        
    27.             def recursive_var_check(variables):
    28.                 for v in variables:
    29.                     print("Variables:", v, "\tName:", v.get_browse_name(), "\tPath:", v.get_path(), "\tValue:", v.get_value())
    30.                     new_vars = v.get_variables()
    31.                     if len(new_vars) != 0:
    32.                         recursive_var_check(new_vars)
    33.                        
    34.             for child in c:
    35.                 try:
    36.                     print("Childname:", child.get_browse_name())
    37.                     print(child.get_description()) # LocalizedText(Encoding:2, Locale:None, Text:The browse entry point when looking for objects in the server address space.)
    38.                     print(child.get_description_refs()) # []
    39.                     print(child.get_node_class()) # NodeClass.Object
    40.                     print(child.get_parent()) # i=84
    41.                     print(child.get_path()) # [Node(TwoByteNodeId(i=84)), Node(TwoByteNodeId(i=85))]
    42.                     print(child.get_properties()) # []
    43.                     print(child.get_type_definition()) # TwoByteNodeId(i=61)
    44.                     variables = child.get_variables()
    45.                    
    46.                     recursive_var_check(variables)
    47.                 except ua.UaStatusCodeError as e:
    48.                     print("Error catched:", e)
    49.                 new_childs = child.get_children()
    50.                 if len(new_childs) != 0:
    51.                     recursive_print_children_browse_names(new_childs)
    52.                     #if len(new_childs) >=6:
    53.                     #    recursive_print_children_browse_names(new_childs[:6])
    54.                     #else:
    55.                     #    recursive_print_children_browse_names(new_childs)
    56.  
    57.         for child in children:
    58.             print("##")
    59.             print(dir(child))
    60.             # 'get_access_level', 'get_array_dimensions', 'get_attribute', 'get_attributes', 'get_browse_name', 'get_child', 'get_children', 'get_children_descriptions', 'get_data_type', 'get_data_type_as_variant_type', 'get_data_value', 'get_description', 'get_description_refs', 'get_display_name', 'get_encoding_refs', 'get_event_notifier', 'get_methods', 'get_node_class', 'get_parent', 'get_path', 'get_properties', 'get_referenced_nodes', 'get_references', 'get_type_definition', 'get_user_access_level', 'get_value', 'get_value_rank', 'get_variables'
    61.             # print(child.get_access_level())
    62.             # print(child.get_array_dimensions())
    63.             # print(child.get_attributes())
    64.             # print(child.get_data_type())
    65.             # print(child.get_data_type_as_variant_type())
    66.             # print(child.get_data_value())
    67.             print(child.get_description()) # LocalizedText(Encoding:2, Locale:None, Text:The browse entry point when looking for objects in the server address space.)
    68.             print(child.get_description_refs()) # []
    69.             print(child.get_node_class()) # NodeClass.Object
    70.             print(child.get_parent()) # i=84
    71.             print(child.get_path()) # [Node(TwoByteNodeId(i=84)), Node(TwoByteNodeId(i=85))]
    72.             print(child.get_properties()) # []
    73.             print(child.get_type_definition()) # TwoByteNodeId(i=61)
    74.             # print(child.get_value())
    75.            
    76.         print("### Starting the recursive loop")
    77.         recursive_print_children_browse_names(children)
    78.         #recursive_print_children_browse_names([objects])
    79.        
    80.     finally:
    81.         client.disconnect()
    82.  
    Dieses Skript baucht ungefähr ein ganzes Jahrhundert um alle Nodes abzugreifen. Ich bin mir nicht sicher, ob das wirklich der eleganteste Weg ist.
    Allerdings ist meine derzeitige Idee während ich durch die Client-Objekte iteriere, parallel neue Objekte für den Server zu erstellen, die den gleichen Namen und die gleichen Kinder haben. Zudem müsste ich auch entsprechende Callbacks definieren, sobald sich einer der Werte bei OrigServer (hier client VerbindunG) ändern, diese auch auf den neu erstellen Server zu übertragen.

    Alternativ habe ich auch versucht mit export_xml und import_xml zu experimentieren. Abgesehen von langer Wartezeit und anschließenden Fehlermeldungen habe ich da bisher wenig Erfolg gehabt.

  3. #3
    Freier Denker

    (Threadstarter)


    Registriert seit
    Jul 2013
    Beiträge
    547

    Re: [Python] OPCUA Copy Server

    Ich habe ein paar Fortschritte in der Zwischenzeit gemacht.
    Ich habe mir aktuell manuell die fehlenden Datentypen rausgesucht und erstelle diese nachträglich.
    Leider scheint dabei irgendwas von mir noch zu fehlen.

    Der Baum sieht anders aus, obwohl die eigentlichen Elemente anscheinend gleich sind.
    Ich kopiere die Datentypen von der Simatic (Siemens) Steuerung und erstelle damit meinen OwnServer.
    Klicke auf die Grafik für eine größere Ansicht 

Name:	OwnServer.jpg 
Hits:	23 
Größe:	283,5 KB 
ID:	58649Klicke auf die Grafik für eine größere Ansicht 

Name:	Siemens_Server.jpg 
Hits:	19 
Größe:	304,3 KB 
ID:	58650

    Hat da jemand eine Ahnung, was ich anscheinend anders mache? Ich dachte es liegt vielleicht am Display_Name aber der scheint ja ebenfalls gleich zu sein...

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •