From b7ac9263e11025af92d94d03e3009c87afc92841 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Mon, 28 Dec 2020 01:10:13 +0100 Subject: [PATCH 01/77] conversion from OPCUA <-> OPC DA server Primarily for testing purposes, the conversion is done for the RobotStudio application using aynschronous methods. --- .vscode/settings.json | 4 ++ server/server.py | 110 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 server/server.py diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5993f5e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.enabled": true, + "python.pythonPath": "C:\\Users\\FKV\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe" +} \ No newline at end of file diff --git a/server/server.py b/server/server.py new file mode 100644 index 0000000..79c7355 --- /dev/null +++ b/server/server.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +import logging, sys, asyncio, OpenOPC, decimal + +from asyncua import ua, Server, uamethod + +UA_URI = 'https://hv.se' +OPCDA_SERVER_STRING = "" +readable_variables = {} +writeable_variables = {} +tree = {} +obj_in_node = {} + + +#Constants +ITEM_ACCESS_RIGHTS = 5 +ACCESS_READ = 0 +ACCESS_WRITE = 1 +ACCESS_READ_WRITE = 2 +ITEM_VALUE = 2 + +class SubHandler(object): + """ + Subscription handler to receive events from the server. + """ + + + def datachange_notification(self, node, val, data): + p_a_string = node.get_path_as_string() #Get a list containing root, objects, opc da server + da_address = '.'.join([a.split(':')[1] for a in p_a_string[3:]]) + da = OpenOPC.client() + da.connect(OPCDA_SERVER_STRING) + print('Datachanged ', da_address, val) + da.write((da_address, val,)) + da.close() + +def read_value(value): + value = value[0] + if isinstance(value,decimal.Decimal): + value = float(value) + elif isinstance(value,list): + if len(value) == 0: + value = None + elif isinstance(value,tuple): + if len(value) == 0: + value = None + + return value + +async def sort_nodes_list(list, idx, root, da): + + for node in list: + parts = node.split('.') #We split it into parts to separate each "part" + folders = parts[:-1] # The first part is the folder, typically multiple ones + file = parts[-1] #Then we have the file that is in the folder + for i, folder in enumerate(folders,1): + if i == 1: + parent = root + + else: + parent = tree[path] + path = '.'.join(folders[0:i]) + if path not in tree.keys(): + tree[path] = await parent.add_folder(idx, folder) + + for id, description_of_id, value in da.properties(node): + if id is ITEM_ACCESS_RIGHTS: + if value == 'Read': + value = ACCESS_READ + elif value == 'Write': + value = ACCESS_WRITE + elif value == 'Read/Write': + value = ACCESS_READ_WRITE + obj_in_node[id] = value + curr_value = read_value((obj_in_node[ITEM_VALUE],)) + if type(curr_value) != int: + curr_value = 0 + + opcua_node = await tree[path].add_variable(idx, file, ua.Variant(curr_value, ua.VariantType.UInt16)) + + if obj_in_node[ITEM_ACCESS_RIGHTS] in [ACCESS_READ]: + readable_variables[node] = opcua_node + #print(opcua_node) + if obj_in_node[ITEM_ACCESS_RIGHTS] in [ACCESS_WRITE, ACCESS_READ_WRITE]: + await opcua_node.set_writable() + writeable_variables[node] = opcua_node + #print(opcua_node) + + +async def main(): + + #We connect to the OPC-DA server (I assume there will only be one, else this will have to be changed) + #We can also check if there is a server and if there isn't one we'll run only an OPC UA server without conversion + da = OpenOPC.client() + OPCDA_SERVER_STRING = da.servers()[0] + da.connect(OPCDA_SERVER_STRING) #Connect to the first server in the array + print(OPCDA_SERVER_STRING) + + #Setup the server for UA + server = Server() + await server.init() + server.set_endpoint('opc.tcp://0.0.0.0:4840/freeopcua/server/') + idx = await server.register_namespace(UA_URI) + root = await server.nodes.objects.add_object(idx,OPCDA_SERVER_STRING) + + #We want to find the OPC-DA server nodes in aliases + nodes_list = da.list('*', recursive=True) #A list of dot-delimited strings + await sort_nodes_list(nodes_list, idx, root, da) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file From f619ffc8a7df191c260a0a63a8b4443f6953aaf3 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Tue, 29 Dec 2020 12:04:28 +0100 Subject: [PATCH 02/77] Added the UA server --- server/server.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/server/server.py b/server/server.py index 79c7355..b1dd0ce 100644 --- a/server/server.py +++ b/server/server.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -import logging, sys, asyncio, OpenOPC, decimal +import sys, asyncio, OpenOPC, decimal from asyncua import ua, Server, uamethod @@ -22,7 +22,6 @@ class SubHandler(object): """ Subscription handler to receive events from the server. """ - def datachange_notification(self, node, val, data): p_a_string = node.get_path_as_string() #Get a list containing root, objects, opc da server @@ -106,5 +105,25 @@ async def main(): nodes_list = da.list('*', recursive=True) #A list of dot-delimited strings await sort_nodes_list(nodes_list, idx, root, da) + try: + async with server: #Starting the server + handler = SubHandler() #Subscribing to datachanges coming from the UA clients + sub = await server.create_subscription(500, handler) + handle = await sub.subscribe_data_change(writeable_variables.values()) + readable_vars = list(writeable_variables) #readable_variables + #print(writeable_variables) + while True: + await asyncio.sleep(1) + for i in da.list(readable_vars): + da_id = i[0] + var_handler = readable_variables[da_id] + var_handler.set_value(read_value(i[1:])) + + + finally: + await server.stop() + da.close() + + if __name__ == "__main__": asyncio.run(main()) \ No newline at end of file From 662c74b13c0cd34041bfec2259e252392214c54a Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sat, 2 Jan 2021 19:53:46 +0100 Subject: [PATCH 03/77] Changes to readability of the OPCDA to OPCUA conversion, it is now working! --- server/server.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/server/server.py b/server/server.py index b1dd0ce..678c283 100644 --- a/server/server.py +++ b/server/server.py @@ -1,8 +1,13 @@ #!/usr/bin/env python -import sys, asyncio, OpenOPC, decimal +#Credits to http://courses.compute.dtu.dk/02619/software/opcda_to_opcua.py +#Helped with the conversion of OPCDA to OPCUA +import sys, asyncio, OpenOPC, decimal, time, pywintypes +from datetime import datetime from asyncua import ua, Server, uamethod +pywintypes.datetime = pywintypes.TimeType + UA_URI = 'https://hv.se' OPCDA_SERVER_STRING = "" readable_variables = {} @@ -32,6 +37,7 @@ def datachange_notification(self, node, val, data): da.write((da_address, val,)) da.close() + def read_value(value): value = value[0] if isinstance(value,decimal.Decimal): @@ -110,13 +116,16 @@ async def main(): handler = SubHandler() #Subscribing to datachanges coming from the UA clients sub = await server.create_subscription(500, handler) handle = await sub.subscribe_data_change(writeable_variables.values()) - readable_vars = list(writeable_variables) #readable_variables - #print(writeable_variables) + #In Robotstudio all variables are writeable, so the readable variables are empty + #This should be changed when tried in a real environment, so temporary for now + readable_vars = list(writeable_variables.keys()) #readable_variables + #print(readable_vars) while True: await asyncio.sleep(1) - for i in da.list(readable_vars): + for i in da.read(readable_vars): + print(i) da_id = i[0] - var_handler = readable_variables[da_id] + var_handler = writeable_variables[da_id] # Due to change var_handler.set_value(read_value(i[1:])) From 76f8d92d77aee2456468b08b725555e4407cc097 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 10 Jan 2021 15:57:21 +0100 Subject: [PATCH 04/77] Initial code for server discovery. --- remotecontrol/discovery.py | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 remotecontrol/discovery.py diff --git a/remotecontrol/discovery.py b/remotecontrol/discovery.py new file mode 100644 index 0000000..446ca34 --- /dev/null +++ b/remotecontrol/discovery.py @@ -0,0 +1,73 @@ +from zeroconf import ServiceBrowser, Zeroconf + +INAME = 0 +IADDR = 1 +IPORT = 2 +DADDR = 0 +DPORT = 1 + +def get_info(zeroconf, type, name): + info = zeroconf.get_service_info(type, name) + if info is not None: + name = info.get_name() + addresses = info.parsed_addresses() + port = info.port + return (name, addresses, port) + return None + +class DiscoveryListener: + def __init__(self, servicedict): + self.servicedict = servicedict; + + def remove_service(self, zeroconf, type, name): + info = get_info(zeroconf, type, name) + if info: + print("Service %s removed" % (name,)) + del self.servicedict[info[INAME]] + + def add_service(self, zeroconf, type, name): + info = get_info(zeroconf, type, name) + if info: + print("Service %s added, addresses: %s" % (info[INAME], info[IADDR])) + self.servicedict[info[INAME]] = (info[IADDR], info[IPORT]) + + def update_service(self, zeroconf, type, name): + info = get_info(zeroconf, type, name) + if info: + print("Service %s updated, addresses: %s" % (info[INAME], info[IADDR])) + self.servicedict[info[INAME]] = (info[IADDR], info[IPORT]) + +""" Vi ska kolla efter dessa tjänster: + opc.tcp" = "_opcua-tcp._tcp" + "opc.https" eller "https" = "_opcua-https._tcp" + "opc.wss" = "_opcua-wss._tcp" + enligt https://github.com/OPCFoundation/UA-LDS/blob/master/zeroconf.c + rad 222-238. """ + +types = ["_http._tcp.local.", "_googlecast._tcp.local."] +class Discovery: + def __init__(self, types): + self.sdict = {} + self.zeroconf = Zeroconf() + self.types = types + self.listener = DiscoveryListener(self.sdict) + browser = ServiceBrowser(self.zeroconf, self.types, self.listener) + + def get_services(self): + l = [] + if not self.sdict: + return l + for k in self.sdict.keys(): + name = k + port = self.sdict[k][DPORT] + for addr in self.sdict[k][DADDR]: + l.append((name, addr, port)) + return l + + +d = Discovery(types) + +while True: + input("Press enter to get new printout....\n\n") + for item in d.get_services(): + print(item) From e0865135644c7869179956c85bc9193749a089af Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Sun, 10 Jan 2021 19:09:39 +0100 Subject: [PATCH 05/77] Add files via upload Test OPCUA client/server communication with a client Gui --- opcua projekt/opcclient.py | 92 ++++++++++++++++++++++++++++++++++++++ opcua projekt/py.server.py | 41 +++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 opcua projekt/opcclient.py create mode 100644 opcua projekt/py.server.py diff --git a/opcua projekt/opcclient.py b/opcua projekt/opcclient.py new file mode 100644 index 0000000..fdb73af --- /dev/null +++ b/opcua projekt/opcclient.py @@ -0,0 +1,92 @@ +from opcua import Client +from tkinter import * +from tkinter import ttk +import time + +url = "opc.tcp://127.0.0.1:4840" + +client = Client(url) + +#connecting client to server +client.connect() + + +class Root(Tk): + def __init__(self): + super(Root, self).__init__() + self.title("GUI for client") + self.minsize(400,200) + + self.button = ttk.Button(self, text="Press4Temperature",command = self.clickbutton) + self.button.grid(column=0, row=0) + + self.pressurebutton = ttk.Button(self,text="Press4Pressure", command = self.clickbutton2) + self.pressurebutton.grid(column=0, row = 1) + + self.datetimebutton = ttk.Button(self,text="Press4Datetime", command = self.clickbutton3) + self.datetimebutton.grid(column=0, row = 2) + + + + + self.label = ttk.Label(self, text="The temperature is:") + self.label.grid(column=1, row=0) + + self.label = ttk.Label(self, text="The Pressure is:") + self.label.grid(column=1, row=1) + + self.label = ttk.Label(self, text="The Datetime is:") + self.label.grid(column=1, row=2) + + + + def clickbutton(self): + self.Temp = client.get_node("ns=2;i=2") + Temperature = self.Temp.get_value() + self.label = ttk.Label(self, text=Temperature) + self.label.grid(column=3, row=0) + + + + def clickbutton2(self): + self.Press = client.get_node("ns=2;i=3") + Pressure = self.Press.get_value() + self.label = ttk.Label(self, text=Pressure) + self.label.grid(column=3, row=1) + + + + def clickbutton3(self): + self.Time = client.get_node("ns=2;i=4") + TIME = self.Time.get_value() + self.label = ttk.Label(self, text=TIME) + self.label.grid(column=3, row=2) + + + +""" + + + +#getting the variables from the nodes from the server + + + while True: + Temp = client.get_node("ns=2;i=2") + Temperature = Temp.get_value() + print(Temperature) + + Press = client.get_node("ns=2;i=3") + Pressure = Press.get_value() + print(Pressure) + + TIME = client.get_node("ns=2;i=4") + TIME_value = TIME.get_value() + print(TIME_value) + + time.sleep(5) +""" + + +root = Root() +root.mainloop() \ No newline at end of file diff --git a/opcua projekt/py.server.py b/opcua projekt/py.server.py new file mode 100644 index 0000000..e65a6a5 --- /dev/null +++ b/opcua projekt/py.server.py @@ -0,0 +1,41 @@ +from opcua import Server +from random import randint +import datetime +import time + +server = Server() + +url = "opc.tcp://127.0.0.1:4840" +server.set_endpoint(url) + +name = "Test for GUI" + +addspace = server.register_namespace(name) + +node = server.get_objects_node() + +Params = node.add_object(addspace, "Parameters") + +Temp = Params.add_variable(addspace, "Temperature",0) +Press = Params.add_variable(addspace, "Pressure",0) +Time = Params.add_variable(addspace, "Time",0) + +Temp.set_writable() +Press.set_writable() +Time.set_writable() + +#starting server +server.start() + +while True: + Temperature = randint(0,35) + Pressure = randint(100,250) + TIME = datetime.datetime.now() + + print(Temperature,Pressure,TIME) + + Temp.set_value(Temperature) + Press.set_value(Pressure) + Time.set_value(TIME) + + time.sleep(5) From f4eb9f87e9ac3f60683fecac4f7cce17d254004f Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Sun, 10 Jan 2021 19:10:32 +0100 Subject: [PATCH 06/77] Update opcclient.py --- opcua projekt/opcclient.py | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/opcua projekt/opcclient.py b/opcua projekt/opcclient.py index fdb73af..9be9edb 100644 --- a/opcua projekt/opcclient.py +++ b/opcua projekt/opcclient.py @@ -64,29 +64,7 @@ def clickbutton3(self): -""" - - - -#getting the variables from the nodes from the server - - - while True: - Temp = client.get_node("ns=2;i=2") - Temperature = Temp.get_value() - print(Temperature) - - Press = client.get_node("ns=2;i=3") - Pressure = Press.get_value() - print(Pressure) - - TIME = client.get_node("ns=2;i=4") - TIME_value = TIME.get_value() - print(TIME_value) - - time.sleep(5) -""" root = Root() -root.mainloop() \ No newline at end of file +root.mainloop() From e1db9b2c2e6707bc98b414b7fbd69c5d0328df8c Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 10 Jan 2021 19:26:37 +0100 Subject: [PATCH 07/77] Changed GUI file structure. --- {remotecontrol => uaxplorer}/discovery.py | 0 {opcua projekt => uaxplorer}/py.server.py | 0 opcua projekt/opcclient.py => uaxplorer/uaxplorer.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {remotecontrol => uaxplorer}/discovery.py (100%) rename {opcua projekt => uaxplorer}/py.server.py (100%) rename opcua projekt/opcclient.py => uaxplorer/uaxplorer.py (100%) diff --git a/remotecontrol/discovery.py b/uaxplorer/discovery.py similarity index 100% rename from remotecontrol/discovery.py rename to uaxplorer/discovery.py diff --git a/opcua projekt/py.server.py b/uaxplorer/py.server.py similarity index 100% rename from opcua projekt/py.server.py rename to uaxplorer/py.server.py diff --git a/opcua projekt/opcclient.py b/uaxplorer/uaxplorer.py similarity index 100% rename from opcua projekt/opcclient.py rename to uaxplorer/uaxplorer.py From 2e9ff9ffbad5f723de1a9efbb37c670d8f4ed21d Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 10 Jan 2021 19:33:31 +0100 Subject: [PATCH 08/77] Made discovery as class only file, imported into uaxplorer. --- uaxplorer/discovery.py | 12 ++---------- uaxplorer/uaxplorer.py | 1 + 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/uaxplorer/discovery.py b/uaxplorer/discovery.py index 446ca34..ae20f78 100644 --- a/uaxplorer/discovery.py +++ b/uaxplorer/discovery.py @@ -44,7 +44,7 @@ def update_service(self, zeroconf, type, name): enligt https://github.com/OPCFoundation/UA-LDS/blob/master/zeroconf.c rad 222-238. """ -types = ["_http._tcp.local.", "_googlecast._tcp.local."] +types = ["_opcua-https._tcp.local.", "_opcua-tcp._tcp.local.", "_opcua-wss._tcp.local."] class Discovery: def __init__(self, types): self.sdict = {} @@ -62,12 +62,4 @@ def get_services(self): port = self.sdict[k][DPORT] for addr in self.sdict[k][DADDR]: l.append((name, addr, port)) - return l - - -d = Discovery(types) - -while True: - input("Press enter to get new printout....\n\n") - for item in d.get_services(): - print(item) + return l \ No newline at end of file diff --git a/uaxplorer/uaxplorer.py b/uaxplorer/uaxplorer.py index 9be9edb..1a8a7d4 100644 --- a/uaxplorer/uaxplorer.py +++ b/uaxplorer/uaxplorer.py @@ -1,6 +1,7 @@ from opcua import Client from tkinter import * from tkinter import ttk +from discovery import Discovery import time url = "opc.tcp://127.0.0.1:4840" From ce1f5e41a843a2abf82ca43cab597772d6705311 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 10 Jan 2021 19:34:54 +0100 Subject: [PATCH 09/77] Fixed typo. --- uaxplorer/discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uaxplorer/discovery.py b/uaxplorer/discovery.py index ae20f78..160acb2 100644 --- a/uaxplorer/discovery.py +++ b/uaxplorer/discovery.py @@ -17,7 +17,7 @@ def get_info(zeroconf, type, name): class DiscoveryListener: def __init__(self, servicedict): - self.servicedict = servicedict; + self.servicedict = servicedict def remove_service(self, zeroconf, type, name): info = get_info(zeroconf, type, name) From 564e3d3aabe867dcc7ea05e36569d401e9b510d5 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Tue, 19 Jan 2021 19:28:38 +0100 Subject: [PATCH 10/77] To grab the discovery server and methods for grabbing all the details in the array --- uaxplorer/ui_methods/server_discovery.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 uaxplorer/ui_methods/server_discovery.py diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py new file mode 100644 index 0000000..446fe7b --- /dev/null +++ b/uaxplorer/ui_methods/server_discovery.py @@ -0,0 +1,21 @@ +#DISCOVER ALL SERVERS WITH THIS METHOD. + +TUPLE_ARRAY = [] +DISCOVERY_OUTPUT = [('test_server', 'IP_ADDRESS', 'PORT_NUMBER'), ('test_server2', 'IP_ADDRESS2', 'PORT_NUMBER2')] #Change this to the discovery + #output we get later on + +def insert_array(servers): + for i in servers: + TUPLE_ARRAY.append(i) + +def get_all(array, type): #0 for server name, 1 for ip address, 2 for port number + temp_array = [] + for i in TUPLE_ARRAY: + temp_array.append(i[type]) + return temp_array + + + + +insert_array(DISCOVERY_OUTPUT) +print(get_all(TUPLE_ARRAY, 1)) \ No newline at end of file From 34f2a8dc6717cff18a0df79cb37818cc03d10029 Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Wed, 20 Jan 2021 15:47:34 +0100 Subject: [PATCH 11/77] Add files via upload Hardcode for Mapping --- uaxplorer/window.py | 60 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 uaxplorer/window.py diff --git a/uaxplorer/window.py b/uaxplorer/window.py new file mode 100644 index 0000000..62d401a --- /dev/null +++ b/uaxplorer/window.py @@ -0,0 +1,60 @@ +from tkinter import * +from tkinter import ttk +from opcua import Client, ua + +class window(Tk): + def __init__(self): + super(window,self).__init__() + self.title("GUI") + self.geometry("600x600") + self.treetime = ttk.Treeview(self) + self.treetime.pack() + self.treetime.insert('', 0 , 'item1', text=ram) + self.treetime.insert('', 0, 'item2', text=vam) + self.treetime.insert('', 0 , 'item3', text=par) + self.treetime.insert('', 0 , 'item4', text=bam) + + self.treetime.move('item2', 'item1', 'end') + self.treetime.move('item3', 'item1', 'end') + self.treetime.move('item4', 'item1', 'end') + + +if __name__ == "__main__": + client = Client("opc.tcp://127.0.0.1:4840") + try: + client.connect() + client.load_type_definitions()# load definition of server specific structures/extension objects + + + root = client.get_root_node() + print(root) + objects = client.get_objects_node() + print("Objects node is: ", objects) + children = root.get_children() + print(children) + + rar = client.get_node("ns=2;i=1") + ram = rar.get_browse_name() + print(ram) + + var = client.get_node("ns=2;i=2") + vam = var.get_browse_name() + print(vam) + + par = client.get_node("ns=2;i=3").get_browse_name() + bam = par.__dict__['Name'] + + print(bam) + + gar = client.get_node("ns=2;i=4") + gam = gar.get_browse_name() + print(gam) + + finally: + client.disconnect() + + + + +root = window() +root.mainloop() \ No newline at end of file From 352c2d8c7df5f46652cadf6510623935bf881896 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 20 Jan 2021 20:16:35 +0100 Subject: [PATCH 12/77] Fixed service registration on server, now client finds the server. --- .vscode/settings.json | 2 +- server/announce_service.py | 25 +++++++++++++++++++++++++ server/server.py | 7 ++++++- uaxplorer/discovery.py | 11 +++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 server/announce_service.py diff --git a/.vscode/settings.json b/.vscode/settings.json index 5993f5e..eaefbe4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.linting.enabled": true, - "python.pythonPath": "C:\\Users\\FKV\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe" + "python.pythonPath": "/usr/sbin/python" } \ No newline at end of file diff --git a/server/announce_service.py b/server/announce_service.py new file mode 100644 index 0000000..9877976 --- /dev/null +++ b/server/announce_service.py @@ -0,0 +1,25 @@ +from zeroconf import ServiceInfo, Zeroconf +import socket + +ZC_PORT = 4840 +DEV_NAME = "_testopc." + +def start_service_announcement(): + info = ServiceInfo("_opcua-tcp._tcp.local.", + DEV_NAME + "_opcua-tcp._tcp.local.", + port = ZC_PORT, + server=DEV_NAME, + addresses=[socket.inet_pton(socket.AF_INET, get_ip_address())]) + zeroconf = Zeroconf() + zeroconf.register_service(info) + +def get_ip_address(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + return s.getsockname()[0] + +if __name__ == "__main__": + print("Starting service announcement for testing purposes"!) + print("Press enter to exit.") + start_service_announcement() + input() \ No newline at end of file diff --git a/server/server.py b/server/server.py index 678c283..bdb3e31 100644 --- a/server/server.py +++ b/server/server.py @@ -5,6 +5,11 @@ import sys, asyncio, OpenOPC, decimal, time, pywintypes from datetime import datetime from asyncua import ua, Server, uamethod +from zeroconf import ServiceInfo, Zeroconf +import socket + +#local imports +import announce_service as sa pywintypes.datetime = pywintypes.TimeType @@ -133,6 +138,6 @@ async def main(): await server.stop() da.close() - if __name__ == "__main__": + sa.start_service_announcement() asyncio.run(main()) \ No newline at end of file diff --git a/uaxplorer/discovery.py b/uaxplorer/discovery.py index 160acb2..c732f9e 100644 --- a/uaxplorer/discovery.py +++ b/uaxplorer/discovery.py @@ -44,7 +44,7 @@ def update_service(self, zeroconf, type, name): enligt https://github.com/OPCFoundation/UA-LDS/blob/master/zeroconf.c rad 222-238. """ -types = ["_opcua-https._tcp.local.", "_opcua-tcp._tcp.local.", "_opcua-wss._tcp.local."] +types = ["_opcua-tcp._tcp.local.", "_opcua-https._tcp.local.", "_opcua-wss._tcp.local.", "_opcua-tcp._tcp.local."] class Discovery: def __init__(self, types): self.sdict = {} @@ -62,4 +62,11 @@ def get_services(self): port = self.sdict[k][DPORT] for addr in self.sdict[k][DADDR]: l.append((name, addr, port)) - return l \ No newline at end of file + return l + +d = Discovery(types) + +if __name__ == "__main__": + while True: + print(d.get_services()) + input() From 97ddd309b97a7d91a85aa86bddf351c0194e00f8 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Thu, 21 Jan 2021 14:44:01 +0100 Subject: [PATCH 13/77] Made a navigating class with functions to dynamically grab all nodes and paths from the server --- uaxplorer/ui_methods/navigating_nodes.py | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 uaxplorer/ui_methods/navigating_nodes.py diff --git a/uaxplorer/ui_methods/navigating_nodes.py b/uaxplorer/ui_methods/navigating_nodes.py new file mode 100644 index 0000000..daa7706 --- /dev/null +++ b/uaxplorer/ui_methods/navigating_nodes.py @@ -0,0 +1,51 @@ +#Class for navigating the nodes + +from opcua import Client, ua + +#How to use the class in your own folder +#This is the import -> from ui_methods.navigating_nodes import Navigating_nodes as navNodes + +#Create an instance with the client -> navigating = navNodes(client) +#Run one of the functions with your instance -> navigating.get_root_nodes() + +#You could also run all of them at the same time like so: +#print(navigating.get_name_from_nodes(navigating.get_children_nodes(navigating.get_root_nodes()))) +#To get the names, but best is to save every node (Cleaner code) + +class Navigating_nodes: + + def __init__(self, client): + self.client = client + + + def get_root_nodes(self): + TEMP_ARRAY = [] + + for i in self.client.get_objects_node().get_children()[1:]: # Skipping first element as it is unnecessary, we grab all objects in a server + + TEMP_ARRAY.append(i) + + return TEMP_ARRAY + + def get_children_nodes(self, object_array): #Gets the children nodes from the root node and adds them into a dictonary + CHILDREN_NODE_DICT = {} + + for i in object_array: + for j in self.client.get_node(i).get_children(): + if i not in CHILDREN_NODE_DICT: + CHILDREN_NODE_DICT[i] = list() + CHILDREN_NODE_DICT[i].append(j) + + return CHILDREN_NODE_DICT + + def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects : values that are only path values and converts to the name + name_dict = {} + for key, values in dict_list.items(): + for value in values: + + if key not in name_dict: + name_dict.setdefault(self.client.get_node(key).get_browse_name().__dict__['Name'], []) + + name_dict[self.client.get_node(key).get_browse_name().__dict__['Name']].append(self.client.get_node(value).get_browse_name().__dict__['Name']) + + return name_dict From b830cc05777e885869b2b51f387b211cfcfbfcd1 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Thu, 21 Jan 2021 15:52:57 +0100 Subject: [PATCH 14/77] Changed port info to be a string, and added some usage documentation at the top of the file. --- uaxplorer/discovery.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/uaxplorer/discovery.py b/uaxplorer/discovery.py index c732f9e..dac4d58 100644 --- a/uaxplorer/discovery.py +++ b/uaxplorer/discovery.py @@ -1,5 +1,16 @@ from zeroconf import ServiceBrowser, Zeroconf + +# This is the Discovery implementation. +# +# Usage: Create an instance of the Discovery class. +# As input it takes a list of strings of the services +# it should search for. E.g : ["_opcua-tcp._tcp.local."] +# To get a list of all found servers, call +# the get_services() method and it will return a list of +# tuples of the following format: ('name', 'ip address', 'port') + + INAME = 0 IADDR = 1 IPORT = 2 @@ -11,7 +22,7 @@ def get_info(zeroconf, type, name): if info is not None: name = info.get_name() addresses = info.parsed_addresses() - port = info.port + port = str(info.port) return (name, addresses, port) return None @@ -44,7 +55,6 @@ def update_service(self, zeroconf, type, name): enligt https://github.com/OPCFoundation/UA-LDS/blob/master/zeroconf.c rad 222-238. """ -types = ["_opcua-tcp._tcp.local.", "_opcua-https._tcp.local.", "_opcua-wss._tcp.local.", "_opcua-tcp._tcp.local."] class Discovery: def __init__(self, types): self.sdict = {} @@ -67,6 +77,10 @@ def get_services(self): d = Discovery(types) if __name__ == "__main__": + Test_types = ["_opcua-tcp._tcp.local.", + "_opcua-https._tcp.local.", + "_opcua-wss._tcp.local."] + while True: print(d.get_services()) input() From 6d057f7b614405bfc46806c34d40c2e8b175d8fc Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Thu, 21 Jan 2021 16:00:07 +0100 Subject: [PATCH 15/77] Added some documentation and default values to the start_service_announcement function. --- server/announce_service.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/server/announce_service.py b/server/announce_service.py index 9877976..840aff8 100644 --- a/server/announce_service.py +++ b/server/announce_service.py @@ -1,18 +1,29 @@ from zeroconf import ServiceInfo, Zeroconf import socket +# This file handles the announcement of the OPCUA +# service for zeroconf discovery to work. +# +# Usage: +# Call the start_service_announcement() function. +# Input is name of the device and the port it's using. + +# Standard OPCUA server port, per IANA. ZC_PORT = 4840 +# Test DEV_NAME = "_testopc." -def start_service_announcement(): +def start_service_announcement(device_name=DEV_NAME, port=ZC_PORT): info = ServiceInfo("_opcua-tcp._tcp.local.", - DEV_NAME + "_opcua-tcp._tcp.local.", + device_name + "_opcua-tcp._tcp.local.", port = ZC_PORT, - server=DEV_NAME, + server=device_name, addresses=[socket.inet_pton(socket.AF_INET, get_ip_address())]) zeroconf = Zeroconf() zeroconf.register_service(info) +# Hacky way to get ip address which isn't localhost under linux. +# The google dns ip does not have to be routeable. def get_ip_address(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) From 410a4921f4ebc43788fcf2c45b73a2ba5fe58682 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Thu, 21 Jan 2021 16:02:25 +0100 Subject: [PATCH 16/77] Added some more documentation and fixed the name of the device in the server file. --- server/announce_service.py | 2 ++ server/server.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/server/announce_service.py b/server/announce_service.py index 840aff8..8262640 100644 --- a/server/announce_service.py +++ b/server/announce_service.py @@ -7,6 +7,8 @@ # Usage: # Call the start_service_announcement() function. # Input is name of the device and the port it's using. +# The device name should take the form "_NAME.". +# The beginning underscore and ending dot is important. # Standard OPCUA server port, per IANA. ZC_PORT = 4840 diff --git a/server/server.py b/server/server.py index bdb3e31..ca7415a 100644 --- a/server/server.py +++ b/server/server.py @@ -139,5 +139,5 @@ async def main(): da.close() if __name__ == "__main__": - sa.start_service_announcement() + sa.start_service_announcement(device_name="_adda-server.") asyncio.run(main()) \ No newline at end of file From 908f04d18f7d5b49025a43006878fd71b471f014 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:32:55 +0100 Subject: [PATCH 17/77] Moved discovery to ui_methods also fixed server_discovery to work with discovery Server_discovery is now useful, can grab all the servers. --- server/announce_service.py | 2 +- uaxplorer/{ => ui_methods}/discovery.py | 4 +-- uaxplorer/ui_methods/server_discovery.py | 40 ++++++++++++++++-------- 3 files changed, 30 insertions(+), 16 deletions(-) rename uaxplorer/{ => ui_methods}/discovery.py (98%) diff --git a/server/announce_service.py b/server/announce_service.py index 8262640..7a429dc 100644 --- a/server/announce_service.py +++ b/server/announce_service.py @@ -32,7 +32,7 @@ def get_ip_address(): return s.getsockname()[0] if __name__ == "__main__": - print("Starting service announcement for testing purposes"!) + print("Starting service announcement for testing purposes!") print("Press enter to exit.") start_service_announcement() input() \ No newline at end of file diff --git a/uaxplorer/discovery.py b/uaxplorer/ui_methods/discovery.py similarity index 98% rename from uaxplorer/discovery.py rename to uaxplorer/ui_methods/discovery.py index dac4d58..10f262d 100644 --- a/uaxplorer/discovery.py +++ b/uaxplorer/ui_methods/discovery.py @@ -74,13 +74,13 @@ def get_services(self): l.append((name, addr, port)) return l -d = Discovery(types) + if __name__ == "__main__": Test_types = ["_opcua-tcp._tcp.local.", "_opcua-https._tcp.local.", "_opcua-wss._tcp.local."] - + d = Discovery(Test_types) while True: print(d.get_services()) input() diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index 446fe7b..9e99938 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -1,21 +1,35 @@ #DISCOVER ALL SERVERS WITH THIS METHOD. +import discovery as disc -TUPLE_ARRAY = [] -DISCOVERY_OUTPUT = [('test_server', 'IP_ADDRESS', 'PORT_NUMBER'), ('test_server2', 'IP_ADDRESS2', 'PORT_NUMBER2')] #Change this to the discovery - #output we get later on +# For testing: +#d = Server_Discovery() # Create an instance -def insert_array(servers): - for i in servers: - TUPLE_ARRAY.append(i) +#print(d.get_servers()) #Get all servers available in a tuple -def get_all(array, type): #0 for server name, 1 for ip address, 2 for port number - temp_array = [] - for i in TUPLE_ARRAY: - temp_array.append(i[type]) - return temp_array +#print(d.get_all(1)) # Get all ip addresses from all servers +class Server_Discovery(): + def __init__(self): + self.DISCOVERY_OUTPUT = [] + def get_servers(self): # To find the servers available + Test_types = ["_opcua-tcp._tcp.local.", + "_opcua-https._tcp.local.", + "_opcua-wss._tcp.local."] -insert_array(DISCOVERY_OUTPUT) -print(get_all(TUPLE_ARRAY, 1)) \ No newline at end of file + d = disc.Discovery(Test_types) + + while(len(self.DISCOVERY_OUTPUT) == 0): + if(len(d.get_services()) > 0): + self.DISCOVERY_OUTPUT = d.get_services() + + return self.DISCOVERY_OUTPUT + + + def get_all(self, type): #0 for server name, 1 for ip address, 2 for port number + temp_array = [] + for i in self.DISCOVERY_OUTPUT: + temp_array.append(i[type]) + + return temp_array From 2ff81772d7cd0d950fd13a828f656e72009a5702 Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Mon, 25 Jan 2021 19:20:43 +0100 Subject: [PATCH 18/77] This is the Main Ui --- uaxplorer/MainUI.py | 116 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 uaxplorer/MainUI.py diff --git a/uaxplorer/MainUI.py b/uaxplorer/MainUI.py new file mode 100644 index 0000000..040a132 --- /dev/null +++ b/uaxplorer/MainUI.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'zuo.ui' +# +# Created by: PyQt5 UI code generator 5.15.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from zeroconf import ServiceBrowser, Zeroconf +from ui_methods.server_discovery import * +#from Ui_client import * +import sys + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + + MainWindow.setObjectName("MainWindow") + MainWindow.resize(912, 490) + MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.Connect = QtWidgets.QPushButton(self.centralwidget) + self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) + self.Connect.setStyleSheet("color: rgb(200, 200, 200);") + self.Connect.setObjectName("Connect") + self.Discover = QtWidgets.QPushButton(self.centralwidget) + self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) + self.Discover.setStyleSheet("color: rgb(200, 200, 200);") + self.Discover.clicked.connect(self.discover_servers) + self.Discover.setObjectName("Discover") + self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) + self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) + self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") + self.lineEdit.setObjectName("lineEdit") + self.groupBox = QtWidgets.QGroupBox(self.centralwidget) + self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) + self.groupBox.setFlat(False) + self.groupBox.setCheckable(False) + self.groupBox.setObjectName("groupBox") + self.groupBox.setStyleSheet("color: rgb(200,200,200);") + self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) + self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) + self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) + self.horizontalScrollBar.setObjectName("horizontalScrollBar") + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) + self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) + self.textBrowser.setObjectName("textBrowser") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) + self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) + self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") + self.pushButton.setObjectName("pushButton") + self.treeView = QtWidgets.QTreeView(self.centralwidget) + self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) + self.treeView.setObjectName("treeView") + self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) + self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.Connect.raise_() + self.Discover.raise_() + self.lineEdit.raise_() + self.textBrowser.raise_() + self.pushButton.raise_() + self.treeView.raise_() + self.groupBox.raise_() + self.verticalScrollBar.raise_() + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) + self.Connect.setText(_translate("MainWindow", "Connect")) + self.Discover.setText(_translate("MainWindow", "Discover")) + self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) + self.textBrowser.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.pushButton.setText(_translate("MainWindow", "Disconnect")) + + def discover_servers(self): + servers = Server_Discovery() + servers_arr = servers.get_servers() + + + + + + + + + +if __name__ == "__main__": + + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + + sys.exit(app.exec_()) From 4a55ca54a12c444d993a594a42f01c45d3886967 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Mon, 25 Jan 2021 21:14:28 +0100 Subject: [PATCH 19/77] Added discovery utility function to get a server as address:port combo. --- .../__pycache__/discovery.cpython-39.pyc | Bin 0 -> 2449 bytes .../__pycache__/server_discovery.cpython-39.pyc | Bin 0 -> 1089 bytes uaxplorer/ui_methods/server_discovery.py | 13 ++++++++++++- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 uaxplorer/ui_methods/__pycache__/discovery.cpython-39.pyc create mode 100644 uaxplorer/ui_methods/__pycache__/server_discovery.cpython-39.pyc diff --git a/uaxplorer/ui_methods/__pycache__/discovery.cpython-39.pyc b/uaxplorer/ui_methods/__pycache__/discovery.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d33f177789f788c519eccb7912800046e005792 GIT binary patch literal 2449 zcmcgtO;Z~;7?$?SYhyb=35B#Yq;1-98gNgY4yBy{ZabZ6NhYK-9nW+&+m#Fpws(~nfS&MasxXdMMJht7bWxtWzYncK4W0O}sOq`Ng&&+c;-z}a|>Anvvj?c+!{>~KAs zhC*@{gp3I;C70TkN@yodgsOSk?+%H)G+D-%)^*;BJ zLp){y?N9|lXqo!vE71}a@4-lf!;=NA`2?zb41$v;1s_|CG7J1`Gn+Zkg14Cmt;-6` zht^|7@K;ICHAd(E981N!TtxW0Q9&K6@e4*E_=E(;$^v~>qk3j&V-`mWJnu-}Zi%}v zXAF~zdFX0C05`-c2tIA>hL7?j#TBZHfX^hvmOd&6dhn|8B(y712G5L z2P3ZXOa_ue8{-gi)Iws;DWjlWiDV)0uO`8?i!>|E^|jv~5|KcPsM&cQeg?WO{EPJ*%>5W%f5ie@p$ui3ICJ>cf~_ELpw+O zRBE?@DFuy5FfU9~-eonY)43k^nkrYT);$sw_pfJ(*C2F_D+@~s9etJSo4!7?H>WvlRzSX+Fy@R#&QbO{!UC F`X7U78GQf% literal 0 HcmV?d00001 diff --git a/uaxplorer/ui_methods/__pycache__/server_discovery.cpython-39.pyc b/uaxplorer/ui_methods/__pycache__/server_discovery.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98b1aa083c7e29074fd22f148c177e0b202e04e1 GIT binary patch literal 1089 zcmZuv&2G~`5T4z2QpZhclmoO9>H%@_p~(wWRUttLA*B^*k+6go%e$necI>X5LZal< zK1j>0ujDHy9sy3wI3!IAEA8z3?d&(-j=!7szz;H37y94;clI2G0>vVT z83zV2_XLC2pturOpu{EjEXDfZT0b!pdI8DR#nCvxT~^ZIF$Er1Oz|u5gc8ca(^3^x z#j~Q;u##Og`r2G*BfDW1B&du7wo;OGs@kEd)W!g&AQ5FBZduP|MWg$s-#L8$>fHx< zcyfGna*RGJ)6wT!FjdU|{kDCY#JcSVXS&_h7wyBN&dH12P7=qXVHo&%m<-z^|63X* zMw|91l(EiF6P2~IIWD!p)k?>OEoC?ib1CbDhkyZ_Y(MOvwaB{H3eilvY~>{?1El&p zo3R|O*_7R|8PE9?zKBaC?2MZ|wD`4k1Ep=5=G?;e#d(wRkUr6K#pJO}(qQE8=0Vz$ zB%&nnqt;~Wum3d9)9hYkapP-t&u@rrw^CTtvsYXGNFgFUG!_QE`atKBmxN;>ZS*9zv2m?J? za`+6D0nCUq=)%Y5OdvySDyD23ZWx1eXUh9vh;x^h@QbRSrh2Gcs{~$HgxPa)9jDSa z#vd0fd#qvxU7``rL}Qvz*%2-PE?0<61a(Uaz)vo7$r6%2Vab R=3+8?(`1zi9D literal 0 HcmV?d00001 diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index 9e99938..f1c1e8d 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -25,7 +25,6 @@ def get_servers(self): # To find the servers available self.DISCOVERY_OUTPUT = d.get_services() return self.DISCOVERY_OUTPUT - def get_all(self, type): #0 for server name, 1 for ip address, 2 for port number temp_array = [] @@ -33,3 +32,15 @@ def get_all(self, type): #0 for server name, 1 for ip address, 2 for port number temp_array.append(i[type]) return temp_array + + def combine(self, server): + return server[1] + ":" + server[2] + + def get_all_as_address(self): + arr = [] + for s in self.DISCOVERY_OUTPUT: + arr.append(combine(s)) + + return arr + + From 0e6f2e8a43ccc4da5737917166f000c3227dc7fe Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Mon, 25 Jan 2021 21:16:14 +0100 Subject: [PATCH 20/77] Same as last commit, added utility for discovery. --- server/client.py | 28 ++++++++++++++++++ .../__pycache__/uaxplorer.cpython-39.pyc | Bin 0 -> 2018 bytes uaxplorer/uaxplorer.py | 2 -- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 server/client.py create mode 100644 uaxplorer/__pycache__/uaxplorer.cpython-39.pyc diff --git a/server/client.py b/server/client.py new file mode 100644 index 0000000..0029127 --- /dev/null +++ b/server/client.py @@ -0,0 +1,28 @@ +from asyncua import Client + +# Example of url: +# 'opc.tcp://localhost:4840/freeopcua/server/' +class LocalClient(): + def __init__(self, url): + self.nodes = {} + self.values = {} + self.url = url + + def run(self): + async with Client(self.url) as client: + while True: + for n in self.nodes.keys(): + node = client.get_node(n) + value = await node.read_value() + self.values[self.nodes[n]] = value + + def add_node(self, node, name): + self.nodes[node] = name + + def remove_node(self, node, name): + self.values.pop(name) + self.nodes.pop(node) + + def get_value(self, name): + return self.values[name]) + diff --git a/uaxplorer/__pycache__/uaxplorer.cpython-39.pyc b/uaxplorer/__pycache__/uaxplorer.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af35695bf0f2c68e79e024d874e115ab7c4bc0ec GIT binary patch literal 2018 zcmcgtPj4GV6rb6DUfXNOp#>snAy*?2w~nPkpa`0vs!|CRxE$uPYz7snp)O)_ePA-kUe`d;5Fipw(&+c>da0`}JOp zkbiJceJmJ!3zGi=iW5#_l2Aq|+D2?7CNs5d#ul@<#cV-^eN34noLOy7D7jC#&7Bj% z9pN6EtS;)ugf&EC)`2HL`;(1q9B!#_ywmI5 z?%&zE1;5)ngD(cRLR?mT%;g{-fFTH@oG{}hIiQ?!7nCB3PRX$`GZ4a*%uG(_P!g~#Zg6vMP@?RSGbP-b zl2MJ@r{yGPA_6`zOjsHg5A_H^-D1gnD>@3EwEcUruizS4SRXKb!xyq zYv5l4Kkl^J2mS5W{1|AvCF^+!_V z`CurLv5POc4~iA{Bpe>2K=&Zu;~tG?hAJ&b>mZEG?S=G(3Ew2h3T#OY5nOcFb?wE_s__GU-q75iRcC4q3G?2qu!5?b{~KD`7RW* zNTV=NQI__K;Q2Vtq>vZZ*7${N14NYk6HtVGG^wS{6Up9vZrJU9j4g_tRFeRdFV9YE3jO%$Jw8_X@0l= zRdly6J;-&A-K04o;o2cx7l@%*Rk}}w^3#k9W+9)-I*yNmxDc{~Yu-oo0jl?Kp&>tn z7i(6yCqF{3Rv)9a4(c Date: Mon, 25 Jan 2021 21:43:26 +0100 Subject: [PATCH 21/77] Fixed not using local method combine. --- uaxplorer/ui_methods/server_discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index f1c1e8d..29f0442 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -39,7 +39,7 @@ def combine(self, server): def get_all_as_address(self): arr = [] for s in self.DISCOVERY_OUTPUT: - arr.append(combine(s)) + arr.append(self.combine(s)) return arr From 7f0018f2d7a7a8c9f92d00d0076b51eba240e81e Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Wed, 27 Jan 2021 13:44:03 +0100 Subject: [PATCH 22/77] UI Client for connecting to the opc servers --- uaxplorer/Ui_client.py | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 uaxplorer/Ui_client.py diff --git a/uaxplorer/Ui_client.py b/uaxplorer/Ui_client.py new file mode 100644 index 0000000..1ef6853 --- /dev/null +++ b/uaxplorer/Ui_client.py @@ -0,0 +1,73 @@ +from opcua import Client, ua +from ui_methods.server_discovery import * +import time + + + +class Ui_client(): + def __init__(self): + self.client = Client + + + def discover_servers(self): + url = Server_Discovery() + servers = url.get_servers() + print(servers) + + + def pick_server(self): + + sd = Server_Discovery() + sd.get_servers() + print("Enter the Ip:Port to the server you wanna connect to") + server_url = input() #ip and port + if server_url in sd.get_all_as_address(): + client = Client("opc.tcp://" + server_url) + client.connect() + print("client is connected") + else: + print("ip doesnt exist") + + + + + + + + + + + + # def client_connection(self, url): + + + #url_a = url.get_all(1) + url.get_all(2) + #url = ':'.join(map(str, url_a)) + # print(url) + + + #client = Client("opc.tcp://" + url) + + #try: + # client.connect() + # if True: + # print("Client is now connected to the server") + + # else: + # print("The client cannot connect to the server") + + # finally: + # client.disconnect() + + + + + + +if __name__ =="__main__": + Ui_client().discover_servers() + Ui_client().pick_server() + + + + \ No newline at end of file From 88b63f9fad29a5885450a80a7dd61eab2ac342fb Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 27 Jan 2021 22:04:19 +0100 Subject: [PATCH 23/77] Fixed using the wrong port in announce_service. --- server/announce_service.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/announce_service.py b/server/announce_service.py index 7a429dc..da062eb 100644 --- a/server/announce_service.py +++ b/server/announce_service.py @@ -15,10 +15,10 @@ # Test DEV_NAME = "_testopc." -def start_service_announcement(device_name=DEV_NAME, port=ZC_PORT): +def start_service_announcement(device_name=DEV_NAME, iport=ZC_PORT): info = ServiceInfo("_opcua-tcp._tcp.local.", device_name + "_opcua-tcp._tcp.local.", - port = ZC_PORT, + port = iport, server=device_name, addresses=[socket.inet_pton(socket.AF_INET, get_ip_address())]) zeroconf = Zeroconf() @@ -35,4 +35,4 @@ def get_ip_address(): print("Starting service announcement for testing purposes!") print("Press enter to exit.") start_service_announcement() - input() \ No newline at end of file + input() From 2c7b2458e3d2c0a32f883f8f44bd9c20ae97f6d5 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 27 Jan 2021 22:25:41 +0100 Subject: [PATCH 24/77] Fixed so we make a copy of the keys list to iterate over while getting servers. --- uaxplorer/ui_methods/discovery.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uaxplorer/ui_methods/discovery.py b/uaxplorer/ui_methods/discovery.py index 10f262d..f30ecc7 100644 --- a/uaxplorer/ui_methods/discovery.py +++ b/uaxplorer/ui_methods/discovery.py @@ -67,7 +67,7 @@ def get_services(self): l = [] if not self.sdict: return l - for k in self.sdict.keys(): + for k in list(self.sdict.keys()): name = k port = self.sdict[k][DPORT] for addr in self.sdict[k][DADDR]: From 431d0b0c05193b2b2d3a21724c2bc16909101806 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 27 Jan 2021 22:28:11 +0100 Subject: [PATCH 25/77] Updated server discovery to run the function for 5 seconds instead of till the array isnt the length of 0 anymore --- uaxplorer/ui_methods/server_discovery.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index 29f0442..ded2cf7 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -1,6 +1,6 @@ #DISCOVER ALL SERVERS WITH THIS METHOD. import discovery as disc - +import time # For testing: #d = Server_Discovery() # Create an instance @@ -19,11 +19,11 @@ def get_servers(self): # To find the servers available "_opcua-wss._tcp.local."] d = disc.Discovery(Test_types) - - while(len(self.DISCOVERY_OUTPUT) == 0): + time_to_end = time.time() + 5 + while(time.time() < time_to_end): if(len(d.get_services()) > 0): self.DISCOVERY_OUTPUT = d.get_services() - + return self.DISCOVERY_OUTPUT def get_all(self, type): #0 for server name, 1 for ip address, 2 for port number @@ -42,5 +42,3 @@ def get_all_as_address(self): arr.append(self.combine(s)) return arr - - From 294453081be05d30dae938337371d6fa723f571e Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Thu, 28 Jan 2021 15:38:23 +0100 Subject: [PATCH 26/77] Fixed connecting to client dynamically. --- uaxplorer/Ui_client.py | 73 ------------------------------- uaxplorer/ui_methods/Ui_client.py | 39 +++++++++++++++++ 2 files changed, 39 insertions(+), 73 deletions(-) delete mode 100644 uaxplorer/Ui_client.py create mode 100644 uaxplorer/ui_methods/Ui_client.py diff --git a/uaxplorer/Ui_client.py b/uaxplorer/Ui_client.py deleted file mode 100644 index 1ef6853..0000000 --- a/uaxplorer/Ui_client.py +++ /dev/null @@ -1,73 +0,0 @@ -from opcua import Client, ua -from ui_methods.server_discovery import * -import time - - - -class Ui_client(): - def __init__(self): - self.client = Client - - - def discover_servers(self): - url = Server_Discovery() - servers = url.get_servers() - print(servers) - - - def pick_server(self): - - sd = Server_Discovery() - sd.get_servers() - print("Enter the Ip:Port to the server you wanna connect to") - server_url = input() #ip and port - if server_url in sd.get_all_as_address(): - client = Client("opc.tcp://" + server_url) - client.connect() - print("client is connected") - else: - print("ip doesnt exist") - - - - - - - - - - - - # def client_connection(self, url): - - - #url_a = url.get_all(1) + url.get_all(2) - #url = ':'.join(map(str, url_a)) - # print(url) - - - #client = Client("opc.tcp://" + url) - - #try: - # client.connect() - # if True: - # print("Client is now connected to the server") - - # else: - # print("The client cannot connect to the server") - - # finally: - # client.disconnect() - - - - - - -if __name__ =="__main__": - Ui_client().discover_servers() - Ui_client().pick_server() - - - - \ No newline at end of file diff --git a/uaxplorer/ui_methods/Ui_client.py b/uaxplorer/ui_methods/Ui_client.py new file mode 100644 index 0000000..894fc48 --- /dev/null +++ b/uaxplorer/ui_methods/Ui_client.py @@ -0,0 +1,39 @@ +from opcua import Client, ua +from server_discovery import * + + + +class Ui_client(): + def __init__(self, server): + self.client = Client + self.servers = server + + def establish_client_connection(self): + + self.client = Client("opc.tcp://" + self.servers) + try: + self.client.connect() + print("client is connected: " + self.servers) + + finally: + self.client.disconnect() + + + +def create_connection(): + url = Server_Discovery() + url.get_servers() + servers = url.get_all_as_address() + print(servers) + established_servers = list() + for i in servers: + established_servers.append(Ui_client(i)) + + established_servers[0].establish_client_connection() + established_servers[1].establish_client_connection() + print(established_servers[0].client.get_root_node()) + + + + +create_connection() \ No newline at end of file From b20c4bc8a5242582b87b296f9c8f4d042c264966 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 29 Jan 2021 11:35:15 +0100 Subject: [PATCH 27/77] Changed name of client.py to localclient.py to match the class name. --- server/{client.py => localclient.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename server/{client.py => localclient.py} (100%) diff --git a/server/client.py b/server/localclient.py similarity index 100% rename from server/client.py rename to server/localclient.py From d9277c461f6e277d9bf18316281e6e8274af8989 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 3 Feb 2021 08:07:21 +0100 Subject: [PATCH 28/77] Some changes to py.server moved and MainUI is still being fixed. --- server/__pycache__/server.cpython-38.pyc | Bin 0 -> 3680 bytes {uaxplorer => server}/py.server.py | 86 +++--- uaxplorer/{ => ui_methods}/MainUI.py | 263 ++++++++++-------- uaxplorer/ui_methods/Ui_client.py | 15 +- .../__pycache__/Ui_client.cpython-38.pyc | Bin 0 -> 1153 bytes .../navigating_nodes.cpython-38.pyc | Bin 0 -> 1470 bytes uaxplorer/ui_methods/navigating_nodes.py | 3 +- 7 files changed, 197 insertions(+), 170 deletions(-) create mode 100644 server/__pycache__/server.cpython-38.pyc rename {uaxplorer => server}/py.server.py (76%) rename uaxplorer/{ => ui_methods}/MainUI.py (81%) create mode 100644 uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc create mode 100644 uaxplorer/ui_methods/__pycache__/navigating_nodes.cpython-38.pyc diff --git a/server/__pycache__/server.cpython-38.pyc b/server/__pycache__/server.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..535f52e3b68812d2d24a6c894d554cd213e1a309 GIT binary patch literal 3680 zcmai0&2QYs6`vuwB$xZ4Pk+d962(c2C}em$)BgD@!joL-~f44eH{*eYe-YlrP}{Y*G3YX@_(7P+Dp&I>nO zNLaXtaslNc$|d22mjw%#gdbiJ_3#yxtM`})MB{TN8uHZ>JA6$vMeB1GzAoBg2H#~d zE9UThLtYc}hnD)goIQ9`EQrM~IA@ZN-;!^funF3N@3NH(FK}zNy~2$zVl8#jmn*ih zhOu#XrFt%v@rQ9=>Zh4lu}p*dl8)R;yP5Grsj^O%c8^<6wI1ZR*4CarU(KaqFKXEH z1*ghF)j@gbN1Y#_&0Pfoe4q><%B_k5kc;>Ucd^#t$8NM3DF}zgE2fakY z71ru?!{gsGHIIR>KUmwxRr9r<{`|q(hRhFjHdxD({@@7J+D9MX-CuwAZr1M))1(uV z?ba&8Jet~PaAbnpy(HJ2tUtK(UDAn+l!D`gf#Z^ zxMv(vISd9-JUuQ9L>$FJD4FNR5^-t2s#<`?=FOw1wwkn(wk+kgT7n3Mty>VGWG=Kr&r1` z7h`+h9xEuQt!+hz!RkShCuy$Zv?GltWGCsv^4`X@QS0`ySR02z!`MBUnwlOCdXl0U zE*i>nS1nx-C>e{W99qQzIas^2dJ{G$tGi|GSn4|ZUqba9I_fBYM#)2HR{*GoC5nrZwF%cia!t_J)4XtnePV5}Pj?xH&=&2h;Fs>E6s86gdVE8_xRkzu*8-+b~3unTy`%fO(qvohJY8OrI z9s264qAhAT=a1SSwbLt*Tu6V?bx2J}9;B~yfcb6RD4N*qOWo8B-CAN@4j0bob}@6p z^~|BI{wW;DrKaOq=;7(P@q96(=MEMQ7AMxkuCyA>V!gM-wBO~2?w4Gx7PI<7;oN4^ z{<;pj)}gDMVs?CS!m3qZv{`+LW<3Kd!u|fXYQfAe{)1o1PGvM0X9vzRwTFbu@7t#9N zyPLba(azSr`+K{laVl=EZ5WJTZxx5Y~5p;uWVH%>RfHarR4scl?LAc}N|NsX0= za8AgxcQMMkQ6khDwhDvJP^k(^VUP_v!?-d*NhxQ%VrKG8>2f5G2&O;6+WZzOW>t{$ z`8+7kwdvdBm#sxekf+4~AlvA7thcOL?xVE1`WUM_w8iO_u5}bPgr)Fc0=M*3v zTa${VINEjrnBr-53IsU5wG%e73Ty113q@<}iRziP#=fpk_%yWb!a4{J8sla`yG&sS z@Uw%Ku<@MNzOe9&xA9Ed1$s}M)8Ib-Lt(u)4fWsM!KrgN)y1hu%L?~wC$I3v3wm*U zVFK_^&-8T*Jpc;eSbxagXTO~8v%@}N`qKEa@J{CfL~OhSf*>lmF$!?UfbLj;SU@ig zy;#yrvuKX5=vUC|RS5{CADlh=_p`sz{?KaXoGsXRmJ*FmKsgmGiItDvhPPLBXMn8i z`fB;V_1+KOyS~;1=E^{rrT}Y(zq1D_t|NsSsvJpF@-J*qpT_Y z>O)#pL$(2YHb5!SbkVn;JEtEfDIq1lfr>dkkQ8v=c7dXR&+?0Qn=e`xBp2vgw6EHP zFT$VCUvq%8Cs;)Z2JI6?$1O^4;&-R19bDTGl9Kk)7c1X^&PTb5u%>eE3axf1@kg3; z^cma-ddn3hs9|NdVO$bn>25_yYv>V2wyOw|vmBIZ%VLFzVDcTgF38X^=8C4-l7Qr=xrZf33^ z0Rf3k4~=o3-eYM7og`!~er->!18J9Mp_qTo8LzX+@Qwr}~C!_WTCS<9N8@mv1e J{yXHU{{k-Oy)^&; literal 0 HcmV?d00001 diff --git a/uaxplorer/py.server.py b/server/py.server.py similarity index 76% rename from uaxplorer/py.server.py rename to server/py.server.py index e65a6a5..95f53eb 100644 --- a/uaxplorer/py.server.py +++ b/server/py.server.py @@ -1,41 +1,45 @@ -from opcua import Server -from random import randint -import datetime -import time - -server = Server() - -url = "opc.tcp://127.0.0.1:4840" -server.set_endpoint(url) - -name = "Test for GUI" - -addspace = server.register_namespace(name) - -node = server.get_objects_node() - -Params = node.add_object(addspace, "Parameters") - -Temp = Params.add_variable(addspace, "Temperature",0) -Press = Params.add_variable(addspace, "Pressure",0) -Time = Params.add_variable(addspace, "Time",0) - -Temp.set_writable() -Press.set_writable() -Time.set_writable() - -#starting server -server.start() - -while True: - Temperature = randint(0,35) - Pressure = randint(100,250) - TIME = datetime.datetime.now() - - print(Temperature,Pressure,TIME) - - Temp.set_value(Temperature) - Press.set_value(Pressure) - Time.set_value(TIME) - - time.sleep(5) +from opcua import Server +from random import randint +import datetime +import time, sys +sys.path.insert(1, '/Users/FKV/Desktop/simpysim/OPCUA-Communication/server') +import announce_service as sa + +server = Server() + +url = "opc.tcp://192.168.10.196:4840" +server.set_endpoint(url) + +name = "Test for GUI" + +addspace = server.register_namespace(name) + +node = server.get_objects_node() + +Params = node.add_object(addspace, "Parameters") + +Temp = Params.add_variable(addspace, "Temperature",0) +Press = Params.add_variable(addspace, "Pressure",0) +Time = Params.add_variable(addspace, "Time",0) + +Temp.set_writable() +Press.set_writable() +Time.set_writable() + +#starting server +server.start() +sa.start_service_announcement(device_name="kfd347-server.", +iport=4840) + +while True: + Temperature = randint(0,35) + Pressure = randint(100,250) + TIME = datetime.datetime.now() + + print(Temperature,Pressure,TIME) + + Temp.set_value(Temperature) + Press.set_value(Pressure) + Time.set_value(TIME) + + time.sleep(5) diff --git a/uaxplorer/MainUI.py b/uaxplorer/ui_methods/MainUI.py similarity index 81% rename from uaxplorer/MainUI.py rename to uaxplorer/ui_methods/MainUI.py index 040a132..0111b8f 100644 --- a/uaxplorer/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -1,116 +1,147 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'zuo.ui' -# -# Created by: PyQt5 UI code generator 5.15.2 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets -from zeroconf import ServiceBrowser, Zeroconf -from ui_methods.server_discovery import * -#from Ui_client import * -import sys - - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - - MainWindow.setObjectName("MainWindow") - MainWindow.resize(912, 490) - MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") - self.centralwidget = QtWidgets.QWidget(MainWindow) - self.centralwidget.setObjectName("centralwidget") - self.Connect = QtWidgets.QPushButton(self.centralwidget) - self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) - self.Connect.setStyleSheet("color: rgb(200, 200, 200);") - self.Connect.setObjectName("Connect") - self.Discover = QtWidgets.QPushButton(self.centralwidget) - self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) - self.Discover.setStyleSheet("color: rgb(200, 200, 200);") - self.Discover.clicked.connect(self.discover_servers) - self.Discover.setObjectName("Discover") - self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) - self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) - self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") - self.lineEdit.setObjectName("lineEdit") - self.groupBox = QtWidgets.QGroupBox(self.centralwidget) - self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) - self.groupBox.setFlat(False) - self.groupBox.setCheckable(False) - self.groupBox.setObjectName("groupBox") - self.groupBox.setStyleSheet("color: rgb(200,200,200);") - self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) - self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) - self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) - self.horizontalScrollBar.setObjectName("horizontalScrollBar") - self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) - self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) - self.textBrowser.setObjectName("textBrowser") - self.pushButton = QtWidgets.QPushButton(self.centralwidget) - self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) - self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") - self.pushButton.setObjectName("pushButton") - self.treeView = QtWidgets.QTreeView(self.centralwidget) - self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) - self.treeView.setObjectName("treeView") - self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) - self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) - self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) - self.verticalScrollBar.setObjectName("verticalScrollBar") - self.Connect.raise_() - self.Discover.raise_() - self.lineEdit.raise_() - self.textBrowser.raise_() - self.pushButton.raise_() - self.treeView.raise_() - self.groupBox.raise_() - self.verticalScrollBar.raise_() - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) - self.menubar.setObjectName("menubar") - MainWindow.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(MainWindow) - self.statusbar.setObjectName("statusbar") - MainWindow.setStatusBar(self.statusbar) - - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) - self.Connect.setText(_translate("MainWindow", "Connect")) - self.Discover.setText(_translate("MainWindow", "Discover")) - self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) - self.textBrowser.setHtml(_translate("MainWindow", "\n" -"\n" -"


")) - self.pushButton.setText(_translate("MainWindow", "Disconnect")) - - def discover_servers(self): - servers = Server_Discovery() - servers_arr = servers.get_servers() - - - - - - - - - -if __name__ == "__main__": - - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - - sys.exit(app.exec_()) +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'zuo.ui' +# +# Created by: PyQt5 UI code generator 5.15.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + + +from PyQt5 import QtCore, QtGui, QtWidgets, Qt +from zeroconf import ServiceBrowser, Zeroconf +import server_discovery as dsc +import Ui_client as ui_c +import navigating_nodes as nav +#from Ui_client import * +import sys + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + self.clients = list() + MainWindow.setObjectName("MainWindow") + MainWindow.resize(912, 490) + MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.Connect = QtWidgets.QPushButton(self.centralwidget) + self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) + self.Connect.setStyleSheet("color: rgb(200, 200, 200);") + self.Connect.setObjectName("Connect") + self.Discover = QtWidgets.QPushButton(self.centralwidget) + self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) + self.Discover.setStyleSheet("color: rgb(200, 200, 200);") + self.Discover.clicked.connect(self.discover_servers) + self.Discover.setObjectName("Discover") + self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) + self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) + self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") + self.lineEdit.setObjectName("lineEdit") + self.groupBox = QtWidgets.QGroupBox(self.centralwidget) + self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) + self.groupBox.setFlat(False) + self.groupBox.setCheckable(False) + self.groupBox.setObjectName("groupBox") + self.groupBox.setStyleSheet("color: rgb(200,200,200);") + self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) + self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) + self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) + self.horizontalScrollBar.setObjectName("horizontalScrollBar") + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) + self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) + self.textBrowser.setObjectName("textBrowser") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) + self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) + self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") + self.pushButton.setObjectName("pushButton") + self.treeView = QtWidgets.QTreeView(self.centralwidget) + self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) + self.treeView.setHeaderHidden(True) + self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) + self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.Connect.raise_() + self.Discover.raise_() + self.lineEdit.raise_() + self.textBrowser.raise_() + self.pushButton.raise_() + self.treeView.raise_() + self.groupBox.raise_() + self.verticalScrollBar.raise_() + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + treeModel = Qt.QStandardItemModel() + self.rootNode = treeModel.invisibleRootItem() + test = Qt.QStandardItem('Testing') + self.rootNode.appendRow(test) + self.treeView.setModel(treeModel) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) + self.Connect.setText(_translate("MainWindow", "Connect")) + self.Discover.setText(_translate("MainWindow", "Discover")) + self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) + self.textBrowser.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.pushButton.setText(_translate("MainWindow", "Disconnect")) + + def create_model(self): + temp_node_arr = list() + name_dict = {} + for i in self.clients: + + temp_node_arr.append(nav.Navigating_nodes(i.client)) + i.client.connect() + + for i in temp_node_arr: + name_dict = i.get_name_from_nodes( + i.get_children_nodes( + i.get_root_nodes())) + + + + + + print(temp_node_arr[0].get_name_from_nodes(temp_node_arr[0].get_children_nodes(temp_node_arr[0].get_root_nodes()))) + + def discover_servers(self): + url = dsc.Server_Discovery() + url.get_servers() + servers = url.get_all_as_address() + for i in servers: + self.clients.append(ui_c.Ui_client(i)) + + self.create_model() + + + + + + + + + +if __name__ == "__main__": + + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + + sys.exit(app.exec_()) diff --git a/uaxplorer/ui_methods/Ui_client.py b/uaxplorer/ui_methods/Ui_client.py index 894fc48..cd161f2 100644 --- a/uaxplorer/ui_methods/Ui_client.py +++ b/uaxplorer/ui_methods/Ui_client.py @@ -1,16 +1,15 @@ from opcua import Client, ua -from server_discovery import * +import server_discovery as disc class Ui_client(): def __init__(self, server): - self.client = Client + self.client = Client("opc.tcp://" + server) self.servers = server def establish_client_connection(self): - self.client = Client("opc.tcp://" + self.servers) try: self.client.connect() print("client is connected: " + self.servers) @@ -21,7 +20,7 @@ def establish_client_connection(self): def create_connection(): - url = Server_Discovery() + url = disc.Server_Discovery() url.get_servers() servers = url.get_all_as_address() print(servers) @@ -29,11 +28,5 @@ def create_connection(): for i in servers: established_servers.append(Ui_client(i)) - established_servers[0].establish_client_connection() - established_servers[1].establish_client_connection() - print(established_servers[0].client.get_root_node()) + return established_servers - - - -create_connection() \ No newline at end of file diff --git a/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34c893ed6ac4f5a919254a409a68ca62570a3b7f GIT binary patch literal 1153 zcmaJ=&2AGh5VpNPNmE)vD*n_SdI4#XXeA^r6+%ENI3NY2Du7mt%(6Cg%Vx8+9Ymw; zDSeRk$iwggzH;IfI5Fc*+rR-wI~k8>#@~E19|jA%?pEM^q0kAc_NoKSr z95j$j5J@sZ$~;R=J}#3<`mHL)N~&a%^N}?BW1*9a zDz>U4+vYqkbHjNH(fc3>U8U3BrPH}XM)7dIP;bNMYTg26x>1t@a>~vKCB}pL$$^gy zQ1X^MB<_sj**j=m!^HKb?9E(nX5+GynUP{=GYV7y>1;>jdTzF@b0w%$UK$mGLqruK zcFZ&ar^&8MZPJfLuJ`Ai;`3NwV;LMYf+md74()ltPgY=8>UqyOW{yQzV#-maE*x9E~t~@A{ov9so!49 zluV6W=*JC6uDc)z1wPy_1leV39c)o64&bd!Quj!d2PWHXuyQvWSbRYf#5>i1zCa7R<30wmg!R`uA_^^9oO$4y}VjZsUnBF*kogT$iS{(7B6jCR6 z+j8JD&LK>Vku|g-930;#@920yjh|Szz(H5b>`?ij^Ny$hwvSFB3lev3v@ zA6Mlx(rG?Dg(Z6Oba=3HdsyaqU8D(kP!>@g|2fS{B~?_XJeSq0Qs}6-;NkZ4)C8QT zMOtxw4L!3i)3g4?Shkm12?e#~z#OLsFqoDwejX&UZ1J_1tdPlP2<;#goDs2&(E=rPDX(p5`j2};@vJeADK|zqP z^KAFmr+jB`Z|Aw8sU_7e>u_o|(NvXX)h6R}kZFWTs7r6KE<5|?e=(l7IF~*T3~|~> zAHqXQFdm5O-ppwuZkz?hm_c@P<~9)N@tV3*Id4eA8t1-U`(*Amt|jp0Nzkx;qVU#% z*8&L81FRjj0aD{;sjk#!t6)R!wsd%`RD2q44Ie&wJlfkG^YN3>E+0J_K2ulGkWMbr z*jfeI68Q==HC_%xSU|7SfOhB>JNxcZF3Tjew&&PjrJ{G1O#=@tn>n#8CJ~!Cz{3SD z(aihA<;RB2u~HaR1afm1Wa0_hhYDIi{{pc~&VZi(N$#!MSe{>;bde|2lyS##E(dGI zODmac)6udL@-VKm%3@Yn>EyI&;!naw3y9I7&Jv3(C8ULvPG8UXgM*3HB8ubd`z atnh!Ju+8nkW%roie(T5&sAhj4z3#uEP+}1P literal 0 HcmV?d00001 diff --git a/uaxplorer/ui_methods/navigating_nodes.py b/uaxplorer/ui_methods/navigating_nodes.py index daa7706..1af79bd 100644 --- a/uaxplorer/ui_methods/navigating_nodes.py +++ b/uaxplorer/ui_methods/navigating_nodes.py @@ -20,11 +20,10 @@ def __init__(self, client): def get_root_nodes(self): TEMP_ARRAY = [] - for i in self.client.get_objects_node().get_children()[1:]: # Skipping first element as it is unnecessary, we grab all objects in a server TEMP_ARRAY.append(i) - + return TEMP_ARRAY def get_children_nodes(self, object_array): #Gets the children nodes from the root node and adds them into a dictonary From e310052358e095837ffefbb43f21cb6d43076c48 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 5 Feb 2021 20:44:53 +0100 Subject: [PATCH 29/77] Added in UI mapping --- uaxplorer/ui_methods/MainUI.py | 43 ++++++++++++++++++++---- uaxplorer/ui_methods/server_discovery.py | 2 +- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 0111b8f..fdbdb59 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -59,6 +59,7 @@ def setupUi(self, MainWindow): self.treeView = QtWidgets.QTreeView(self.centralwidget) self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) self.treeView.setHeaderHidden(True) + self.treeView.setStyleSheet("color: rgb(200, 200, 200);") self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) @@ -81,10 +82,14 @@ def setupUi(self, MainWindow): MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) + + ############################### Discovery ################################ + treeModel = Qt.QStandardItemModel() + self.treeView.setHeaderHidden(True) self.rootNode = treeModel.invisibleRootItem() - test = Qt.QStandardItem('Testing') - self.rootNode.appendRow(test) + self.SERVER_ARR = [] + self.STANDARDITEM_ARR = [] self.treeView.setModel(treeModel) def retranslateUi(self, MainWindow): @@ -109,23 +114,49 @@ def create_model(self): i.client.connect() for i in temp_node_arr: - name_dict = i.get_name_from_nodes( + name_dict.update(i.get_name_from_nodes( i.get_children_nodes( - i.get_root_nodes())) + i.get_root_nodes()))) + + z = 0 + for key in name_dict.keys(): + temp = Qt.QStandardItem(key) + + l = name_dict[key] + for i in l: + temp.appendRow(Qt.QStandardItem(i)) + self.STANDARDITEM_ARR[z].appendRow(temp) + z += 1 + + + # for value in name_dict.values(): + # i.appendRow(Qt.QStandardItem(value)) - - print(temp_node_arr[0].get_name_from_nodes(temp_node_arr[0].get_children_nodes(temp_node_arr[0].get_root_nodes()))) + # Server1 Server2 + #["Parameters": ["temp", "ekrker", "efwer"], "Parameters2": ["qwkrk", "fkqk"]] + + def discover_servers(self): url = dsc.Server_Discovery() url.get_servers() + self.SERVER_ARR = url.get_all(0) servers = url.get_all_as_address() for i in servers: self.clients.append(ui_c.Ui_client(i)) + for i in self.SERVER_ARR: + self.STANDARDITEM_ARR.append(Qt.QStandardItem(i)) + + for j in range(len(self.SERVER_ARR)): + self.STANDARDITEM_ARR[j].setEditable(False) + self.rootNode.appendRow(self.STANDARDITEM_ARR[j]) + + + self.create_model() diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index ded2cf7..d7bd4e8 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -41,4 +41,4 @@ def get_all_as_address(self): for s in self.DISCOVERY_OUTPUT: arr.append(self.combine(s)) - return arr + return arr \ No newline at end of file From 18dd1fab7417d029c54092b2ca196eec4f7e1c63 Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Fri, 5 Feb 2021 21:02:15 +0100 Subject: [PATCH 30/77] Created a "right hand tree" for a 2nd access --- uaxplorer/ui_methods/MainUI.py | 334 +++++++++++++++------------------ 1 file changed, 156 insertions(+), 178 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index fdbdb59..17f87b8 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -1,178 +1,156 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'zuo.ui' -# -# Created by: PyQt5 UI code generator 5.15.2 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - - -from PyQt5 import QtCore, QtGui, QtWidgets, Qt -from zeroconf import ServiceBrowser, Zeroconf -import server_discovery as dsc -import Ui_client as ui_c -import navigating_nodes as nav -#from Ui_client import * -import sys - - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - self.clients = list() - MainWindow.setObjectName("MainWindow") - MainWindow.resize(912, 490) - MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") - self.centralwidget = QtWidgets.QWidget(MainWindow) - self.centralwidget.setObjectName("centralwidget") - self.Connect = QtWidgets.QPushButton(self.centralwidget) - self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) - self.Connect.setStyleSheet("color: rgb(200, 200, 200);") - self.Connect.setObjectName("Connect") - self.Discover = QtWidgets.QPushButton(self.centralwidget) - self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) - self.Discover.setStyleSheet("color: rgb(200, 200, 200);") - self.Discover.clicked.connect(self.discover_servers) - self.Discover.setObjectName("Discover") - self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) - self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) - self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") - self.lineEdit.setObjectName("lineEdit") - self.groupBox = QtWidgets.QGroupBox(self.centralwidget) - self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) - self.groupBox.setFlat(False) - self.groupBox.setCheckable(False) - self.groupBox.setObjectName("groupBox") - self.groupBox.setStyleSheet("color: rgb(200,200,200);") - self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) - self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) - self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) - self.horizontalScrollBar.setObjectName("horizontalScrollBar") - self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) - self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) - self.textBrowser.setObjectName("textBrowser") - self.pushButton = QtWidgets.QPushButton(self.centralwidget) - self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) - self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") - self.pushButton.setObjectName("pushButton") - self.treeView = QtWidgets.QTreeView(self.centralwidget) - self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) - self.treeView.setHeaderHidden(True) - self.treeView.setStyleSheet("color: rgb(200, 200, 200);") - self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) - self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) - self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) - self.verticalScrollBar.setObjectName("verticalScrollBar") - self.Connect.raise_() - self.Discover.raise_() - self.lineEdit.raise_() - self.textBrowser.raise_() - self.pushButton.raise_() - self.treeView.raise_() - self.groupBox.raise_() - self.verticalScrollBar.raise_() - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) - self.menubar.setObjectName("menubar") - MainWindow.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(MainWindow) - self.statusbar.setObjectName("statusbar") - MainWindow.setStatusBar(self.statusbar) - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - ############################### Discovery ################################ - - treeModel = Qt.QStandardItemModel() - self.treeView.setHeaderHidden(True) - self.rootNode = treeModel.invisibleRootItem() - self.SERVER_ARR = [] - self.STANDARDITEM_ARR = [] - self.treeView.setModel(treeModel) - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) - self.Connect.setText(_translate("MainWindow", "Connect")) - self.Discover.setText(_translate("MainWindow", "Discover")) - self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) - self.textBrowser.setHtml(_translate("MainWindow", "\n" -"\n" -"


")) - self.pushButton.setText(_translate("MainWindow", "Disconnect")) - - def create_model(self): - temp_node_arr = list() - name_dict = {} - for i in self.clients: - - temp_node_arr.append(nav.Navigating_nodes(i.client)) - i.client.connect() - - for i in temp_node_arr: - name_dict.update(i.get_name_from_nodes( - i.get_children_nodes( - i.get_root_nodes()))) - - - - z = 0 - for key in name_dict.keys(): - temp = Qt.QStandardItem(key) - - l = name_dict[key] - for i in l: - temp.appendRow(Qt.QStandardItem(i)) - self.STANDARDITEM_ARR[z].appendRow(temp) - z += 1 - - - # for value in name_dict.values(): - # i.appendRow(Qt.QStandardItem(value)) - - - # Server1 Server2 - #["Parameters": ["temp", "ekrker", "efwer"], "Parameters2": ["qwkrk", "fkqk"]] - - - - def discover_servers(self): - url = dsc.Server_Discovery() - url.get_servers() - self.SERVER_ARR = url.get_all(0) - servers = url.get_all_as_address() - for i in servers: - self.clients.append(ui_c.Ui_client(i)) - - for i in self.SERVER_ARR: - self.STANDARDITEM_ARR.append(Qt.QStandardItem(i)) - - for j in range(len(self.SERVER_ARR)): - self.STANDARDITEM_ARR[j].setEditable(False) - self.rootNode.appendRow(self.STANDARDITEM_ARR[j]) - - - - self.create_model() - - - - - - - - - -if __name__ == "__main__": - - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - - sys.exit(app.exec_()) +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'zuo.ui' +# +# Created by: PyQt5 UI code generator 5.15.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5.QtWidgets import QDialog, QApplication, QTreeWidgetItem +from PyQt5.Qt import QStandardItemModel, QStandardItem +from zeroconf import ServiceBrowser, Zeroconf +from ui_methods.server_discovery import Server_Discovery +from ui_methods.discovery import * + +import sys + + +class Ui_MainWindow(object): + + def setupUi(self, MainWindow): + + MainWindow.setObjectName("MainWindow") + MainWindow.resize(914, 513) + MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.Connect = QtWidgets.QPushButton(self.centralwidget) + self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) + self.Connect.setStyleSheet("color: rgb(200, 200, 200);") + self.Connect.setObjectName("Connect") + self.Discover = QtWidgets.QPushButton(self.centralwidget) + self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) + self.Discover.setStyleSheet("color: rgb(200, 200, 200);") + self.Discover.clicked.connect(self.server_disc) + self.Discover.setObjectName("Discover") + self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) + self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) + self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") + self.lineEdit.setObjectName("lineEdit") + self.groupBox = QtWidgets.QGroupBox(self.centralwidget) + self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) + self.groupBox.setFlat(False) + self.groupBox.setCheckable(False) + self.groupBox.setObjectName("groupBox") + self.groupBox.setStyleSheet("color: rgb(200,200,200);") + self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) + self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 19)) + self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) + self.horizontalScrollBar.setObjectName("horizontalScrollBar") + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) + self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) + self.textBrowser.setObjectName("textBrowser") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) + self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) + self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") + self.pushButton.setObjectName("pushButton") + self.treeView = QtWidgets.QTreeView(self.centralwidget) + self.treeView.setGeometry(QtCore.QRect(10, 26, 331, 325)) + self.treeView.setObjectName("treeView") + self.right_treeView = QtWidgets.QTreeView(self.centralwidget) + self.right_treeView.setGeometry(QtCore.QRect(910, 26, 331, 325)) + self.right_treeView.setObjectName("Right_treeview") + self.right_treeView.hide() + self.right_verticalscrollbar = QtWidgets.QScrollBar(self.centralwidget) + self.right_verticalscrollbar.setOrientation(QtCore.Qt.Vertical) + self.right_verticalscrollbar.setGeometry(QtCore.QRect(1220, 28, 20, 320)) + self.right_verticalscrollbar.hide() + self.right_verticalscrollbar.setObjectName("VerticalScrollbar_for_righttreeview") + self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) + self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 20, 320)) + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.Connect.raise_() + self.Discover.raise_() + self.lineEdit.raise_() + self.textBrowser.raise_() + self.pushButton.raise_() + self.treeView.raise_() + self.groupBox.raise_() + self.verticalScrollBar.raise_() + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 914, 21)) + self.menubar.setObjectName("menubar") + self.menuFile = QtWidgets.QMenu(self.menubar) + self.menuFile.setObjectName("menuFile") + self.menuView = QtWidgets.QMenu(self.menubar) + self.menuView.setObjectName("menuView") + MainWindow.setMenuBar(self.menubar) + self.actionRight_Hand_tree = QtWidgets.QAction(MainWindow) + self.actionRight_Hand_tree.setObjectName("actionRight_Hand_tree") + self.actionRight_Hand_tree.triggered.connect(self.creating_right_window) + self.menuView.addAction(self.actionRight_Hand_tree) + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuView.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) + self.Connect.setText(_translate("MainWindow", "Connect")) + self.Discover.setText(_translate("MainWindow", "Discover")) + self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) + self.textBrowser.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.pushButton.setText(_translate("MainWindow", "Disconnect")) + self.menuFile.setTitle(_translate("MainWindow", "File")) + self.menuView.setTitle(_translate("MainWindow", "View")) + self.actionRight_Hand_tree.setText(_translate("MainWindow", "Expand a right hand tree")) + + + + + def server_disc(self): + servers = Server_Discovery() + servers_arr = servers.get_servers() + + def creating_right_window(self): + + MainWindow.resize(1245, 513) # resizing the window to be able to fit the new treeview + self.textBrowser.resize(1241, 111) # making the textbox bigger to be able to display more information + self.right_treeView.show() + self.right_verticalscrollbar.show() + + + + + + + + +if __name__ == "__main__": + + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + + sys.exit(app.exec_()) From 60bfafa29ca4ee794055ec5f19f5e894810480b7 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 5 Feb 2021 21:09:34 +0100 Subject: [PATCH 31/77] Revert "Created a "right hand tree" for a 2nd access" This reverts commit 18dd1fab7417d029c54092b2ca196eec4f7e1c63. --- uaxplorer/ui_methods/MainUI.py | 334 ++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 156 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 17f87b8..fdbdb59 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -1,156 +1,178 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'zuo.ui' -# -# Created by: PyQt5 UI code generator 5.15.2 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets -from PyQt5.QtWidgets import QDialog, QApplication, QTreeWidgetItem -from PyQt5.Qt import QStandardItemModel, QStandardItem -from zeroconf import ServiceBrowser, Zeroconf -from ui_methods.server_discovery import Server_Discovery -from ui_methods.discovery import * - -import sys - - -class Ui_MainWindow(object): - - def setupUi(self, MainWindow): - - MainWindow.setObjectName("MainWindow") - MainWindow.resize(914, 513) - MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") - self.centralwidget = QtWidgets.QWidget(MainWindow) - self.centralwidget.setObjectName("centralwidget") - self.Connect = QtWidgets.QPushButton(self.centralwidget) - self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) - self.Connect.setStyleSheet("color: rgb(200, 200, 200);") - self.Connect.setObjectName("Connect") - self.Discover = QtWidgets.QPushButton(self.centralwidget) - self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) - self.Discover.setStyleSheet("color: rgb(200, 200, 200);") - self.Discover.clicked.connect(self.server_disc) - self.Discover.setObjectName("Discover") - self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) - self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) - self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") - self.lineEdit.setObjectName("lineEdit") - self.groupBox = QtWidgets.QGroupBox(self.centralwidget) - self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) - self.groupBox.setFlat(False) - self.groupBox.setCheckable(False) - self.groupBox.setObjectName("groupBox") - self.groupBox.setStyleSheet("color: rgb(200,200,200);") - self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) - self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 19)) - self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) - self.horizontalScrollBar.setObjectName("horizontalScrollBar") - self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) - self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) - self.textBrowser.setObjectName("textBrowser") - self.pushButton = QtWidgets.QPushButton(self.centralwidget) - self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) - self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") - self.pushButton.setObjectName("pushButton") - self.treeView = QtWidgets.QTreeView(self.centralwidget) - self.treeView.setGeometry(QtCore.QRect(10, 26, 331, 325)) - self.treeView.setObjectName("treeView") - self.right_treeView = QtWidgets.QTreeView(self.centralwidget) - self.right_treeView.setGeometry(QtCore.QRect(910, 26, 331, 325)) - self.right_treeView.setObjectName("Right_treeview") - self.right_treeView.hide() - self.right_verticalscrollbar = QtWidgets.QScrollBar(self.centralwidget) - self.right_verticalscrollbar.setOrientation(QtCore.Qt.Vertical) - self.right_verticalscrollbar.setGeometry(QtCore.QRect(1220, 28, 20, 320)) - self.right_verticalscrollbar.hide() - self.right_verticalscrollbar.setObjectName("VerticalScrollbar_for_righttreeview") - self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) - self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 20, 320)) - self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) - self.verticalScrollBar.setObjectName("verticalScrollBar") - self.Connect.raise_() - self.Discover.raise_() - self.lineEdit.raise_() - self.textBrowser.raise_() - self.pushButton.raise_() - self.treeView.raise_() - self.groupBox.raise_() - self.verticalScrollBar.raise_() - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) - self.menubar.setObjectName("menubar") - MainWindow.setMenuBar(self.menubar) - self.statusbar = QtWidgets.QStatusBar(MainWindow) - self.statusbar.setObjectName("statusbar") - MainWindow.setStatusBar(self.statusbar) - self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 914, 21)) - self.menubar.setObjectName("menubar") - self.menuFile = QtWidgets.QMenu(self.menubar) - self.menuFile.setObjectName("menuFile") - self.menuView = QtWidgets.QMenu(self.menubar) - self.menuView.setObjectName("menuView") - MainWindow.setMenuBar(self.menubar) - self.actionRight_Hand_tree = QtWidgets.QAction(MainWindow) - self.actionRight_Hand_tree.setObjectName("actionRight_Hand_tree") - self.actionRight_Hand_tree.triggered.connect(self.creating_right_window) - self.menuView.addAction(self.actionRight_Hand_tree) - self.menubar.addAction(self.menuFile.menuAction()) - self.menubar.addAction(self.menuView.menuAction()) - - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - - def retranslateUi(self, MainWindow): - _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) - self.Connect.setText(_translate("MainWindow", "Connect")) - self.Discover.setText(_translate("MainWindow", "Discover")) - self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) - self.textBrowser.setHtml(_translate("MainWindow", "\n" -"\n" -"


")) - self.pushButton.setText(_translate("MainWindow", "Disconnect")) - self.menuFile.setTitle(_translate("MainWindow", "File")) - self.menuView.setTitle(_translate("MainWindow", "View")) - self.actionRight_Hand_tree.setText(_translate("MainWindow", "Expand a right hand tree")) - - - - - def server_disc(self): - servers = Server_Discovery() - servers_arr = servers.get_servers() - - def creating_right_window(self): - - MainWindow.resize(1245, 513) # resizing the window to be able to fit the new treeview - self.textBrowser.resize(1241, 111) # making the textbox bigger to be able to display more information - self.right_treeView.show() - self.right_verticalscrollbar.show() - - - - - - - - -if __name__ == "__main__": - - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - - sys.exit(app.exec_()) +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'zuo.ui' +# +# Created by: PyQt5 UI code generator 5.15.2 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + + +from PyQt5 import QtCore, QtGui, QtWidgets, Qt +from zeroconf import ServiceBrowser, Zeroconf +import server_discovery as dsc +import Ui_client as ui_c +import navigating_nodes as nav +#from Ui_client import * +import sys + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + self.clients = list() + MainWindow.setObjectName("MainWindow") + MainWindow.resize(912, 490) + MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.Connect = QtWidgets.QPushButton(self.centralwidget) + self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) + self.Connect.setStyleSheet("color: rgb(200, 200, 200);") + self.Connect.setObjectName("Connect") + self.Discover = QtWidgets.QPushButton(self.centralwidget) + self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) + self.Discover.setStyleSheet("color: rgb(200, 200, 200);") + self.Discover.clicked.connect(self.discover_servers) + self.Discover.setObjectName("Discover") + self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) + self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) + self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") + self.lineEdit.setObjectName("lineEdit") + self.groupBox = QtWidgets.QGroupBox(self.centralwidget) + self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) + self.groupBox.setFlat(False) + self.groupBox.setCheckable(False) + self.groupBox.setObjectName("groupBox") + self.groupBox.setStyleSheet("color: rgb(200,200,200);") + self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) + self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) + self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) + self.horizontalScrollBar.setObjectName("horizontalScrollBar") + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) + self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) + self.textBrowser.setObjectName("textBrowser") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) + self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) + self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") + self.pushButton.setObjectName("pushButton") + self.treeView = QtWidgets.QTreeView(self.centralwidget) + self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) + self.treeView.setHeaderHidden(True) + self.treeView.setStyleSheet("color: rgb(200, 200, 200);") + self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) + self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) + self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) + self.verticalScrollBar.setObjectName("verticalScrollBar") + self.Connect.raise_() + self.Discover.raise_() + self.lineEdit.raise_() + self.textBrowser.raise_() + self.pushButton.raise_() + self.treeView.raise_() + self.groupBox.raise_() + self.verticalScrollBar.raise_() + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) + self.menubar.setObjectName("menubar") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + ############################### Discovery ################################ + + treeModel = Qt.QStandardItemModel() + self.treeView.setHeaderHidden(True) + self.rootNode = treeModel.invisibleRootItem() + self.SERVER_ARR = [] + self.STANDARDITEM_ARR = [] + self.treeView.setModel(treeModel) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) + self.Connect.setText(_translate("MainWindow", "Connect")) + self.Discover.setText(_translate("MainWindow", "Discover")) + self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) + self.textBrowser.setHtml(_translate("MainWindow", "\n" +"\n" +"


")) + self.pushButton.setText(_translate("MainWindow", "Disconnect")) + + def create_model(self): + temp_node_arr = list() + name_dict = {} + for i in self.clients: + + temp_node_arr.append(nav.Navigating_nodes(i.client)) + i.client.connect() + + for i in temp_node_arr: + name_dict.update(i.get_name_from_nodes( + i.get_children_nodes( + i.get_root_nodes()))) + + + + z = 0 + for key in name_dict.keys(): + temp = Qt.QStandardItem(key) + + l = name_dict[key] + for i in l: + temp.appendRow(Qt.QStandardItem(i)) + self.STANDARDITEM_ARR[z].appendRow(temp) + z += 1 + + + # for value in name_dict.values(): + # i.appendRow(Qt.QStandardItem(value)) + + + # Server1 Server2 + #["Parameters": ["temp", "ekrker", "efwer"], "Parameters2": ["qwkrk", "fkqk"]] + + + + def discover_servers(self): + url = dsc.Server_Discovery() + url.get_servers() + self.SERVER_ARR = url.get_all(0) + servers = url.get_all_as_address() + for i in servers: + self.clients.append(ui_c.Ui_client(i)) + + for i in self.SERVER_ARR: + self.STANDARDITEM_ARR.append(Qt.QStandardItem(i)) + + for j in range(len(self.SERVER_ARR)): + self.STANDARDITEM_ARR[j].setEditable(False) + self.rootNode.appendRow(self.STANDARDITEM_ARR[j]) + + + + self.create_model() + + + + + + + + + +if __name__ == "__main__": + + app = QtWidgets.QApplication(sys.argv) + MainWindow = QtWidgets.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + + sys.exit(app.exec_()) From f362309cc6e26965105920a36520efe66d6e3ef3 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sat, 6 Feb 2021 19:46:51 +0100 Subject: [PATCH 32/77] Added Artins treeView from rollback --- uaxplorer/ui_methods/MainUI.py | 48 ++++++++++++++++++++++++---- uaxplorer/ui_methods/client_nodes.py | 9 ++++++ 2 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 uaxplorer/ui_methods/client_nodes.py diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index fdbdb59..745655e 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -22,7 +22,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): self.clients = list() MainWindow.setObjectName("MainWindow") - MainWindow.resize(912, 490) + MainWindow.resize(914, 513) MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") @@ -46,7 +46,7 @@ def setupUi(self, MainWindow): self.groupBox.setObjectName("groupBox") self.groupBox.setStyleSheet("color: rgb(200,200,200);") self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) - self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 16)) + self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 19)) self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) self.horizontalScrollBar.setObjectName("horizontalScrollBar") self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) @@ -57,11 +57,19 @@ def setupUi(self, MainWindow): self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") self.pushButton.setObjectName("pushButton") self.treeView = QtWidgets.QTreeView(self.centralwidget) - self.treeView.setGeometry(QtCore.QRect(10, 30, 331, 321)) - self.treeView.setHeaderHidden(True) - self.treeView.setStyleSheet("color: rgb(200, 200, 200);") + self.treeView.setGeometry(QtCore.QRect(10, 26, 331, 325)) + self.treeView.setObjectName("treeView") + self.right_treeView = QtWidgets.QTreeView(self.centralwidget) + self.right_treeView.setGeometry(QtCore.QRect(910, 26, 331, 325)) + self.right_treeView.setObjectName("Right_treeview") + self.right_treeView.hide() + self.right_verticalscrollbar = QtWidgets.QScrollBar(self.centralwidget) + self.right_verticalscrollbar.setOrientation(QtCore.Qt.Vertical) + self.right_verticalscrollbar.setGeometry(QtCore.QRect(1220, 28, 20, 320)) + self.right_verticalscrollbar.hide() + self.right_verticalscrollbar.setObjectName("VerticalScrollbar_for_righttreeview") self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) - self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 16, 321)) + self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 20, 320)) self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) self.verticalScrollBar.setObjectName("verticalScrollBar") self.Connect.raise_() @@ -70,7 +78,7 @@ def setupUi(self, MainWindow): self.textBrowser.raise_() self.pushButton.raise_() self.treeView.raise_() - self.groupBox.raise_() + self.groupBox.raise_() self.verticalScrollBar.raise_() MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) @@ -80,6 +88,21 @@ def setupUi(self, MainWindow): self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 914, 21)) + self.menubar.setObjectName("menubar") + self.menuFile = QtWidgets.QMenu(self.menubar) + self.menuFile.setObjectName("menuFile") + self.menuView = QtWidgets.QMenu(self.menubar) + self.menuView.setObjectName("menuView") + MainWindow.setMenuBar(self.menubar) + self.actionRight_Hand_tree = QtWidgets.QAction(MainWindow) + self.actionRight_Hand_tree.setObjectName("actionRight_Hand_tree") + self.actionRight_Hand_tree.triggered.connect(self.creating_right_window) + self.menuView.addAction(self.actionRight_Hand_tree) + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuView.menuAction()) + self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) @@ -104,6 +127,17 @@ def retranslateUi(self, MainWindow): "\n" "


")) self.pushButton.setText(_translate("MainWindow", "Disconnect")) + self.menuFile.setTitle(_translate("MainWindow", "File")) + self.menuView.setTitle(_translate("MainWindow", "View")) + self.actionRight_Hand_tree.setText(_translate("MainWindow", "Expand a right hand tree")) + + + + def creating_right_window(self): + MainWindow.resize(1245, 513) # resizing the window to be able to fit the new treeview + self.textBrowser.resize(1241, 111) # making the textbox bigger to be able to display more information + self.right_treeView.show() + self.right_verticalscrollbar.show() def create_model(self): temp_node_arr = list() diff --git a/uaxplorer/ui_methods/client_nodes.py b/uaxplorer/ui_methods/client_nodes.py new file mode 100644 index 0000000..fc61a4c --- /dev/null +++ b/uaxplorer/ui_methods/client_nodes.py @@ -0,0 +1,9 @@ + + + + + + +class Client_nodes: + def __init__(self): + \ No newline at end of file From 609adc3668d7821ec679108bfe6e78e4b5b72847 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sun, 7 Feb 2021 18:07:55 +0100 Subject: [PATCH 33/77] New class for handling QStandardItems (Rows, columns) cleaner code in MainUI for handling the StandardItems and a class for StandardItem --- uaxplorer/ui_methods/MainUI.py | 70 +++++------------- uaxplorer/ui_methods/Ui_client.py | 10 --- .../__pycache__/Ui_client.cpython-38.pyc | Bin 1153 -> 891 bytes .../__pycache__/client_nodes.cpython-38.pyc | Bin 0 -> 1778 bytes uaxplorer/ui_methods/client_nodes.py | 40 +++++++++- 5 files changed, 57 insertions(+), 63 deletions(-) create mode 100644 uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 745655e..bfc96c9 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -13,6 +13,7 @@ from zeroconf import ServiceBrowser, Zeroconf import server_discovery as dsc import Ui_client as ui_c +import client_nodes as cl_node import navigating_nodes as nav #from Ui_client import * import sys @@ -108,12 +109,11 @@ def setupUi(self, MainWindow): ############################### Discovery ################################ - treeModel = Qt.QStandardItemModel() + self.treeModel = Qt.QStandardItemModel() self.treeView.setHeaderHidden(True) - self.rootNode = treeModel.invisibleRootItem() - self.SERVER_ARR = [] - self.STANDARDITEM_ARR = [] - self.treeView.setModel(treeModel) + self.rootNode = self.treeModel.invisibleRootItem() + self.treeView.setModel(self.treeModel) + def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate @@ -139,60 +139,30 @@ def creating_right_window(self): self.right_treeView.show() self.right_verticalscrollbar.show() - def create_model(self): - temp_node_arr = list() - name_dict = {} - for i in self.clients: - - temp_node_arr.append(nav.Navigating_nodes(i.client)) - i.client.connect() - - for i in temp_node_arr: - name_dict.update(i.get_name_from_nodes( - i.get_children_nodes( - i.get_root_nodes()))) - - - - z = 0 - for key in name_dict.keys(): - temp = Qt.QStandardItem(key) - - l = name_dict[key] - for i in l: - temp.appendRow(Qt.QStandardItem(i)) - self.STANDARDITEM_ARR[z].appendRow(temp) - z += 1 - - - # for value in name_dict.values(): - # i.appendRow(Qt.QStandardItem(value)) - - - # Server1 Server2 - #["Parameters": ["temp", "ekrker", "efwer"], "Parameters2": ["qwkrk", "fkqk"]] - - def discover_servers(self): + if len(self.clients) > 0: + self.clients.clear() + + self.treeModel.removeRows(0, self.treeModel.rowCount()) + url = dsc.Server_Discovery() url.get_servers() self.SERVER_ARR = url.get_all(0) servers = url.get_all_as_address() + print(servers) + j = 0 for i in servers: - self.clients.append(ui_c.Ui_client(i)) + self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) + j += 1 - for i in self.SERVER_ARR: - self.STANDARDITEM_ARR.append(Qt.QStandardItem(i)) - - for j in range(len(self.SERVER_ARR)): - self.STANDARDITEM_ARR[j].setEditable(False) - self.rootNode.appendRow(self.STANDARDITEM_ARR[j]) - - - - self.create_model() + for i in self.clients: + self.rootNode.appendRow(i.ROOT_NODE) + i.ROOT_NODE.appendRow(i.FOLDER_NODE) + for j in i.MAP_VALUE_NODES.values(): + for d in j: + i.FOLDER_NODE[0].appendRow(d) diff --git a/uaxplorer/ui_methods/Ui_client.py b/uaxplorer/ui_methods/Ui_client.py index cd161f2..66cac60 100644 --- a/uaxplorer/ui_methods/Ui_client.py +++ b/uaxplorer/ui_methods/Ui_client.py @@ -8,16 +8,6 @@ def __init__(self, server): self.client = Client("opc.tcp://" + server) self.servers = server - def establish_client_connection(self): - - try: - self.client.connect() - print("client is connected: " + self.servers) - - finally: - self.client.disconnect() - - def create_connection(): url = disc.Server_Discovery() diff --git a/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc index 34c893ed6ac4f5a919254a409a68ca62570a3b7f..6ce192968fdc359c4b4dff6c5a52c156e2a3f621 100644 GIT binary patch delta 124 zcmZqV{LRJ}%FD~e00e8V$|rnfoXFS2C^B)PC`&MdCduFWJc!2?6+78iZb&`Ca+|Ul;!~n7Yl&QVdQ{9HXtp)R3tH3mPM1BA1G7A O0wN?P2eRlhvH<{4-x)dp delta 390 zcmey(*2u{h%FD~e00e3&k_l&-Ch|2g%1oRn%9FyIwh=-UT4>ct-KaU%zpcv$81|}v(E=D2dD%H?T zs4_h~`usFmZ*fds$Ecyj57difBiL?`wM8sI0_3V<5QBq}gJtqh#>K2fd_dmh#Y~Zm eVw3+eNis@L7H8Jv76uA}H55rt_GZ>+6aWBhwpnBV diff --git a/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..314bd065846998132ffc3fac10b16ffbf842257e GIT binary patch literal 1778 zcmZux&2LmU5Vzm=+w6uAXn|BkIic>MfeROe5HueMm2R>m0c36EKAxTM;C*cTyb+0B zq)Lm#sZ#%d9(IrX1N~Ea@U^GhdhUhJythGHwYEH-8ISGp%x}Ek>9h#6Co8KzeR2u; z6NUNafbbAT`4WgAf~F*+jAjMH4yTi@W4yjzFO)QHUq2otChY4Mi8z$HG*(}CE#s}H ziOjXJMp2iVrCxNL97a0H4|!e)S;lzGd<$kmc?TFt7!`zBf(lF6(w;cr8uY1s;Cvtl zRJg)>A2Rm?aR~FkE#I_vb(D)pi6>fShMZ%tw$48RjUif}n+F_5%7#%s1)|BAOz4zQ zQjrNSri6^Yenp$UW?=%Y!K*h`CRI&gp`71Pa<035-vk!9pk(!Y6IE~7Z^N-~3=kdM4ekoNs z*!uqYU_+K~buk>2Nj5wIGT7c(-+%P&dXZ(LJc%J+MLrls?}l)kRD)5%GpSz}q8!9^ zcAH$T4Nue}xQwwaks-9zbgK>X=@;{I*Q~=R{{m8372%|!K&b{GRrDGeqhxERB5$DZgk6JZhGgOX0($Nz z06ZQXA`KIAX6qJM+hcm>oPLAcq+Ns4o^&c_vIyf6jHM}=0xS`DFUVck3w8@=_mWih zv-t*@TmdhA;f*ak^XRvuU(o_~ zUIn<5H(|Z*j_Vtb{;0w3_fA{IFkaK~@ZRm)=WU3=-ucf!yHs6)Ni86M)d1wxu~c=c zjkjA13bA6m21Rvx>O`xrP#r$o-rnQA?Tt-y>4!%<{Q0A&`8!s9{VT*2{F4w*{uTrZZumGLq;42HkrP*%#tvoT_ag>h0LARdgz*^%J zi)9%`c?$@k9`r@)CeD@vm1DuUX}7H<8UWX(D;8GMJS0C?Q?Ki&>u`p;fn)^<9zJ(r zT_^ReU&8{v;i5WF?sXT9yK~alcNNw}lMVbR71oh)A|k@*N3jKAQZlX hb^7xK-((l>_&;Y%aK9=12NWzyJmflEqHSxr{SWUVygmQ` literal 0 HcmV?d00001 diff --git a/uaxplorer/ui_methods/client_nodes.py b/uaxplorer/ui_methods/client_nodes.py index fc61a4c..0f31c05 100644 --- a/uaxplorer/ui_methods/client_nodes.py +++ b/uaxplorer/ui_methods/client_nodes.py @@ -1,9 +1,43 @@ +from PyQt5 import QtCore, QtGui, QtWidgets, Qt +from opcua import Client, ua +from navigating_nodes import Navigating_nodes as navNodes +class StandardItem(Qt.QStandardItem): + def __init__(self, txt='', font_size=10, set_bold=False, color=QtGui.QColor(255, 255, 255)): + super().__init__() + self.setEditable(False) + self.setForeground(color) + self.setText(txt) + fnt = QtGui.QFont('Open Sans', font_size) + fnt.setBold(set_bold) + self.setFont(fnt) +class Client_nodes: + def __init__(self, server, server_name): + self.server_name = server_name + self.Server = server + self.client = Client("opc.tcp://" + server) + self.ROOT_NODE = StandardItem(self.server_name, 12, True, color=QtGui.QColor(128, 128, 128)) + self.MAP_VALUE_NODES = {} + self.FOLDER_NODE = [] -class Client_nodes: - def __init__(self): - \ No newline at end of file + NODE_MAP = {} + nav_nodes = navNodes(self.client) + try: + self.client.connect() + NODE_MAP.update(nav_nodes.get_name_from_nodes(nav_nodes.get_children_nodes(nav_nodes.get_root_nodes()))) + + finally: + self.client.disconnect() + + for key, values in NODE_MAP.items(): + for value in values: + if key not in self.MAP_VALUE_NODES: + self.MAP_VALUE_NODES[key] = list() + self.MAP_VALUE_NODES[key].append(StandardItem(value, 10)) + + for key in self.MAP_VALUE_NODES: + self.FOLDER_NODE.append(StandardItem(key)) \ No newline at end of file From 7c7326fd892a2d4788feb45e1f3af14fc458d865 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sun, 7 Feb 2021 20:27:25 +0100 Subject: [PATCH 34/77] Pushed changes for handling multiple objects for displaying & mapping --- uaxplorer/ui_methods/MainUI.py | 14 ++++++-------- .../__pycache__/client_nodes.cpython-38.pyc | Bin 1778 -> 1821 bytes uaxplorer/ui_methods/client_nodes.py | 8 ++++---- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index bfc96c9..b5b0ef7 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -69,10 +69,6 @@ def setupUi(self, MainWindow): self.right_verticalscrollbar.setGeometry(QtCore.QRect(1220, 28, 20, 320)) self.right_verticalscrollbar.hide() self.right_verticalscrollbar.setObjectName("VerticalScrollbar_for_righttreeview") - self.verticalScrollBar = QtWidgets.QScrollBar(self.centralwidget) - self.verticalScrollBar.setGeometry(QtCore.QRect(320, 30, 20, 320)) - self.verticalScrollBar.setOrientation(QtCore.Qt.Vertical) - self.verticalScrollBar.setObjectName("verticalScrollBar") self.Connect.raise_() self.Discover.raise_() self.lineEdit.raise_() @@ -80,7 +76,6 @@ def setupUi(self, MainWindow): self.pushButton.raise_() self.treeView.raise_() self.groupBox.raise_() - self.verticalScrollBar.raise_() MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) @@ -143,7 +138,6 @@ def creating_right_window(self): def discover_servers(self): if len(self.clients) > 0: self.clients.clear() - self.treeModel.removeRows(0, self.treeModel.rowCount()) url = dsc.Server_Discovery() @@ -158,11 +152,15 @@ def discover_servers(self): for i in self.clients: self.rootNode.appendRow(i.ROOT_NODE) - i.ROOT_NODE.appendRow(i.FOLDER_NODE) + for j in i.FOLDER_NODE: + i.ROOT_NODE.appendRow(j) + t = 0 for j in i.MAP_VALUE_NODES.values(): for d in j: - i.FOLDER_NODE[0].appendRow(d) + i.FOLDER_NODE[t].appendRow(d) + t += 1 + diff --git a/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc index 314bd065846998132ffc3fac10b16ffbf842257e..5447db6ad18e2c19378cdb0aecb867a536d6ce63 100644 GIT binary patch delta 421 zcmYjNu}T9$5S_if-Mw7y&crAvf=CLzpqNS!2{vk_*r;fsDCC01fF_&6BJ2_Hz)qX1 zEHrnOwt|g+;1^i<2eI@EoK58p@6F8PyRx!$PbReXdwd57?(6ErzBNffWz0B@}?nWIeBDTi#|#~ hIF@G=bT>?569X}8IH*`SIm9y-EW?5T5SX)2-hmP zdjuOFz(=sK&?iVIzCfG}?(qH0d^-#K;{SU7iRU?hXRp?{oQhZfffWnbZX2N-5!$yP`=> zO;*lf2hO+7Xe?r>CHl44(gOGGYfHUF^9jD+v$fd#*G0%G5!Y5I6PgnOgXh7Mxq+M2 z*z6T%xk$(?{S4s<0_$+(-1OV4QM Date: Sun, 14 Feb 2021 14:26:01 +0100 Subject: [PATCH 35/77] Added first draft of sever class for a Rapsberry PI device. --- server/serverpi.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 server/serverpi.py diff --git a/server/serverpi.py b/server/serverpi.py new file mode 100644 index 0000000..70b2ec5 --- /dev/null +++ b/server/serverpi.py @@ -0,0 +1,51 @@ +#/usr/bin/env python3 + +# This file contains a set of classes and methods to handle +# running a OPC UA server on a raspberry pi. + + +import asyncio +import random +from asyncua import ua, uamethod, Server + +SERVER_NAME = "RaspPI OPC UA Server" +FLAT_NAME = SERVER_NAME.replace(" ", "") +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840/raspua/server" +UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME +TEMP = 19 + +class ServerPI: + + def __init__(self): + self.temp = TEMP + + async def go(self): + server = Server() + await server.init() + server.set_endpoint(SERVER_ENDPOINT) + server.set_server_name(SERVER_NAME) + server.set_security_policy([ + ua.SecurityPolicyType.NoSecurity, + ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, + ua.SecurityPolicyType.Basic256Sha256_Sign]) + + idx = await server.register_namespace(UA_NAMESPACE) + + dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) + + lFolder = await server.nodes.objects.add_folder(idx, "Sensors") + device = await server.nodes.objects.add_object(idx, "ZeDevice", dev) + + zobj = await server.nodes.objects.add_object(idx, "ZeObject") + zvar = await zobj.add_variable(idx, "Temperature", self.temp) + + async with server: + while True: + await asyncio.sleep(0.1) + await zvar.write_value(self.temp) + self.temp = 19 + random.random() + + +if __name__ == "__main__": + sp = ServerPI() + asyncio.run(sp.go()) From 50dd17e2b69f69f4dd0990d5e85184d5c389849f Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 14 Feb 2021 14:38:14 +0100 Subject: [PATCH 36/77] Added service announcement to PI server file. --- server/serverpi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/serverpi.py b/server/serverpi.py index 70b2ec5..01ce65f 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -7,6 +7,7 @@ import asyncio import random from asyncua import ua, uamethod, Server +import announce_service as sa SERVER_NAME = "RaspPI OPC UA Server" FLAT_NAME = SERVER_NAME.replace(" ", "") @@ -47,5 +48,6 @@ async def go(self): if __name__ == "__main__": + sa.start_service_announcement() sp = ServerPI() asyncio.run(sp.go()) From edcbcbc07eb41c3b4516560f18d91a4b08bb184a Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Sun, 14 Feb 2021 14:40:41 +0100 Subject: [PATCH 37/77] Added a first gitignore file. --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbba710 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +server/__pycache__ From c4d42e3328a3bb2d5588f75eb69d930a926b67e3 Mon Sep 17 00:00:00 2001 From: yazdii <73112257+yazdii@users.noreply.github.com> Date: Wed, 17 Feb 2021 22:41:05 +0100 Subject: [PATCH 38/77] Optimized the UI, Updated it creating new options --- uaxplorer/ui_methods/MainUI.py | 109 +++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 26 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index b5b0ef7..f27f51d 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -15,7 +15,6 @@ import Ui_client as ui_c import client_nodes as cl_node import navigating_nodes as nav -#from Ui_client import * import sys @@ -23,52 +22,63 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): self.clients = list() MainWindow.setObjectName("MainWindow") - MainWindow.resize(914, 513) - MainWindow.setStyleSheet("background-color: rgb(38, 38, 38);") + MainWindow.resize(1014, 513) + MainWindow.setStyleSheet("background-color: rgb(200, 200, 200;") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") + self.Connect = QtWidgets.QPushButton(self.centralwidget) - self.Connect.setGeometry(QtCore.QRect(780, 0, 51, 21)) - self.Connect.setStyleSheet("color: rgb(200, 200, 200);") + self.Connect.setGeometry(QtCore.QRect(780, 0, 81, 21)) + self.Connect.setStyleSheet("color: rgb(0, 0, 0);") + self.Connect.clicked.connect(self.manual_connection) self.Connect.setObjectName("Connect") + self.Discover = QtWidgets.QPushButton(self.centralwidget) self.Discover.setGeometry(QtCore.QRect(690, 0, 81, 21)) - self.Discover.setStyleSheet("color: rgb(200, 200, 200);") + self.Discover.setStyleSheet("color: rgb(0, 0, 0);") self.Discover.clicked.connect(self.discover_servers) self.Discover.setObjectName("Discover") + self.lineEdit = QtWidgets.QLineEdit(self.centralwidget) - self.lineEdit.setGeometry(QtCore.QRect(0, 0, 681, 21)) - self.lineEdit.setStyleSheet("color: rgb(200, 200, 200);") + self.lineEdit.setGeometry(QtCore.QRect(10, 0, 681, 21)) + self.lineEdit.setStyleSheet("color: rgb(0, 0, 0);") self.lineEdit.setObjectName("lineEdit") + self.groupBox = QtWidgets.QGroupBox(self.centralwidget) - self.groupBox.setGeometry(QtCore.QRect(340, 20, 571, 331)) + self.groupBox.setGeometry(QtCore.QRect(340, 20, 611, 331)) self.groupBox.setFlat(False) self.groupBox.setCheckable(False) self.groupBox.setObjectName("groupBox") - self.groupBox.setStyleSheet("color: rgb(200,200,200);") - self.horizontalScrollBar = QtWidgets.QScrollBar(self.groupBox) - self.horizontalScrollBar.setGeometry(QtCore.QRect(0, 310, 571, 19)) - self.horizontalScrollBar.setOrientation(QtCore.Qt.Horizontal) - self.horizontalScrollBar.setObjectName("horizontalScrollBar") + self.groupBox.setStyleSheet("color: rgb(0,0,0);") + + + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) - self.textBrowser.setGeometry(QtCore.QRect(0, 360, 911, 111)) + self.textBrowser.setGeometry(QtCore.QRect(10, 360, 942, 111)) self.textBrowser.setObjectName("textBrowser") + self.pushButton = QtWidgets.QPushButton(self.centralwidget) - self.pushButton.setGeometry(QtCore.QRect(840, 0, 61, 21)) - self.pushButton.setStyleSheet("color: rgb(200, 200, 200);") + self.pushButton.setGeometry(QtCore.QRect(870, 0, 81, 21)) + self.pushButton.setStyleSheet("color: rgb(0, 0, 0);") self.pushButton.setObjectName("pushButton") + + self.pairingbutton = QtWidgets.QPushButton(self.centralwidget) + self.pairingbutton.setGeometry(QtCore.QRect(960, 0, 81, 21)) + self.pairingbutton.setStyleSheet("color: rgb(0, 0, 0);") + self.pairingbutton.setObjectName("pairButton") + self.pairingbutton.hide() + + + self.treeView = QtWidgets.QTreeView(self.centralwidget) self.treeView.setGeometry(QtCore.QRect(10, 26, 331, 325)) self.treeView.setObjectName("treeView") + self.right_treeView = QtWidgets.QTreeView(self.centralwidget) - self.right_treeView.setGeometry(QtCore.QRect(910, 26, 331, 325)) + self.right_treeView.setGeometry(QtCore.QRect(951, 26, 331, 325)) self.right_treeView.setObjectName("Right_treeview") self.right_treeView.hide() - self.right_verticalscrollbar = QtWidgets.QScrollBar(self.centralwidget) - self.right_verticalscrollbar.setOrientation(QtCore.Qt.Vertical) - self.right_verticalscrollbar.setGeometry(QtCore.QRect(1220, 28, 20, 320)) - self.right_verticalscrollbar.hide() - self.right_verticalscrollbar.setObjectName("VerticalScrollbar_for_righttreeview") + self.Connect.raise_() self.Discover.raise_() self.lineEdit.raise_() @@ -92,10 +102,23 @@ def setupUi(self, MainWindow): self.menuView = QtWidgets.QMenu(self.menubar) self.menuView.setObjectName("menuView") MainWindow.setMenuBar(self.menubar) + self.closing_app = QtWidgets.QAction(MainWindow) + self.closing_app.setObjectName("Closing Application") + self.closing_app.setShortcut("CTRL+Q") + self.closing_app.triggered.connect(self.closing_application) self.actionRight_Hand_tree = QtWidgets.QAction(MainWindow) self.actionRight_Hand_tree.setObjectName("actionRight_Hand_tree") + self.actionRight_Hand_tree.setShortcut("CTRL+N") self.actionRight_Hand_tree.triggered.connect(self.creating_right_window) + self.hide_Right_Hand_tree = QtWidgets.QAction(MainWindow) + self.hide_Right_Hand_tree.setObjectName("Hiding tree") + self.hide_Right_Hand_tree.setShortcut("CTRL+M") + self.hide_Right_Hand_tree.triggered.connect(self.closing_right_window) + + self.menuFile.addAction(self.closing_app) self.menuView.addAction(self.actionRight_Hand_tree) + self.menuView.addAction(self.hide_Right_Hand_tree) + self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuView.menuAction()) @@ -108,6 +131,7 @@ def setupUi(self, MainWindow): self.treeView.setHeaderHidden(True) self.rootNode = self.treeModel.invisibleRootItem() self.treeView.setModel(self.treeModel) + def retranslateUi(self, MainWindow): @@ -122,17 +146,50 @@ def retranslateUi(self, MainWindow): "\n" "


")) self.pushButton.setText(_translate("MainWindow", "Disconnect")) + self.pairingbutton.setText(_translate("MainWindow", "Pair")) + self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuView.setTitle(_translate("MainWindow", "View")) self.actionRight_Hand_tree.setText(_translate("MainWindow", "Expand a right hand tree")) + self.hide_Right_Hand_tree.setText(_translate("MainWindow", "Hide the right hand tree")) + self.closing_app.setText(_translate("MainWindow", "Quit")) + + def closing_application(self): + QtWidgets.qApp.quit() + + def creating_right_window(self): - MainWindow.resize(1245, 513) # resizing the window to be able to fit the new treeview - self.textBrowser.resize(1241, 111) # making the textbox bigger to be able to display more information + MainWindow.resize(1300, 513) # resizing the window to be able to fit the new treeview + self.textBrowser.resize(1272, 111) # making the textbox bigger to be able to display more information self.right_treeView.show() - self.right_verticalscrollbar.show() + self.pairingbutton.show() + + + def closing_right_window(self): + self.right_treeView.hide() + self.textBrowser.resize(942, 111) + self.pairingbutton.hide() + + + def manual_connection(self): + manually_entered_server = self.lineEdit.text() + url = dsc.Server_Discovery() + url.get_servers() + servers = url.get_all_as_address() + + if manually_entered_server in servers: + self.textBrowser.append("Server:" + manually_entered_server + " found and connected.") + + + else: + self.textBrowser.append("no server found") + + + + def discover_servers(self): From adc1296718a0ff1d767df15b2aa926227121276f Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sun, 21 Feb 2021 21:27:32 +0100 Subject: [PATCH 39/77] Changes to the Linking which is now recursive (In testing), mapping has been completely revamped to be instead clickable to navigate through (You double click to check the next variables in that node) --- server/py.server.py | 24 ++++-- server/server.py | 3 + uaxplorer/uaxplorer.py | 69 ------------------ uaxplorer/ui_methods/Ui_client.py | 1 - .../__pycache__/Ui_client.cpython-38.pyc | Bin 891 -> 891 bytes .../__pycache__/client_nodes.cpython-38.pyc | Bin 1821 -> 1880 bytes .../navigating_nodes.cpython-38.pyc | Bin 1470 -> 2654 bytes uaxplorer/ui_methods/client_nodes.py | 15 ++-- uaxplorer/ui_methods/navigating_nodes.py | 65 ++++++++++++++--- uaxplorer/ui_methods/server_discovery.py | 2 +- 10 files changed, 86 insertions(+), 93 deletions(-) delete mode 100644 uaxplorer/uaxplorer.py diff --git a/server/py.server.py b/server/py.server.py index 95f53eb..c6c827a 100644 --- a/server/py.server.py +++ b/server/py.server.py @@ -15,17 +15,29 @@ addspace = server.register_namespace(name) node = server.get_objects_node() - +t = node.add_variable(addspace,"Lol", 0) Params = node.add_object(addspace, "Parameters") - -Temp = Params.add_variable(addspace, "Temperature",0) -Press = Params.add_variable(addspace, "Pressure",0) -Time = Params.add_variable(addspace, "Time",0) +New_object = Params.add_object(addspace, "Ddad") +tt = New_object.add_object(addspace, "Hi") +ff = tt.add_variable(addspace, "Yoo", 0) +Params2 = node.add_object(addspace, "Params2") +Params3 = Params2.add_object(addspace, "Params5") +Params4 = Params3.add_object(addspace, "Params6") +Params5 = Params4.add_object(addspace, "Params8") +Ff = Params3.add_variable(addspace, "Hello", 0) +tt = Params4.add_variable(addspace, "ff", 0) +ttr = Params5.add_variable(addspace, "feeef", 0) +Temp = Params.add_variable(addspace, "ixTemperature",0) +Press = Params.add_variable(addspace, "ixPressure",0) +Time = Params.add_variable(addspace, "ixTime",0) +Test = Params.add_variable(addspace, "ixTest",0) +Test3 = Params.add_variable(addspace, "qxTesting",0) +Test4 = Params2.add_variable(addspace, "qxTrue", 0) Temp.set_writable() Press.set_writable() Time.set_writable() - +Test.set_writable() #starting server server.start() sa.start_service_announcement(device_name="kfd347-server.", diff --git a/server/server.py b/server/server.py index ca7415a..e20a8df 100644 --- a/server/server.py +++ b/server/server.py @@ -115,9 +115,12 @@ async def main(): #We want to find the OPC-DA server nodes in aliases nodes_list = da.list('*', recursive=True) #A list of dot-delimited strings await sort_nodes_list(nodes_list, idx, root, da) + + try: async with server: #Starting the server + handler = SubHandler() #Subscribing to datachanges coming from the UA clients sub = await server.create_subscription(500, handler) handle = await sub.subscribe_data_change(writeable_variables.values()) diff --git a/uaxplorer/uaxplorer.py b/uaxplorer/uaxplorer.py deleted file mode 100644 index eada932..0000000 --- a/uaxplorer/uaxplorer.py +++ /dev/null @@ -1,69 +0,0 @@ -from opcua import Client -from discovery import Discovery -import time - -url = "opc.tcp://127.0.0.1:4840" - -client = Client(url) - -#connecting client to server -client.connect() - - -class Root(Tk): - def __init__(self): - super(Root, self).__init__() - self.title("GUI for client") - self.minsize(400,200) - - self.button = ttk.Button(self, text="Press4Temperature",command = self.clickbutton) - self.button.grid(column=0, row=0) - - self.pressurebutton = ttk.Button(self,text="Press4Pressure", command = self.clickbutton2) - self.pressurebutton.grid(column=0, row = 1) - - self.datetimebutton = ttk.Button(self,text="Press4Datetime", command = self.clickbutton3) - self.datetimebutton.grid(column=0, row = 2) - - - - - self.label = ttk.Label(self, text="The temperature is:") - self.label.grid(column=1, row=0) - - self.label = ttk.Label(self, text="The Pressure is:") - self.label.grid(column=1, row=1) - - self.label = ttk.Label(self, text="The Datetime is:") - self.label.grid(column=1, row=2) - - - - def clickbutton(self): - self.Temp = client.get_node("ns=2;i=2") - Temperature = self.Temp.get_value() - self.label = ttk.Label(self, text=Temperature) - self.label.grid(column=3, row=0) - - - - def clickbutton2(self): - self.Press = client.get_node("ns=2;i=3") - Pressure = self.Press.get_value() - self.label = ttk.Label(self, text=Pressure) - self.label.grid(column=3, row=1) - - - - def clickbutton3(self): - self.Time = client.get_node("ns=2;i=4") - TIME = self.Time.get_value() - self.label = ttk.Label(self, text=TIME) - self.label.grid(column=3, row=2) - - - - - -root = Root() -root.mainloop() diff --git a/uaxplorer/ui_methods/Ui_client.py b/uaxplorer/ui_methods/Ui_client.py index 66cac60..69b6b6f 100644 --- a/uaxplorer/ui_methods/Ui_client.py +++ b/uaxplorer/ui_methods/Ui_client.py @@ -8,7 +8,6 @@ def __init__(self, server): self.client = Client("opc.tcp://" + server) self.servers = server - def create_connection(): url = disc.Server_Discovery() url.get_servers() diff --git a/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/Ui_client.cpython-38.pyc index 6ce192968fdc359c4b4dff6c5a52c156e2a3f621..5364739dcaa14c87ffff182306db515860eca12b 100644 GIT binary patch delta 30 kcmey(_M442l$V!_0SF$hGfMcpk@r0lBlqOLOtp-x0FnU-Z2$lO delta 30 kcmey(_M442l$V!_0SMM!l~4G(k@r0lBhTc&Otp+`0FVL+Q2+n{ diff --git a/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc index 5447db6ad18e2c19378cdb0aecb867a536d6ce63..57e286cd5a3cad52618e0935917ab21258b4b4e4 100644 GIT binary patch delta 897 zcmYjPOHUL*5boE!mYHRF=^~04jk=N$Azn<331K0DASf%uOf)0V11{jM%ybOd%!b1r zI2aG6Zzh_VgPy$@ZpNcG4_?gr3%K|fq`G$#=}LY5b=8-uetpRQ&U>q#mqRd)@7{j? z?Va~)+)*nS@f1Lae9VzgIOc>?PD7e9t}UZmhIH`Mp{dm!uwY`~6g*x75U7PxoFR-7 zltPsZYeo~J%UGgg!bnX{5|WXonV6}Sux{Q?%r0{hsu)KVRH93B4QKgPSR1RzFPPz+ z{E8n>w%s@sGcUr;-Sv&Vuw=Da9PVwksW=e6vsJH&Ag(q--`P}>V55E@uaKj@0&ElE zT*rtDwT{U~;XDK*4MU0*Wr=R*)$v^L8SuC$Khhg3oVHkk0;38C49snXmgY8B+!471 z?a&r((#9RdQdeIFO}$L#A!eufgr>dV1#nMdHgMRZR8K$w*}e>w(X+*;q{*5_VgSDn zmjqpR%v~$`F8W(a2a$W-c^IVwf+Z$rVlW-zhO&%u^R2$X{m=XFl^*I`YpN=iRx@sC zwavlYGxERk6T4m*gd?A*uW|ANpma|DVN;WS?}O=w!P@lV>TIyIT%N7?J#)*8<=GXz zYTGInfTV3{HeZ(G#^r*a-ws6()$1at)p;1p1!GdaF-D3PRb?ychpBDGVe~qTqN}iw zKaH`&{|{1xP?QH40FQ40AZ&u=Nxz;I&tak;bd0zVwSl|%CduK((9B*ngrA^0PXs}= zRuw_8RASLE)QK)A7*U`abUuLYJfcyz9)*nBV@v>S9F58&b77(c3*UQG5$iR+9`Sh* UzKqm;MM{3AP^XUkXI{eIDb%IGwg3PC delta 807 zcmZWm&1(}u6rYcs+0EA`O%NYLJ(C8~Q*vJrj~uKgq+w3AhRpSkP=qZPBi+=gxv| zfQFX(NG}{ss6;O7LH922YBYOjOob)UKAqsPF{)}7rIQJ1l#xc11YUqCR9?U!Dk^{dnHYI~7{@4uOi^4=_>~YD~G=Xy&MRUTCre}y^ zH19DjG4=SXu8Mei-Kq^DywQl!ng$ z&Y~)^)s<=f*7}yayWZTXyPK_AeS4Z|w3@a0?Zi@t?2LNHE~nde*B5Ru7zp)+opnw@ zfhijXy}od)U`|^+^n+)95S#;}ZRt`jR8|(RZ2Vcn5;z0BsUm1t1wfdC*rcFOV!-+c z=nBcgjRh==t0avr^^~8!R)X+gS;I*Uy3TC7|NNi~4&75o04yAo)dzkPS5?j|Eo$F? KN$R>;#@XMe%YJA=LUYFD0J zL9ANkV)7qyqNLo&?@2Cu&56H|Q@Up)kYy*Mnx1FRPJdr_k9OwgJp#qKxc=m?MMD0@ z!E|w8a35;E4T2L+6VjnR?J%EF@+IMn8!rhrj+t)=%FScyo5CDgBSMKV1deUt2z%&4 ztwHrhWJE{wm{P*6?@7ac1Lu+ktL&{L7HLtjUKrujbYU>K4>i9FLJ*&F;xkS;dr5qQ z8{CA}3T;0xGQa29Il)%HC>mR}?|^bYDb8 zu5hmEI1}x~36~ zggYE zYpCm*l^~QdJo4u!j3}S=YlY}^RaZhbAQj!8<>JCLjhc_kurvP<1oB~0mo7u~=mNc@ z+A9o4{cC4(^!D?oV-Xp~s(AVnI>;0F9Z{S-X|sU=T$#YnHt=IX`_;fM&}SL=rVY57 zQ#{y;zgA%>{%pmc4LsN<_m6qF1WY(3115fhbr*I)(gH4YU->;SRMQcFv+(uxfm1r; z+kLEWS9s&ofyv?Ai0s!nL=Yyw!#@sRscd4j6tk7t3Ok}PUs*9wkjo{oS8**d3(e3h zmvIh#SYj+xR#(Q5I?o|BPi6KZ7eNZAVMf4A6a)=dbMJOS&8}Zd#S67~qcRS}QDr|5 zlb*;cOSQfpZYU?1@c{KnOc41M5QLd@5omVDWUm3)b@+MAql>J+c8c^FuZ%G7ObW7% zzN{GEgN}~MF{Y0JQxlkGnrX%OJ;2;l5w@vvw4owXdw`=oVBg|&g_QOIgJ^_x#W}12 z2WyAGGqUiSwd5uEUc-?q(CC1|ysgpy0DI`*D##{adMYE$h7rC8{<2z{_;$>7L}8pr z1Wm(w6?^z9Ol7*ulf0!K6cnKROlBQL=tJ0?V_agOY1A>aR%yR^3g`bl=)dz}35TDE zv;CnPOMEu8)m(?I*JQm0yCXlgA0sl zsJvVhTs#YVNih`*Gi;30T1|PnLcfO2KMVO;XfGj1YO{tI{|>a^`#EOSF~;D}SJ)=& ze{v?mv&xMc2A!G>DlZ5+8Sf<`2&#Gz9QMLQugEz#Ug9$?FQUL8mLH+GhT@%1s2)Cn z5}#(w@Od4C%UsiS-Sh7IuBTS|m$PovETZn6>(@7K-)L@Z-fC{FH#cr?-nn_}=6b6! zS6Q&57fSqdBQfTbo_#TM J1`~p7{0qWpa3TNz delta 677 zcmZuv!EVz)5Zzs`opstkNpqu#lxM%|b!efmLJ>+(2VLMWz}o9&DIetl z(NyxRFFccTfdnfuJaUO4sQ8H`_KIV&ecOB)b<0PsC(ok&c6jCfOQ7lA!)~z-U4j(t$ ztuP9oHd|5iuyG`=LnR*kGr=}puRpzPyDKtfH!c*uj2cGH{> zr7laJC%hj|vfNq|H%xW$)xG(``U$*CJARTHGhnmpk4%FIxOPtXnfBhr*@R2&+UHd} zU~tt=3Ke}Z9@?4ZMzQMe;Zo7|@1Mg`#R@ 0: + if j not in temp_dict: + temp_dict[j] = list() + temp_dict[j].append(self.client.get_node(j).get_children()) + + if bool(temp_dict): + self.run_recursively(temp_dict) + return children_dict + + def run_recursively(self, map_dict): + + new_dict2 = {} + for key, values in map_dict.items(): + for value in values: + for value1 in value: + if len(self.client.get_node(value1).get_children()) > 0: + if value1 not in new_dict2: + new_dict2[value1] = list() + new_dict2[value1].append(self.client.get_node(value1).get_children()) + + if bool(new_dict2): + self.node_recursive_dict.append(map_dict) + self.node_recursive_dict.append(new_dict2) + self.run_recursively(new_dict2) + + for i in self.node_recursive_dict: + for key, values in i.items(): + + print(self.client.get_node(key).get_browse_name().__dict__['Name']) + + + def get_rootnode_nodeid_from_name(self, root_array, children_list): + root_childrenid_dict = {} + for key, values in root_array.items(): #We iterate over all the root nodes and its children nodes. + for i in children_list: # We iterate over the children string array + for value in values: # We check the values in the root node + if key not in root_childrenid_dict: + root_childrenid_dict[key] = list() + if self.client.get_node(value).get_browse_name().__dict__['Name'] == i: + root_childrenid_dict[key].append(value) def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects : values that are only path values and converts to the name name_dict = {} @@ -48,3 +86,12 @@ def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects name_dict[self.client.get_node(key).get_browse_name().__dict__['Name']].append(self.client.get_node(value).get_browse_name().__dict__['Name']) return name_dict + + +client = Client("opc.tcp://192.168.10.196:4840") +client.connect() +navigating = Navigating_nodes(client) +#root_nodes = navigating.get_root_nodes() +#children_nodes = navigating.get_children_nodes(root_nodes) +print(navigating.get_name_from_nodes(navigating.get_children_nodes(navigating.get_root_nodes()))) +#navigating.get_rootnode_nodeid_from_name(children_nodes, ['qxTrue', 'ixTemperature', 'ixTime']) \ No newline at end of file diff --git a/uaxplorer/ui_methods/server_discovery.py b/uaxplorer/ui_methods/server_discovery.py index d7bd4e8..32de2e0 100644 --- a/uaxplorer/ui_methods/server_discovery.py +++ b/uaxplorer/ui_methods/server_discovery.py @@ -23,7 +23,7 @@ def get_servers(self): # To find the servers available while(time.time() < time_to_end): if(len(d.get_services()) > 0): self.DISCOVERY_OUTPUT = d.get_services() - + return self.DISCOVERY_OUTPUT def get_all(self, type): #0 for server name, 1 for ip address, 2 for port number From 67876afa169df42688e68a425245bb77d1a77f84 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Sun, 21 Feb 2021 21:39:00 +0100 Subject: [PATCH 40/77] More changes to linking (now recursive) mapping fixed and more optimized also the bugs with not being able to navigate nodes is fixed --- uaxplorer/ui_methods/MainUI.py | 128 ++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 10 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index f27f51d..da600c7 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -14,9 +14,15 @@ import server_discovery as dsc import Ui_client as ui_c import client_nodes as cl_node +from client_nodes import StandardItem as StItem import navigating_nodes as nav import sys +class Node_storage: + def __init__(self, node_name, node_id, standarditem): + self.node_name = node_name + self.node_id = node_id + self.standarditem = standarditem class Ui_MainWindow(object): def setupUi(self, MainWindow): @@ -125,13 +131,27 @@ def setupUi(self, MainWindow): self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) + ############################### Discovery ################################ self.treeModel = Qt.QStandardItemModel() self.treeView.setHeaderHidden(True) self.rootNode = self.treeModel.invisibleRootItem() self.treeView.setModel(self.treeModel) - + self.treeView.doubleClicked.connect(self.getValueLeft) + + ###### Right hand tree ####### + self.right_treeView.setModel(self.treeModel) + self.right_treeView.doubleClicked.connect(self.getValueRight) + + + ## LINKING ## + self.left_server = None + self.right_server = None + + ## VARIABLES ##### + self.ROOT_CHILDREN_NODES = [] + def retranslateUi(self, MainWindow): @@ -158,8 +178,100 @@ def retranslateUi(self, MainWindow): def closing_application(self): QtWidgets.qApp.quit() + + def getValueLeft(self, val): + node_name = val.data() + bool_continue = True + for i in self.clients: + if(i.server_name == node_name): + for k in i.NODE_ID: + for j in self.ROOT_CHILDREN_NODES: + i.client.connect() + children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] + if(children_name == j.node_name): + bool_continue = False + break + for i in self.clients: + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name): + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + print(children_name) + for k in self.ROOT_CHILDREN_NODES: + if(children_name == k.node_name): + bool_continue = False + break + + for i in self.clients: + if(i.server_name == node_name and bool_continue == True): + + for j in i.NODE_ID: + i.client.connect() + children_name = i.client.get_node(j).get_browse_name().__dict__['Name'] + qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + i.ROOT_NODE.appendRow(qtitem) + self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, j, qtitem)) + + for i in self.clients: + for j in self.ROOT_CHILDREN_NODES: + + if(node_name == j.node_name and bool_continue == True): + + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + # printif(i.client.get_node(d).get_children() == 0): + + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + + qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + j.standarditem.appendRow(qtitem) + self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, d, qtitem)) + #self.treeView.selectedIndexes()[0].model().itemFromIndex(val).appendRow(StItem(children_name, 8, color=QtGui.QColor(180, 180, 180))) + + + def LinkValueLeft(self, val): + if(val.parent()): + SERVER_NAME = val.data() + + print(SERVER_NAME) + if(self.left_server == None and val.parent()): + for i in self.clients: + if(i.server_name == SERVER_NAME): + self.left_server = i + self.textBrowser.append("Linked server to the left to: " + SERVER_NAME) + else: + self.textBrowser.append("Unlinked server to the left for: " + SERVER_NAME) + self.left_server = None + + def getValueRight(self, val): + if(val.parent()): + SERVER_NAME = val.data() + + if(self.right_server == None and val.parent()): + for i in self.clients: + if(i.server_name == SERVER_NAME): + self.right_server = i + self.textBrowser.append("Linked server to the right to: " + SERVER_NAME) + else: + self.textBrowser.append("Unlinked server to the right for: " + SERVER_NAME) + self.right_server = None + #Left ix Pressure -> qx Pressure Right + #Left qx Temperature <- ix Temperature Right + if(self.right_server != None and self.left_server != None): + server1_lst = list([j for i in self.left_server.NODE_MAP.values() for j in i]) + server2_lst = list([j for i in self.right_server.NODE_MAP.values() for j in i]) + result1 = filter(lambda x: 'ix' in x, server1_lst) + result2 = filter(lambda x: 'qx' in x, server2_lst) + print(list(result1)) + print(list(result2)) + + # for i,j in itertools.zip_longest(list([j for i in self.right_server.NODE_MAP.values() for j in i]), + # list([j for i in self.left_server.NODE_MAP.values() for j in i])): + # print(i,j ) + def creating_right_window(self): MainWindow.resize(1300, 513) # resizing the window to be able to fit the new treeview @@ -201,22 +313,18 @@ def discover_servers(self): url.get_servers() self.SERVER_ARR = url.get_all(0) servers = url.get_all_as_address() - print(servers) + + j = 0 for i in servers: self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) + self.textBrowser.append("Service added: " + self.SERVER_ARR[j] + "- At address: " + i) + j += 1 for i in self.clients: self.rootNode.appendRow(i.ROOT_NODE) - for j in i.FOLDER_NODE: - i.ROOT_NODE.appendRow(j) - - t = 0 - for j in i.MAP_VALUE_NODES.values(): - for d in j: - i.FOLDER_NODE[t].appendRow(d) - t += 1 + From d6ccd48d53725b2fa71960d0008c9f282fac0683 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 24 Feb 2021 19:33:33 +0100 Subject: [PATCH 41/77] Big bug changes, thread optimizing of MainUI and mapping is even more optimized. --- server/py.server.py | 2 +- uaxplorer/ui_methods/MainUI.py | 97 +++++++++++------- .../__pycache__/client_nodes.cpython-38.pyc | Bin 1880 -> 1880 bytes .../__pycache__/discovery.cpython-38.pyc | Bin 0 -> 2480 bytes .../navigating_nodes.cpython-38.pyc | Bin 2654 -> 2483 bytes .../server_discovery.cpython-38.pyc | Bin 0 -> 1476 bytes uaxplorer/ui_methods/client_nodes.py | 8 +- uaxplorer/ui_methods/navigating_nodes.py | 12 +-- 8 files changed, 73 insertions(+), 46 deletions(-) create mode 100644 uaxplorer/ui_methods/__pycache__/discovery.cpython-38.pyc create mode 100644 uaxplorer/ui_methods/__pycache__/server_discovery.cpython-38.pyc diff --git a/server/py.server.py b/server/py.server.py index c6c827a..3d50f27 100644 --- a/server/py.server.py +++ b/server/py.server.py @@ -10,7 +10,7 @@ url = "opc.tcp://192.168.10.196:4840" server.set_endpoint(url) -name = "Test for GUI" +name = "vvvvvvvvvvvvvvv" addspace = server.register_namespace(name) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index da600c7..41d5648 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -19,7 +19,8 @@ import sys class Node_storage: - def __init__(self, node_name, node_id, standarditem): + def __init__(self, server_name, node_name, node_id, standarditem): + self.server_name = server_name self.node_name = node_name self.node_id = node_id self.standarditem = standarditem @@ -73,7 +74,7 @@ def setupUi(self, MainWindow): self.pairingbutton.setStyleSheet("color: rgb(0, 0, 0);") self.pairingbutton.setObjectName("pairButton") self.pairingbutton.hide() - + self.pairingbutton.clicked.connect(self.linking_servers) self.treeView = QtWidgets.QTreeView(self.centralwidget) @@ -139,7 +140,6 @@ def setupUi(self, MainWindow): self.rootNode = self.treeModel.invisibleRootItem() self.treeView.setModel(self.treeModel) self.treeView.doubleClicked.connect(self.getValueLeft) - ###### Right hand tree ####### self.right_treeView.setModel(self.treeModel) self.right_treeView.doubleClicked.connect(self.getValueRight) @@ -178,59 +178,79 @@ def retranslateUi(self, MainWindow): def closing_application(self): QtWidgets.qApp.quit() + def linking_servers(self): + server1 = None + server2 = None + for i in self.clients: + if(i.ROOT_NODE.checkState() == 2 and server1 == None): + server1 = i + if(i.ROOT_NODE.checkState() == 2 and i.server_name != server1.server_name): + server2 = i + print(server2.server_name) + print(server1.server_name) + def getValueLeft(self, val): node_name = val.data() bool_continue = True - for i in self.clients: + root = val.parent() #The root node of the node index that the user double clicks + + while(root.parent().data() != None): #We loop till we get None as data (We're at the end of the hierarchy) + root = root.parent() + + for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview if(i.server_name == node_name): for k in i.NODE_ID: + i.client.connect() for j in self.ROOT_CHILDREN_NODES: - i.client.connect() + children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] if(children_name == j.node_name): bool_continue = False break + i.client.disconnect() - for i in self.clients: - for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name): - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - print(children_name) - for k in self.ROOT_CHILDREN_NODES: - if(children_name == k.node_name): - bool_continue = False - break - - for i in self.clients: + for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview + if(root.data() == i.server_name): + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name): + + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + for k in self.ROOT_CHILDREN_NODES: + if(children_name == k.node_name): + self.textBrowser.append("Children_name already exists!" + children_name + k.node_name) + bool_continue = False + break + i.client.disconnect() + + for i in self.clients: #We add the first children to the root node if(i.server_name == node_name and bool_continue == True): for j in i.NODE_ID: + i.client.connect() children_name = i.client.get_node(j).get_browse_name().__dict__['Name'] qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) i.ROOT_NODE.appendRow(qtitem) - self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, j, qtitem)) - - - for i in self.clients: - for j in self.ROOT_CHILDREN_NODES: - - if(node_name == j.node_name and bool_continue == True): - - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - # printif(i.client.get_node(d).get_children() == 0): - - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - - qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) - j.standarditem.appendRow(qtitem) - self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, d, qtitem)) - #self.treeView.selectedIndexes()[0].model().itemFromIndex(val).appendRow(StItem(children_name, 8, color=QtGui.QColor(180, 180, 180))) - + server_name = node_name + self.ROOT_CHILDREN_NODES.append(Node_storage(server_name,children_name, j, qtitem)) + i.client.disconnect() + + + for i in self.clients: #We add the first children to the first children of the root node (and etc) + if(root.data() == i.server_name): + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name and bool_continue == True): + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + j.standarditem.appendRow(qtitem) + server_name = self.treeView.selectedIndexes()[0].model().index(0, 0).data() + self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) + i.client.disconnect() def LinkValueLeft(self, val): if(val.parent()): @@ -309,6 +329,9 @@ def discover_servers(self): self.clients.clear() self.treeModel.removeRows(0, self.treeModel.rowCount()) + if len(self.ROOT_CHILDREN_NODES) > 0: + self.ROOT_CHILDREN_NODES.clear() + url = dsc.Server_Discovery() url.get_servers() self.SERVER_ARR = url.get_all(0) diff --git a/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/client_nodes.cpython-38.pyc index 57e286cd5a3cad52618e0935917ab21258b4b4e4..150598cbccd5278e55b72d7bc59ab9584581b97b 100644 GIT binary patch delta 271 zcmcb?cY}{Nl$V!_0SNp(OcSnc_vh=>K0pZYEfBgQIX(^b delta 271 zcmcb?cY}{Nl$V!_0SLBR8YJx7$g9Q3^z#2^OU7J9tfCK|08xZm_34v)4n~RYNh$qLe=~_wx<%^_1gfxf%X+&}g q*jTWMazF`?y~RvGf`hS0Zt@{EFGkJDyzCO9AUO<8ldafg8F>Mhel8dQ diff --git a/uaxplorer/ui_methods/__pycache__/discovery.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/discovery.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ceef999ada98961d7e9c97ed7c82bee1804040d GIT binary patch literal 2480 zcmcgt-EtE*6qfeSYsWDmg_KYzp`A|a4&<`aNofa2N~hB{Fc^BVTeC|edS(Eh%j}~P^E*@SuViV4pT$(V>_MVAGi#0F2#rdr1m_|#g zmq}ONSLrJ)l?fxM(r!BJz2*4ftpm}eOHWGx#Y5dh)psBYcE}1|I7iNbTeyPlR@JwK zd&rO2frHjT>*bi)@h1GhFPy?F{GQta*cLnH+f6@OdM@)AXVW#}WY|khNF&3}Kt@Yh zM`)NjYo@&N1H^|*&0>FJ&&1cp!uiO-Dp3> zX}bO3m*3l4Qt#$z*4D`&+lOdBe6;!W-tEnFFc=M!ZYNLDVSCj1E9<99s`e;}2Qq(= z3f&fo?xruL+F#H1%L~(~m&h!14&!wm+ONiITrFXwOT+VpjC&VV--3`V;&8enxNzWb zSGdAM3-^lvtuHDfL>q`I+*dOfx2EU+n&?~(rRvgl(?U8`>m~|GxQNBp=!#s}(0yqH|8vWsFvc^+AO#?V3y;+ znnPX2scHoR#<;h&^%%{ghmW6>Uj3LR-!Z`gwwdDp67zHTuki8p^Z4if|G&WBoC80~ zL?=H7U#$|f_u*@l3#JfnpM&@w7oT0S-K^xwG~i<8Ec?DeCjno_ zEawwy9ZL#s?jh!oeHiicz-A!ZaVZXwqYm+ONf{O6Ya$CpyV?XZKC!O0*EeDRgoqST zMAI$v@N?)<}_wMdp zquomIf%**R7>__2Z&&VX<8M)Np|PnJbh1niMKiFt4euMiTXGM*KE|VjeFwj(&%J#5 zK7k?rWniBGb=k7eGQY&fE2jpTUxL?D5h<%1B-TjSIHWkAJL@$1h~qX;H3bEtucBy# z0({U(hH*SziPNk*>fFw|*?LT(pLRR__3@RH@fUfX=`%Ca6|eM}@$Kftl7lH2%7`Ii zQddcQLgFhDL^>sRQDWHJSVX4wMCv@Y;bpienT=o>KN)7D{C((5c&ALxA1GGz8pP6~ Y2P4|g39F6kjmsf-==;}mL#NLF1CdH1RR910 literal 0 HcmV?d00001 diff --git a/uaxplorer/ui_methods/__pycache__/navigating_nodes.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/navigating_nodes.cpython-38.pyc index 1c600d9f4501211bf8c924568247c8e252220dc5..675714d1c6f37f8fd8bfb207371ed544ac4fd891 100644 GIT binary patch delta 375 zcmca7vRPO=l$V!_0SK07nI^0hU|@I*;vfTNAjbiSi> z1v4Wfxm?2~q>r2RCc-8CL1Z`s{WB(jYk*5CQZSLy_#}T6R%J4LKkis__}W~Mj%m0CK8$0J0FK2kXOND}gm#(&YJu_g6#?`~JPi-?fiPjWCz82xry+u6@Fe5gMiD-ZPZ@RFJh3H@Iw754Ok|S7wTku)LyjTyH zt>)x;!^*i*s?4~m@(>nLgo3~g1Km1z9A>$P j5vO`xJFU7k=ai3QcplN8I4Ewz3km$@WGt~`fIRdET8d=j diff --git a/uaxplorer/ui_methods/__pycache__/server_discovery.cpython-38.pyc b/uaxplorer/ui_methods/__pycache__/server_discovery.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c481688d81474c9089fdedc546080d85483be25c GIT binary patch literal 1476 zcmZ`(%}(1u5Z)grF-f2(q6MT@LT#nW2RQYFs){P~Pn7~{2vV(7Ti3e<7biBmPTNGz z2_A$Tc_m+a;uSblopBPBK)cq?&aP+1-+VLP*5YEBz_?%E_);qn@*6j^gTrPArg;oP z5=lei(tKqudrKsf{G3RB##}CGlQ`BD*pY^ha&|D-?7%e7L8v^2%OsWToPZ~n7Oa*m z$Rex-S&}xaMY$l$u$E*6B3HBJrqVx@7Q2D*BT)K2g<#WxI)0@N(_m6ZBqahcH_0V! zI5b<{ec!C_9lrkfN$j-__FD&VW*HTJyCk{@_Sdez-EJABP5aGhaUeonJu{5skHQikXEJxC=M+%B&>lsEmiI>sF6cB9N)TS4Lxsj-}DrEgvY8h@>AY zlTqnb&_^VZP+ghVX)H=$npLJjplJ8tz^s5Ej8S<0SXP-<;aR3tHh6f4>KRu*uLruq zKq!bKXZtZIM`Xx=DY8pG*M`U@O?gV!$%s;NT25J$XsnLI@+`8V7smi~k`1H`iMZuN zbz4W==`tIv{S!8r&Ot`_Hw}Umq*MYx$yQP0>tq#v z0>^4xVO_52_7ythAOJMxnia7UO5XwIN1d-hS3L!rY17_?G%=d%S%iZ>Rn8*11B-OR z;tKFdfkkq`QZ|C_&3S~|W6UGZdDK`>y$ZtNY1e~x6Ioe^P9%FF>Qy05dtNwxqp?7G2}KPBLe%RhFy9%09jcM( x3e5aQvZ>AOCA(zv{M@nJg_}!4 Date: Wed, 24 Feb 2021 19:40:27 +0100 Subject: [PATCH 42/77] Added main function to LocalClient and removed raspua/server path in server endpooint address. --- server/localclient.py | 2 ++ server/serverpi.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/server/localclient.py b/server/localclient.py index 0029127..63b385a 100644 --- a/server/localclient.py +++ b/server/localclient.py @@ -26,3 +26,5 @@ def remove_node(self, node, name): def get_value(self, name): return self.values[name]) +if __name__=="__main__": + c = LocalClient() diff --git a/server/serverpi.py b/server/serverpi.py index 01ce65f..8f3dc9b 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -11,14 +11,21 @@ SERVER_NAME = "RaspPI OPC UA Server" FLAT_NAME = SERVER_NAME.replace(" ", "") -SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840/raspua/server" +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840" UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME TEMP = 19 +def subscribe(endpoint, qx, ix): + + +class SubHandler(): + pass + class ServerPI: def __init__(self): self.temp = TEMP + self.clients = [] async def go(self): server = Server() From 7e3f542f8dbbe74751cd3e56f906a573e7bbedce Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 24 Feb 2021 19:43:31 +0100 Subject: [PATCH 43/77] Added pycache in ui_methods to gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fbba710..8491e74 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ server/__pycache__ +uaxplorer/ui_methods/__pycache__ From ef77dd19f48ebc534df38fd5b4e2fcf60ae30c5f Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 24 Feb 2021 20:30:41 +0100 Subject: [PATCH 44/77] Fixed initial method code. --- server/serverpi.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/server/serverpi.py b/server/serverpi.py index 8f3dc9b..9dc479c 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -15,7 +15,18 @@ UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME TEMP = 19 -def subscribe(endpoint, qx, ix): +# This ua method is used to subscribe to a variable on +# another server. +# Inputs: +# endpoint(string): the path to server to subscribe from. +# qx(string): The variable to subscribe to +# ix(string): The variable to connect subscription to +# Returns a Boolean value, depending on if the subscription +# was successful or not. +@uamethod +def subscribe(parent, endpoint, qx, ix): + + return True class SubHandler(): @@ -47,6 +58,34 @@ async def go(self): zobj = await server.nodes.objects.add_object(idx, "ZeObject") zvar = await zobj.add_variable(idx, "Temperature", self.temp) + endp = ua.Argument() + endp.Name = "Endpoint" + endp.DataType = ua.NodeId(ua.ObjectIDs.String) + endp.ValueRank = -1 + endp.ArrayDimensions = [] + endp.Description = ua.LocalizedText("Address to endpoint") + qxvar = ua.Argument() + qxvar.Name = "qx" + qxvar.DataType = ua.NodeId(ua.ObjectIds.String) + qxvar.ValueRank = -1 + qxvar.ArrayDimensions = [] + qxvar.Description = ua.LocalizedText("Output variable to connect to server.") + ixvar = ua.Argument() + ixvar.Name = "ix" + ixvar.DataType = ua.NodeId(ua.ObjectIds.String) + ixvar.ValueRank = -1 + ixvar.ArrayDimensions = [] + ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") + res = ua.Argument() + res.Name = "Result" + res.DataType = ua.NodeId(ua.ObjectIds.Boolean) + res.ValueRank = -1 + res.ArrayDimensions = [] + res.Description = ua.LocalizedText("Result if variable was connected or not.") + + + submethod = zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], [res]) + async with server: while True: await asyncio.sleep(0.1) From 4ccbcafac50b2345b801caa747138d8fc0c9a3f0 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 24 Feb 2021 21:23:28 +0100 Subject: [PATCH 45/77] Added some more subscribe code and changed variable name of qxtemperature. --- server/serverpi.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 9dc479c..7c92387 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -15,6 +15,11 @@ UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME TEMP = 19 +client = None +handler = SubHandler() +sub = None +handle = None + # This ua method is used to subscribe to a variable on # another server. # Inputs: @@ -25,12 +30,38 @@ # was successful or not. @uamethod def subscribe(parent, endpoint, qx, ix): - - return True + client = Client(endpoint) + try: + client.connect() + client.load_type_definitions() + + #root = client.get_root_node() + + #uri = "http://examples.freeopcua.github.io" + #idx = client.get_namespace_index(uri) + + qxvar = client.get_node(qx) + sub = client.create_subscription(500, handler) + handle = sub.subscribe_data_change(qxvar) + time.sleep(0.1) + + return True class SubHandler(): - pass + """ + Subscription Handler. To receive events from server for a subscription + data_change and event methods are called directly from receiving thread. + Do not do expensive, slow or network operation there. Create another + thread if you need to do such a thing + """ + + 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) + class ServerPI: @@ -56,7 +87,7 @@ async def go(self): device = await server.nodes.objects.add_object(idx, "ZeDevice", dev) zobj = await server.nodes.objects.add_object(idx, "ZeObject") - zvar = await zobj.add_variable(idx, "Temperature", self.temp) + zvar = await zobj.add_variable(idx, "qxTemperature", self.temp) endp = ua.Argument() endp.Name = "Endpoint" From 24dcdb34e0da59d65128a1c483fec9cc3f3e76bf Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 24 Feb 2021 21:46:00 +0100 Subject: [PATCH 46/77] Fixed some minor bugs and typos. --- server/serverpi.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 7c92387..b92d7bc 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -15,10 +15,6 @@ UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME TEMP = 19 -client = None -handler = SubHandler() -sub = None -handle = None # This ua method is used to subscribe to a variable on # another server. @@ -45,6 +41,8 @@ def subscribe(parent, endpoint, qx, ix): sub = client.create_subscription(500, handler) handle = sub.subscribe_data_change(qxvar) time.sleep(0.1) + except: + return False return True @@ -62,6 +60,11 @@ def datachange_notification(self, node, val, data): def event_notification(self, event): print("Python: New event", event) +client = None +handler = SubHandler() +sub = None +handle = None + class ServerPI: @@ -91,7 +94,7 @@ async def go(self): endp = ua.Argument() endp.Name = "Endpoint" - endp.DataType = ua.NodeId(ua.ObjectIDs.String) + endp.DataType = ua.NodeId(ua.ObjectIds.String) endp.ValueRank = -1 endp.ArrayDimensions = [] endp.Description = ua.LocalizedText("Address to endpoint") From e9072d8cdbddd6bd23c3ae42a9ac71b345b62334 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 24 Feb 2021 21:54:47 +0100 Subject: [PATCH 47/77] Fixed folder structure. --- server/serverpi.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index b92d7bc..7d5370c 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -87,10 +87,8 @@ async def go(self): dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) lFolder = await server.nodes.objects.add_folder(idx, "Sensors") - device = await server.nodes.objects.add_object(idx, "ZeDevice", dev) - zobj = await server.nodes.objects.add_object(idx, "ZeObject") - zvar = await zobj.add_variable(idx, "qxTemperature", self.temp) + zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) endp = ua.Argument() endp.Name = "Endpoint" From 65f510bd8670750eb61e5376bab7f4ac3b032e99 Mon Sep 17 00:00:00 2001 From: yazdii Date: Wed, 24 Feb 2021 21:59:00 +0100 Subject: [PATCH 48/77] Manual Connection fixed --- uaxplorer/ui_methods/MainUI.py | 117 ++++++++++++--------------------- 1 file changed, 43 insertions(+), 74 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 41d5648..36ff324 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -13,17 +13,18 @@ from zeroconf import ServiceBrowser, Zeroconf import server_discovery as dsc import Ui_client as ui_c + import client_nodes as cl_node from client_nodes import StandardItem as StItem import navigating_nodes as nav import sys class Node_storage: - def __init__(self, server_name, node_name, node_id, standarditem): - self.server_name = server_name + def __init__(self, node_name, node_id, standarditem): self.node_name = node_name self.node_id = node_id self.standarditem = standarditem + class Ui_MainWindow(object): def setupUi(self, MainWindow): @@ -74,7 +75,7 @@ def setupUi(self, MainWindow): self.pairingbutton.setStyleSheet("color: rgb(0, 0, 0);") self.pairingbutton.setObjectName("pairButton") self.pairingbutton.hide() - self.pairingbutton.clicked.connect(self.linking_servers) + self.treeView = QtWidgets.QTreeView(self.centralwidget) @@ -140,6 +141,7 @@ def setupUi(self, MainWindow): self.rootNode = self.treeModel.invisibleRootItem() self.treeView.setModel(self.treeModel) self.treeView.doubleClicked.connect(self.getValueLeft) + ###### Right hand tree ####### self.right_treeView.setModel(self.treeModel) self.right_treeView.doubleClicked.connect(self.getValueRight) @@ -178,79 +180,59 @@ def retranslateUi(self, MainWindow): def closing_application(self): QtWidgets.qApp.quit() - def linking_servers(self): - server1 = None - server2 = None - for i in self.clients: - if(i.ROOT_NODE.checkState() == 2 and server1 == None): - server1 = i - if(i.ROOT_NODE.checkState() == 2 and i.server_name != server1.server_name): - server2 = i - print(server2.server_name) - print(server1.server_name) - def getValueLeft(self, val): node_name = val.data() bool_continue = True - root = val.parent() #The root node of the node index that the user double clicks - - while(root.parent().data() != None): #We loop till we get None as data (We're at the end of the hierarchy) - root = root.parent() - - for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview + for i in self.clients: if(i.server_name == node_name): for k in i.NODE_ID: - i.client.connect() for j in self.ROOT_CHILDREN_NODES: - + i.client.connect() children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] if(children_name == j.node_name): bool_continue = False break - i.client.disconnect() - for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview - if(root.data() == i.server_name): - for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name): - - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - for k in self.ROOT_CHILDREN_NODES: - if(children_name == k.node_name): - self.textBrowser.append("Children_name already exists!" + children_name + k.node_name) - bool_continue = False - break - i.client.disconnect() - - for i in self.clients: #We add the first children to the root node + for i in self.clients: + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name): + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + print(children_name) + for k in self.ROOT_CHILDREN_NODES: + if(children_name == k.node_name): + bool_continue = False + break + + for i in self.clients: if(i.server_name == node_name and bool_continue == True): for j in i.NODE_ID: - i.client.connect() children_name = i.client.get_node(j).get_browse_name().__dict__['Name'] qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) i.ROOT_NODE.appendRow(qtitem) - server_name = node_name - self.ROOT_CHILDREN_NODES.append(Node_storage(server_name,children_name, j, qtitem)) - i.client.disconnect() - - - for i in self.clients: #We add the first children to the first children of the root node (and etc) - if(root.data() == i.server_name): - for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name and bool_continue == True): - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) - j.standarditem.appendRow(qtitem) - server_name = self.treeView.selectedIndexes()[0].model().index(0, 0).data() - self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) - i.client.disconnect() + self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, j, qtitem)) + + + for i in self.clients: + for j in self.ROOT_CHILDREN_NODES: + + if(node_name == j.node_name and bool_continue == True): + + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + # printif(i.client.get_node(d).get_children() == 0): + + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + + qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + j.standarditem.appendRow(qtitem) + self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, d, qtitem)) + #self.treeView.selectedIndexes()[0].model().itemFromIndex(val).appendRow(StItem(children_name, 8, color=QtGui.QColor(180, 180, 180))) + def LinkValueLeft(self, val): if(val.parent()): @@ -307,31 +289,18 @@ def closing_right_window(self): def manual_connection(self): - manually_entered_server = self.lineEdit.text() - url = dsc.Server_Discovery() - url.get_servers() - servers = url.get_all_as_address() - - if manually_entered_server in servers: - self.textBrowser.append("Server:" + manually_entered_server + " found and connected.") - - else: - self.textBrowser.append("no server found") - - - - + adress = self.lineEdit.text() + self.clients.append(cl_node.Client_nodes(adress,adress)) + self.rootNode.appendRow(self.clients[-1].ROOT_NODE) + def discover_servers(self): if len(self.clients) > 0: self.clients.clear() self.treeModel.removeRows(0, self.treeModel.rowCount()) - if len(self.ROOT_CHILDREN_NODES) > 0: - self.ROOT_CHILDREN_NODES.clear() - url = dsc.Server_Discovery() url.get_servers() self.SERVER_ARR = url.get_all(0) From 475e4222dbac8be71d0a21e6c96936a03311b799 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 24 Feb 2021 22:00:59 +0100 Subject: [PATCH 49/77] Revert "Manual Connection fixed" This reverts commit 65f510bd8670750eb61e5376bab7f4ac3b032e99. --- uaxplorer/ui_methods/MainUI.py | 117 +++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 36ff324..41d5648 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -13,18 +13,17 @@ from zeroconf import ServiceBrowser, Zeroconf import server_discovery as dsc import Ui_client as ui_c - import client_nodes as cl_node from client_nodes import StandardItem as StItem import navigating_nodes as nav import sys class Node_storage: - def __init__(self, node_name, node_id, standarditem): + def __init__(self, server_name, node_name, node_id, standarditem): + self.server_name = server_name self.node_name = node_name self.node_id = node_id self.standarditem = standarditem - class Ui_MainWindow(object): def setupUi(self, MainWindow): @@ -75,7 +74,7 @@ def setupUi(self, MainWindow): self.pairingbutton.setStyleSheet("color: rgb(0, 0, 0);") self.pairingbutton.setObjectName("pairButton") self.pairingbutton.hide() - + self.pairingbutton.clicked.connect(self.linking_servers) self.treeView = QtWidgets.QTreeView(self.centralwidget) @@ -141,7 +140,6 @@ def setupUi(self, MainWindow): self.rootNode = self.treeModel.invisibleRootItem() self.treeView.setModel(self.treeModel) self.treeView.doubleClicked.connect(self.getValueLeft) - ###### Right hand tree ####### self.right_treeView.setModel(self.treeModel) self.right_treeView.doubleClicked.connect(self.getValueRight) @@ -180,59 +178,79 @@ def retranslateUi(self, MainWindow): def closing_application(self): QtWidgets.qApp.quit() + def linking_servers(self): + server1 = None + server2 = None + for i in self.clients: + if(i.ROOT_NODE.checkState() == 2 and server1 == None): + server1 = i + if(i.ROOT_NODE.checkState() == 2 and i.server_name != server1.server_name): + server2 = i + print(server2.server_name) + print(server1.server_name) + def getValueLeft(self, val): node_name = val.data() bool_continue = True - for i in self.clients: + root = val.parent() #The root node of the node index that the user double clicks + + while(root.parent().data() != None): #We loop till we get None as data (We're at the end of the hierarchy) + root = root.parent() + + for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview if(i.server_name == node_name): for k in i.NODE_ID: + i.client.connect() for j in self.ROOT_CHILDREN_NODES: - i.client.connect() + children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] if(children_name == j.node_name): bool_continue = False break + i.client.disconnect() - for i in self.clients: - for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name): - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - print(children_name) - for k in self.ROOT_CHILDREN_NODES: - if(children_name == k.node_name): - bool_continue = False - break - - for i in self.clients: + for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview + if(root.data() == i.server_name): + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name): + + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + for k in self.ROOT_CHILDREN_NODES: + if(children_name == k.node_name): + self.textBrowser.append("Children_name already exists!" + children_name + k.node_name) + bool_continue = False + break + i.client.disconnect() + + for i in self.clients: #We add the first children to the root node if(i.server_name == node_name and bool_continue == True): for j in i.NODE_ID: + i.client.connect() children_name = i.client.get_node(j).get_browse_name().__dict__['Name'] qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) i.ROOT_NODE.appendRow(qtitem) - self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, j, qtitem)) - - - for i in self.clients: - for j in self.ROOT_CHILDREN_NODES: - - if(node_name == j.node_name and bool_continue == True): - - i.client.connect() - for d in i.client.get_node(j.node_id).get_children(): - # printif(i.client.get_node(d).get_children() == 0): - - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - - qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) - j.standarditem.appendRow(qtitem) - self.ROOT_CHILDREN_NODES.append(Node_storage(children_name, d, qtitem)) - #self.treeView.selectedIndexes()[0].model().itemFromIndex(val).appendRow(StItem(children_name, 8, color=QtGui.QColor(180, 180, 180))) - + server_name = node_name + self.ROOT_CHILDREN_NODES.append(Node_storage(server_name,children_name, j, qtitem)) + i.client.disconnect() + + + for i in self.clients: #We add the first children to the first children of the root node (and etc) + if(root.data() == i.server_name): + for j in self.ROOT_CHILDREN_NODES: + if(node_name == j.node_name and bool_continue == True): + i.client.connect() + for d in i.client.get_node(j.node_id).get_children(): + children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + j.standarditem.appendRow(qtitem) + server_name = self.treeView.selectedIndexes()[0].model().index(0, 0).data() + self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) + i.client.disconnect() def LinkValueLeft(self, val): if(val.parent()): @@ -289,18 +307,31 @@ def closing_right_window(self): def manual_connection(self): + manually_entered_server = self.lineEdit.text() + url = dsc.Server_Discovery() + url.get_servers() + servers = url.get_all_as_address() + + if manually_entered_server in servers: + self.textBrowser.append("Server:" + manually_entered_server + " found and connected.") + - adress = self.lineEdit.text() - self.clients.append(cl_node.Client_nodes(adress,adress)) - self.rootNode.appendRow(self.clients[-1].ROOT_NODE) + else: + self.textBrowser.append("no server found") + + + + - def discover_servers(self): if len(self.clients) > 0: self.clients.clear() self.treeModel.removeRows(0, self.treeModel.rowCount()) + if len(self.ROOT_CHILDREN_NODES) > 0: + self.ROOT_CHILDREN_NODES.clear() + url = dsc.Server_Discovery() url.get_servers() self.SERVER_ARR = url.get_all(0) From e643f5e5afa7f9c0cedcd4a4463308bf8607270d Mon Sep 17 00:00:00 2001 From: yazdii Date: Wed, 24 Feb 2021 22:03:17 +0100 Subject: [PATCH 50/77] Updated Manual Connection --- uaxplorer/ui_methods/MainUI.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 41d5648..2421d59 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -307,18 +307,9 @@ def closing_right_window(self): def manual_connection(self): - manually_entered_server = self.lineEdit.text() - url = dsc.Server_Discovery() - url.get_servers() - servers = url.get_all_as_address() - - if manually_entered_server in servers: - self.textBrowser.append("Server:" + manually_entered_server + " found and connected.") - - - else: - self.textBrowser.append("no server found") - + adress = self.lineEdit.text() + self.clients.append(cl_node.Client_nodes(adress, adress)) + self.rootNode.appendRow(self.clients[-1].ROOT_NODE) From 1a0f1efddd05d67e23fcd11fbda5e3fdfaf0bad5 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Thu, 25 Feb 2021 15:50:50 +0100 Subject: [PATCH 51/77] Fixed manually added connections in the treeview. If children had the same name, it will now not confuse it. --- uaxplorer/ui_methods/MainUI.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 2421d59..27d388e 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -203,18 +203,18 @@ def getValueLeft(self, val): for k in i.NODE_ID: i.client.connect() for j in self.ROOT_CHILDREN_NODES: - - children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] - if(children_name == j.node_name): - bool_continue = False - break + if(i.server_name == j.server_name): + children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] + if(children_name == j.node_name): + bool_continue = False + break i.client.disconnect() for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview if(root.data() == i.server_name): for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name): - + if(node_name == j.node_name and root.data() == j.server_name): + i.client.connect() for d in i.client.get_node(j.node_id).get_children(): children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] @@ -241,14 +241,16 @@ def getValueLeft(self, val): for i in self.clients: #We add the first children to the first children of the root node (and etc) if(root.data() == i.server_name): + for j in self.ROOT_CHILDREN_NODES: - if(node_name == j.node_name and bool_continue == True): + if(node_name == j.node_name and root.data() == j.server_name and bool_continue == True): + i.client.connect() for d in i.client.get_node(j.node_id).get_children(): children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) j.standarditem.appendRow(qtitem) - server_name = self.treeView.selectedIndexes()[0].model().index(0, 0).data() + server_name = root.data() self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) i.client.disconnect() @@ -310,6 +312,7 @@ def manual_connection(self): adress = self.lineEdit.text() self.clients.append(cl_node.Client_nodes(adress, adress)) self.rootNode.appendRow(self.clients[-1].ROOT_NODE) + self.textBrowser.append("Manually added service: " + adress + " !Note: name will be set to the address:port!") From 943b7fb1c33c692a1da77528d460af96e23e8638 Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 26 Feb 2021 00:04:21 +0100 Subject: [PATCH 52/77] Find node names and node id in navigating_nodes now possible (Preparing for linking), MainUI fixes and clean up. --- uaxplorer/ui_methods/MainUI.py | 56 +++++--------------- uaxplorer/ui_methods/navigating_nodes.py | 65 +++++++++++++++--------- 2 files changed, 53 insertions(+), 68 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 27d388e..6b00094 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -145,10 +145,6 @@ def setupUi(self, MainWindow): self.right_treeView.doubleClicked.connect(self.getValueRight) - ## LINKING ## - self.left_server = None - self.right_server = None - ## VARIABLES ##### self.ROOT_CHILDREN_NODES = [] @@ -184,10 +180,22 @@ def linking_servers(self): for i in self.clients: if(i.ROOT_NODE.checkState() == 2 and server1 == None): server1 = i + self.textBrowser.append("Linked server 1: " + i.server_name) if(i.ROOT_NODE.checkState() == 2 and i.server_name != server1.server_name): server2 = i + self.textBrowser.append("Linked server 2: " + i.server_name) print(server2.server_name) print(server1.server_name) + + + + if(self.right_server != None and self.left_server != None): + server1_lst = list([j for i in self.left_server.NODE_MAP.values() for j in i]) + server2_lst = list([j for i in self.right_server.NODE_MAP.values() for j in i]) + result1 = filter(lambda x: 'ix' in x, server1_lst) + result2 = filter(lambda x: 'qx' in x, server2_lst) + print(list(result1)) + print(list(result2)) def getValueLeft(self, val): @@ -254,46 +262,6 @@ def getValueLeft(self, val): self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) i.client.disconnect() - def LinkValueLeft(self, val): - if(val.parent()): - SERVER_NAME = val.data() - - print(SERVER_NAME) - if(self.left_server == None and val.parent()): - for i in self.clients: - if(i.server_name == SERVER_NAME): - self.left_server = i - self.textBrowser.append("Linked server to the left to: " + SERVER_NAME) - else: - self.textBrowser.append("Unlinked server to the left for: " + SERVER_NAME) - self.left_server = None - - def getValueRight(self, val): - if(val.parent()): - SERVER_NAME = val.data() - - if(self.right_server == None and val.parent()): - for i in self.clients: - if(i.server_name == SERVER_NAME): - self.right_server = i - self.textBrowser.append("Linked server to the right to: " + SERVER_NAME) - else: - self.textBrowser.append("Unlinked server to the right for: " + SERVER_NAME) - self.right_server = None - #Left ix Pressure -> qx Pressure Right - #Left qx Temperature <- ix Temperature Right - if(self.right_server != None and self.left_server != None): - server1_lst = list([j for i in self.left_server.NODE_MAP.values() for j in i]) - server2_lst = list([j for i in self.right_server.NODE_MAP.values() for j in i]) - result1 = filter(lambda x: 'ix' in x, server1_lst) - result2 = filter(lambda x: 'qx' in x, server2_lst) - print(list(result1)) - print(list(result2)) - - # for i,j in itertools.zip_longest(list([j for i in self.right_server.NODE_MAP.values() for j in i]), - # list([j for i in self.left_server.NODE_MAP.values() for j in i])): - # print(i,j ) - def creating_right_window(self): MainWindow.resize(1300, 513) # resizing the window to be able to fit the new treeview diff --git a/uaxplorer/ui_methods/navigating_nodes.py b/uaxplorer/ui_methods/navigating_nodes.py index 7de0a8c..acaf7c7 100644 --- a/uaxplorer/ui_methods/navigating_nodes.py +++ b/uaxplorer/ui_methods/navigating_nodes.py @@ -16,53 +16,69 @@ class Navigating_nodes: def __init__(self, client): self.client = client - self.node_recursive_dict = list() + self.all_nodes = list() def get_root_nodes(self): TEMP_ARRAY = [] + for i in self.client.get_objects_node().get_children()[1:]: # Skipping first element as it is unnecessary, we grab all objects in a server - TEMP_ARRAY.append(i) return TEMP_ARRAY def get_children_nodes(self, object_array): #Gets the children nodes from the root node and adds them into a dictonary children_dict = {} - temp_dict = {} + for i in object_array: for j in self.client.get_node(i).get_children(): if i not in children_dict: children_dict[i] = list() children_dict[i].append(j) - if len(self.client.get_node(j).get_children()) > 0: + + return children_dict + + def get_children_nodes_name(self, object_array): #We get children name from the ROOT nodes and then run it recursively if necessary. + nodes_name = {} + temp_dict = {} + for i in object_array: #Check in the root dictionary. + if i not in nodes_name: #We append if the root children nodes isnt in our dictionary + nodes_name[i] = list() #Add the node id as key + nodes_name[i].append(self.client.get_node(i).get_browse_name().__dict__['Name']) #Add the variable name as value + for j in self.client.get_node(i).get_children(): #We check the children in root node + if j not in nodes_name: + nodes_name[j] = list() + nodes_name[j].append(self.client.get_node(j).get_browse_name().__dict__['Name']) + if len(self.client.get_node(j).get_children()) > 0: #If the children in the root node has children of its own, then we need to run it recursively if j not in temp_dict: temp_dict[j] = list() temp_dict[j].append(self.client.get_node(j).get_children()) - - if bool(temp_dict): - self.run_recursively(temp_dict) - return children_dict + #print(nodes_name) + if bool(temp_dict): #If we found children inside of children, we will run through it recursively till there is no more to check. + nodes_name = self.run_recursively(temp_dict, nodes_name) + + return nodes_name + - def run_recursively(self, map_dict): - + def run_recursively(self, map_dict, nodes_name): + nodes_name = nodes_name new_dict2 = {} - for key, values in map_dict.items(): + for key, values in map_dict.items(): #Loop through the dictionary and append it to nodes_name and if there is more children append it + # to our new recursive dict for value in values: for value1 in value: + if value1 not in nodes_name: + nodes_name[value1] = list() + nodes_name[value1].append(self.client.get_node(value1).get_browse_name().__dict__['Name']) if len(self.client.get_node(value1).get_children()) > 0: if value1 not in new_dict2: new_dict2[value1] = list() new_dict2[value1].append(self.client.get_node(value1).get_children()) - - if bool(new_dict2): - self.node_recursive_dict.append(map_dict) - self.node_recursive_dict.append(new_dict2) - self.run_recursively(new_dict2) - for i in self.node_recursive_dict: - for key, values in i.items(): - - print() + if bool(new_dict2): #If there is even more children, run it again! + self.run_recursively(new_dict2, nodes_name) + + return nodes_name + def get_rootnode_nodeid_from_name(self, root_array, children_list): @@ -74,6 +90,7 @@ def get_rootnode_nodeid_from_name(self, root_array, children_list): root_childrenid_dict[key] = list() if self.client.get_node(value).get_browse_name().__dict__['Name'] == i: root_childrenid_dict[key].append(value) + return root_childrenid_dict def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects : values that are only path values and converts to the name name_dict = {} @@ -88,10 +105,10 @@ def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects return name_dict -#client = Client("opc.tcp://192.168.10.196:4840") -#client.connect() -#navigating = Navigating_nodes(client) -#root_nodes = navigating.get_root_nodes() +client = Client("opc.tcp://192.168.10.196:4840") +client.connect() +navigating = Navigating_nodes(client) +print(navigating.get_children_nodes_name(navigating.get_root_nodes())) #children_nodes = navigating.get_children_nodes(root_nodes) #print(navigating.get_name_from_nodes(navigating.get_children_nodes(navigating.get_root_nodes()))) #navigating.get_rootnode_nodeid_from_name(children_nodes, ['qxTrue', 'ixTemperature', 'ixTime']) \ No newline at end of file From 8765f85701fcde730454e2bb95132b1e7a04440b Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 26 Feb 2021 21:17:31 +0100 Subject: [PATCH 53/77] Added method to methods object. --- server/serverpi.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/serverpi.py b/server/serverpi.py index 7d5370c..c85a754 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -6,7 +6,8 @@ import asyncio import random -from asyncua import ua, uamethod, Server +import time +from asyncua import ua, uamethod, Server, Client import announce_service as sa SERVER_NAME = "RaspPI OPC UA Server" @@ -87,6 +88,7 @@ async def go(self): dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) lFolder = await server.nodes.objects.add_folder(idx, "Sensors") + zobj = await server.nodes.objects.add_object(idx, "methods") zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) From d139cf469725e90f5808203efed50ee5aa7738fa Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 26 Feb 2021 22:10:10 +0100 Subject: [PATCH 54/77] Linking has now been added. --- .vscode/settings.json | 2 +- uaxplorer/ui_methods/MainUI.py | 40 ++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index eaefbe4..5993f5e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.linting.enabled": true, - "python.pythonPath": "/usr/sbin/python" + "python.pythonPath": "C:\\Users\\FKV\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe" } \ No newline at end of file diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 6b00094..deb0891 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -142,7 +142,7 @@ def setupUi(self, MainWindow): self.treeView.doubleClicked.connect(self.getValueLeft) ###### Right hand tree ####### self.right_treeView.setModel(self.treeModel) - self.right_treeView.doubleClicked.connect(self.getValueRight) + self.right_treeView.doubleClicked.connect(self.getValueLeft) ## VARIABLES ##### @@ -177,25 +177,41 @@ def closing_application(self): def linking_servers(self): server1 = None server2 = None + nav1 = None + nav2 = None for i in self.clients: if(i.ROOT_NODE.checkState() == 2 and server1 == None): server1 = i + i.client.connect() + n = nav.Navigating_nodes(i.client) + nav1 = n.get_children_nodes_name(n.get_root_nodes()) + i.client.disconnect() self.textBrowser.append("Linked server 1: " + i.server_name) if(i.ROOT_NODE.checkState() == 2 and i.server_name != server1.server_name): server2 = i + i.client.connect() + n = nav.Navigating_nodes(i.client) + nav2 = n.get_children_nodes_name(n.get_root_nodes()) + i.client.disconnect() self.textBrowser.append("Linked server 2: " + i.server_name) - print(server2.server_name) - print(server1.server_name) - + + + if(server2 != None and server1 != None): + for dict1_key, dict1_values in nav1.items(): + for value in dict1_values: + + for key2,values2 in nav2.items(): + + for value2 in values2: + + if value[2:] == value2[2:]: + print(value, value2, dict1_key, key2) + + server1.client.connect() + f = server1.client.get_objects_node().get_children() + for i in f: + print(server1.client.get_node(i).get_browse_name()) - - if(self.right_server != None and self.left_server != None): - server1_lst = list([j for i in self.left_server.NODE_MAP.values() for j in i]) - server2_lst = list([j for i in self.right_server.NODE_MAP.values() for j in i]) - result1 = filter(lambda x: 'ix' in x, server1_lst) - result2 = filter(lambda x: 'qx' in x, server2_lst) - print(list(result1)) - print(list(result2)) def getValueLeft(self, val): From a62f72ac1e6b89ff138943f66f32839c1af0ec0a Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Tue, 2 Mar 2021 18:36:15 +0100 Subject: [PATCH 55/77] A try to test the PI server. --- server/test_serverpi.py | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 server/test_serverpi.py diff --git a/server/test_serverpi.py b/server/test_serverpi.py new file mode 100644 index 0000000..91eb982 --- /dev/null +++ b/server/test_serverpi.py @@ -0,0 +1,78 @@ +from opcua import Client, ua +IP_ADDRESS = "192.168.0.132" + +SERVER_NAME = "RaspPI OPC UA Server" +FLAT_NAME = SERVER_NAME.replace(" ", "") +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840" +UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME + + +class SubHandler(object): + + """ + Subscription Handler. To receive events from server for a subscription + data_change and event methods are called directly from receiving thread. + Do not do expensive, slow or network operation there. Create another + thread if you need to do such a thing + """ + + 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) + +if __name__ == "__main__": + logging.basicConfig(level=logging.WARN) + #logger = logging.getLogger("KeepAlive") + #logger.setLevel(logging.DEBUG) + + client = Client("opc.tcp://"+IP_ADDRESS+":4840") + # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user + try: + client.connect() + client.load_type_definitions() # load definition of server specific structures/extension objects + + # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects + root = client.get_root_node() + print("Root node is: ", root) + objects = client.get_objects_node() + print("Objects node is: ", objects) + + # Node objects have methods to read and write node attributes as well as browse or populate address space + print("Children of root are: ", root.get_children()) + + # get a specific node knowing its node id + #var = client.get_node(ua.NodeId(1002, 2)) + #var = client.get_node("ns=3;i=2002") + #print(var) + #var.get_data_value() # get value of node as a DataValue object + #var.get_value() # get value of node as a python builtin + #var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type + #var.set_value(3.9) # set node value using implicit data type + + # gettting our namespace idx + idx = client.get_namespace_index(UA_NAMESPACE) + + # Now getting a variable node using its browse path + myvar = client.get_node() + print("myvar is: ", myvar) + + # subscribing to a variable node + handler = SubHandler() + sub = client.create_subscription(500, handler) + handle = sub.subscribe_data_change(myvar) + time.sleep(0.1) + + # we can also subscribe to events from server + sub.subscribe_events() + # sub.unsubscribe(handle) + # sub.delete() + + # calling a method on server + res = obj.call_method("{}:multiply".format(idx), 3, "klk") + print("method result is: ", res) + + embed() + finally: + client.disconnect() \ No newline at end of file From 16d6a13624e1ddd6eb000b02ebbb9ebb5b3b8b42 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Tue, 2 Mar 2021 18:37:59 +0100 Subject: [PATCH 56/77] Changed to use Node as input for subscribe method. --- server/serverpi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index c85a754..98e951a 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -100,13 +100,13 @@ async def go(self): endp.Description = ua.LocalizedText("Address to endpoint") qxvar = ua.Argument() qxvar.Name = "qx" - qxvar.DataType = ua.NodeId(ua.ObjectIds.String) + qxvar.DataType = ua.NodeId(ua.ObjectIds.Node) qxvar.ValueRank = -1 qxvar.ArrayDimensions = [] qxvar.Description = ua.LocalizedText("Output variable to connect to server.") ixvar = ua.Argument() ixvar.Name = "ix" - ixvar.DataType = ua.NodeId(ua.ObjectIds.String) + ixvar.DataType = ua.NodeId(ua.ObjectIds.Node) ixvar.ValueRank = -1 ixvar.ArrayDimensions = [] ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") From 1c19993a242b68008016a6fdab1894c5665eae82 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Tue, 2 Mar 2021 18:58:50 +0100 Subject: [PATCH 57/77] Changed to string usage for server nodes in subscribe method. --- server/serverpi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 98e951a..5658be2 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -100,13 +100,13 @@ async def go(self): endp.Description = ua.LocalizedText("Address to endpoint") qxvar = ua.Argument() qxvar.Name = "qx" - qxvar.DataType = ua.NodeId(ua.ObjectIds.Node) + qxvar.DataType = ua.NodeId(ua.ObjectIds.String) qxvar.ValueRank = -1 qxvar.ArrayDimensions = [] qxvar.Description = ua.LocalizedText("Output variable to connect to server.") ixvar = ua.Argument() ixvar.Name = "ix" - ixvar.DataType = ua.NodeId(ua.ObjectIds.Node) + ixvar.DataType = ua.NodeId(ua.ObjectIds.String) ixvar.ValueRank = -1 ixvar.ArrayDimensions = [] ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") @@ -128,6 +128,6 @@ async def go(self): if __name__ == "__main__": - sa.start_service_announcement() + sa.start_service_announcement(device_name="kfd347-server.", iport=4840) sp = ServerPI() asyncio.run(sp.go()) From 309ab9f099adcbd76be68ab4a96890310c463921 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 11:45:09 +0100 Subject: [PATCH 58/77] Fixed being able to call a remote method, added some test files and removed the client connect from serverpi. --- server/serverpi.py | 54 +++++----- server/serverpi2.py | 139 ++++++++++++++++++++++++ server/test_opcua_client.py | 68 ++++++++++++ server/test_serverpi.py | 188 ++++++++++++++++++++------------- server/test_serverpi_client.py | 59 +++++++++++ 5 files changed, 409 insertions(+), 99 deletions(-) create mode 100644 server/serverpi2.py create mode 100644 server/test_opcua_client.py create mode 100644 server/test_serverpi_client.py diff --git a/server/serverpi.py b/server/serverpi.py index 5658be2..5e896d7 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -12,8 +12,10 @@ SERVER_NAME = "RaspPI OPC UA Server" FLAT_NAME = SERVER_NAME.replace(" ", "") -SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840" +SERVER_PORT = 4840 +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:"+str(SERVER_PORT) UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME +DISCOVERY_NAME="_"+FLAT_NAME[:10]+"." TEMP = 19 @@ -27,25 +29,27 @@ # was successful or not. @uamethod def subscribe(parent, endpoint, qx, ix): - client = Client(endpoint) + print("Inside subscribe!") + #client = Client(str(endpoint)) try: - client.connect() - client.load_type_definitions() + print("Try to connect") + #client.connect() + #client.load_type_definitions() + print("Before get node") #root = client.get_root_node() #uri = "http://examples.freeopcua.github.io" #idx = client.get_namespace_index(uri) - qxvar = client.get_node(qx) + #qxvar = client.get_node(qx) + print("After get node") - sub = client.create_subscription(500, handler) - handle = sub.subscribe_data_change(qxvar) + #sub = client.create_subscription(500, handler) + #handle = sub.subscribe_data_change(qxvar) time.sleep(0.1) except: - return False - - return True + pass class SubHandler(): """ @@ -78,47 +82,48 @@ async def go(self): await server.init() server.set_endpoint(SERVER_ENDPOINT) server.set_server_name(SERVER_NAME) - server.set_security_policy([ - ua.SecurityPolicyType.NoSecurity, - ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, - ua.SecurityPolicyType.Basic256Sha256_Sign]) + #server.set_security_policy([ + # ua.SecurityPolicyType.NoSecurity, + # ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, + # ua.SecurityPolicyType.Basic256Sha256_Sign]) idx = await server.register_namespace(UA_NAMESPACE) - dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) + objects = server.nodes.objects + + #dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) - lFolder = await server.nodes.objects.add_folder(idx, "Sensors") - zobj = await server.nodes.objects.add_object(idx, "methods") + lFolder = await objects.add_folder(idx, "Sensors") + zobj = await objects.add_object(idx, "Methods") zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) endp = ua.Argument() endp.Name = "Endpoint" - endp.DataType = ua.NodeId(ua.ObjectIds.String) + endp.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. endp.ValueRank = -1 endp.ArrayDimensions = [] endp.Description = ua.LocalizedText("Address to endpoint") qxvar = ua.Argument() qxvar.Name = "qx" - qxvar.DataType = ua.NodeId(ua.ObjectIds.String) + qxvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. qxvar.ValueRank = -1 qxvar.ArrayDimensions = [] qxvar.Description = ua.LocalizedText("Output variable to connect to server.") ixvar = ua.Argument() ixvar.Name = "ix" - ixvar.DataType = ua.NodeId(ua.ObjectIds.String) + ixvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. ixvar.ValueRank = -1 ixvar.ArrayDimensions = [] ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") res = ua.Argument() res.Name = "Result" - res.DataType = ua.NodeId(ua.ObjectIds.Boolean) + res.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. res.ValueRank = -1 res.ArrayDimensions = [] res.Description = ua.LocalizedText("Result if variable was connected or not.") - - submethod = zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], [res]) + await zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], []) async with server: while True: @@ -128,6 +133,7 @@ async def go(self): if __name__ == "__main__": - sa.start_service_announcement(device_name="kfd347-server.", iport=4840) + print(DISCOVERY_NAME) + sa.start_service_announcement(device_name=DISCOVERY_NAME, iport=SERVER_PORT) sp = ServerPI() asyncio.run(sp.go()) diff --git a/server/serverpi2.py b/server/serverpi2.py new file mode 100644 index 0000000..73fa4a6 --- /dev/null +++ b/server/serverpi2.py @@ -0,0 +1,139 @@ +#/usr/bin/env python3 + +# This file contains a set of classes and methods to handle +# running a OPC UA server on a raspberry pi. + + +import asyncio +import random +import time +from asyncua import ua, uamethod, Server, Client +import announce_service as sa + +SERVER_NAME = "2RaspPI OPC UA Server" +FLAT_NAME = SERVER_NAME.replace(" ", "") +SERVER_PORT = 4841 +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:"+str(SERVER_PORT) +UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME +DISCOVERY_NAME="_"+FLAT_NAME[:10]+"." +TEMP = 19 + + +# This ua method is used to subscribe to a variable on +# another server. +# Inputs: +# endpoint(string): the path to server to subscribe from. +# qx(string): The variable to subscribe to +# ix(string): The variable to connect subscription to +# Returns a Boolean value, depending on if the subscription +# was successful or not. +@uamethod +def subscribe(parent, endpoint, qx, ix): + print("Inside subscribe!") + client = Client(endpoint.Value) + try: + print("Try to connect") + client.connect() + client.load_type_definitions() + print("Before get node") + + #root = client.get_root_node() + + #uri = "http://examples.freeopcua.github.io" + #idx = client.get_namespace_index(uri) + + qxvar = client.get_node(qx) + print("After get node") + + sub = client.create_subscription(500, handler) + handle = sub.subscribe_data_change(qxvar) + time.sleep(0.1) + except: + pass + +class SubHandler(): + """ + Subscription Handler. To receive events from server for a subscription + data_change and event methods are called directly from receiving thread. + Do not do expensive, slow or network operation there. Create another + thread if you need to do such a thing + """ + + 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) + +client = None +handler = SubHandler() +sub = None +handle = None + + +class ServerPI: + + def __init__(self): + self.temp = TEMP + self.clients = [] + + async def go(self): + server = Server() + await server.init() + server.set_endpoint(SERVER_ENDPOINT) + server.set_server_name(SERVER_NAME) + #server.set_security_policy([ + # ua.SecurityPolicyType.NoSecurity, + # ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, + # ua.SecurityPolicyType.Basic256Sha256_Sign]) + + idx = await server.register_namespace(UA_NAMESPACE) + + objects = server.nodes.objects + + #dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) + + lFolder = await objects.add_folder(idx, "Sensors") + zobj = await objects.add_object(idx, "Methods") + + zvar = await lFolder.add_variable(idx, "ixTemperature", self.temp) + + endp = ua.Argument() + endp.Name = "Endpoint" + endp.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + endp.ValueRank = -1 + endp.ArrayDimensions = [] + endp.Description = ua.LocalizedText("Address to endpoint") + qxvar = ua.Argument() + qxvar.Name = "qx" + qxvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + qxvar.ValueRank = -1 + qxvar.ArrayDimensions = [] + qxvar.Description = ua.LocalizedText("Output variable to connect to server.") + ixvar = ua.Argument() + ixvar.Name = "ix" + ixvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + ixvar.ValueRank = -1 + ixvar.ArrayDimensions = [] + ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") + res = ua.Argument() + res.Name = "Result" + res.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + res.ValueRank = -1 + res.ArrayDimensions = [] + res.Description = ua.LocalizedText("Result if variable was connected or not.") + + await zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], []) + + async with server: + while True: + await asyncio.sleep(0.1) + await zvar.write_value(self.temp) + self.temp = 19 + random.random() + + +if __name__ == "__main__": + print(DISCOVERY_NAME) + sa.start_service_announcement(device_name=DISCOVERY_NAME, iport=SERVER_PORT) + sp = ServerPI() + asyncio.run(sp.go()) diff --git a/server/test_opcua_client.py b/server/test_opcua_client.py new file mode 100644 index 0000000..dc797a9 --- /dev/null +++ b/server/test_opcua_client.py @@ -0,0 +1,68 @@ + +import asyncio +import logging + +from asyncua import Client + +logging.basicConfig(level=logging.INFO) +_logger = logging.getLogger('asyncua') + + +class SubHandler(object): + """ + Subscription Handler. To receive events from server for a subscription + data_change and event methods are called directly from receiving thread. + Do not do expensive, slow or network operation there. Create another + thread if you need to do such a thing + """ + def datachange_notification(self, node, val, data): + print("New data change event", node, val) + + def event_notification(self, event): + print("New event", event) + + +async def main(): + url = "opc.tcp://localhost:4840/freeopcua/server/" + async with Client(url=url) as client: + _logger.info("Root node is: %r", client.nodes.root) + _logger.info("Objects node is: %r", client.nodes.objects) + + # Node objects have methods to read and write node attributes as well as browse or populate address space + _logger.info("Children of root are: %r", await client.nodes.root.get_children()) + + uri = "http://examples.freeopcua.github.io" + idx = await client.get_namespace_index(uri) + _logger.info("index of our namespace is %s", idx) + # get a specific node knowing its node id + #var = client.get_node(ua.NodeId(1002, 2)) + #var = client.get_node("ns=3;i=2002") + #print(var) + #await var.read_data_value() # get value of node as a DataValue object + #await var.read_value() # get value of node as a python builtin + #await var.write_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type + #await var.write_value(3.9) # set node value using implicit data type + + # Now getting a variable node using its browse path + myvar = await client.nodes.root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"]) + obj = await client.nodes.root.get_child(["0:Objects", "2:MyObject"]) + _logger.info("myvar is: %r", myvar) + + # subscribing to a variable node + handler = SubHandler() + sub = await client.create_subscription(500, handler) + handle = await sub.subscribe_data_change(myvar) + await asyncio.sleep(0.1) + + # we can also subscribe to events from server + await sub.subscribe_events() + # await sub.unsubscribe(handle) + # await sub.delete() + + # calling a method on server + res = await obj.call_method("2:multiply", 3, "klk", 4) + _logger.info("method result is: %r", res) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/server/test_serverpi.py b/server/test_serverpi.py index 91eb982..020aea5 100644 --- a/server/test_serverpi.py +++ b/server/test_serverpi.py @@ -1,78 +1,116 @@ -from opcua import Client, ua -IP_ADDRESS = "192.168.0.132" +import asyncio + +from asyncua import ua, uamethod, Server + + +# method to be exposed through server +def func(parent, variant): + print("func method call with parameters: ", variant.Value) + ret = False + if variant.Value % 2 == 0: + ret = True + return [ua.Variant(ret, ua.VariantType.Boolean)] + + +# method to be exposed through server +async def func_async(parent, variant): + if variant.Value % 2 == 0: + print("Sleeping asynchronously for 1 second") + await asyncio.sleep(1) + else: + print("Not sleeping!") + + +# method to be exposed through server +# uses a decorator to automatically convert to and from variants + + +@uamethod +def multiply(parent, x, y, z): + print("multiply method call with parameters: ", x, y) + return x * y * z + + +@uamethod +async def multiply_async(parent, x, y): + sleep_time = x * y + print(f"Sleeping asynchronously for {x * y} seconds") + await asyncio.sleep(sleep_time) + + +async def main(): + # optional: setup logging + #logging.basicConfig(level=logging.WARN) + # logger = logging.getLogger("asyncua.address_space") + # logger.setLevel(logging.DEBUG) + # logger = logging.getLogger("asyncua.internal_server") + # logger.setLevel(logging.DEBUG) + # logger = logging.getLogger("asyncua.binary_server_asyncio") + # logger.setLevel(logging.DEBUG) + # logger = logging.getLogger("asyncua.uaprocessor") + # logger.setLevel(logging.DEBUG) + # logger = logging.getLogger("asyncua.subscription_service") + # logger.setLevel(logging.DEBUG) + + # now setup our server + server = Server() + await server.init() + # server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/") + server.set_endpoint("opc.tcp://0.0.0.0:4840") + server.set_server_name("FreeOpcUa Example Server") + + # setup our own namespace + uri = "http://examples.freeopcua.github.io" + idx = await server.register_namespace(uri) + + # get Objects node, this is where we should put our custom stuff + objects = server.nodes.objects + + # populating our address space + await objects.add_folder(idx, "myEmptyFolder") + myobj = await objects.add_object(idx, "MyObject") + myvar = await myobj.add_variable(idx, "MyVariable", 6.7) + await myvar.set_writable() # Set MyVariable to be writable by clients + myarrayvar = await myobj.add_variable(idx, "myarrayvar", [6.7, 7.9]) + await myobj.add_variable( + idx, "myStronglytTypedVariable", ua.Variant([], ua.VariantType.UInt32) + ) + await myobj.add_property(idx, "myproperty", "I am a property") + await myobj.add_method(idx, "mymethod", func, [ua.VariantType.Int64], [ua.VariantType.Boolean]) + + inargx = ua.Argument() + inargx.Name = "x" + inargx.DataType = ua.NodeId(ua.ObjectIds.Int64) + inargx.ValueRank = -1 + inargx.ArrayDimensions = [] + inargx.Description = ua.LocalizedText("First number x") + inargy = ua.Argument() + inargy.Name = "y" + inargy.DataType = ua.NodeId(ua.ObjectIds.Int64) + inargy.ValueRank = -1 + inargy.ArrayDimensions = [] + inargy.Description = ua.LocalizedText("Second number y") + inargz = ua.Argument() + inargz.Name = "z" + inargz.DataType = ua.NodeId(ua.ObjectIds.Int64) + inargz.ValueRank = -1 + inargz.ArrayDimensions = [] + inargz.Description = ua.LocalizedText("Third number z") + outarg = ua.Argument() + outarg.Name = "Result" + outarg.DataType = ua.NodeId(ua.ObjectIds.Int64) + outarg.ValueRank = -1 + outarg.ArrayDimensions = [] + outarg.Description = ua.LocalizedText("Multiplication result") + + await myobj.add_method(idx, "multiply", multiply, [inargx, inargy, inargz], [outarg]) + await myobj.add_method(idx, "multiply_async", multiply_async, [inargx, inargy], []) + await myobj.add_method(idx, "func_async", func_async, [ua.VariantType.Int64], []) + + async with server: + while True: + await asyncio.sleep(1) -SERVER_NAME = "RaspPI OPC UA Server" -FLAT_NAME = SERVER_NAME.replace(" ", "") -SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840" -UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME - - -class SubHandler(object): - - """ - Subscription Handler. To receive events from server for a subscription - data_change and event methods are called directly from receiving thread. - Do not do expensive, slow or network operation there. Create another - thread if you need to do such a thing - """ - - 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) if __name__ == "__main__": - logging.basicConfig(level=logging.WARN) - #logger = logging.getLogger("KeepAlive") - #logger.setLevel(logging.DEBUG) - - client = Client("opc.tcp://"+IP_ADDRESS+":4840") - # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user - try: - client.connect() - client.load_type_definitions() # load definition of server specific structures/extension objects - - # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects - root = client.get_root_node() - print("Root node is: ", root) - objects = client.get_objects_node() - print("Objects node is: ", objects) - - # Node objects have methods to read and write node attributes as well as browse or populate address space - print("Children of root are: ", root.get_children()) - - # get a specific node knowing its node id - #var = client.get_node(ua.NodeId(1002, 2)) - #var = client.get_node("ns=3;i=2002") - #print(var) - #var.get_data_value() # get value of node as a DataValue object - #var.get_value() # get value of node as a python builtin - #var.set_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type - #var.set_value(3.9) # set node value using implicit data type - - # gettting our namespace idx - idx = client.get_namespace_index(UA_NAMESPACE) - - # Now getting a variable node using its browse path - myvar = client.get_node() - print("myvar is: ", myvar) - - # subscribing to a variable node - handler = SubHandler() - sub = client.create_subscription(500, handler) - handle = sub.subscribe_data_change(myvar) - time.sleep(0.1) - - # we can also subscribe to events from server - sub.subscribe_events() - # sub.unsubscribe(handle) - # sub.delete() - - # calling a method on server - res = obj.call_method("{}:multiply".format(idx), 3, "klk") - print("method result is: ", res) - - embed() - finally: - client.disconnect() \ No newline at end of file + asyncio.run(main()) diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py new file mode 100644 index 0000000..70c1f4c --- /dev/null +++ b/server/test_serverpi_client.py @@ -0,0 +1,59 @@ +import asyncio + +from asyncua import Client + +SERVER_NAME = "RaspPI OPC UA Server" +FLAT_NAME = SERVER_NAME.replace(" ", "") +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:4840" +UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME +TEMP = 19 + +class SubHandler(object): + """ + Subscription Handler. To receive events from server for a subscription + data_change and event methods are called directly from receiving thread. + Do not do expensive, slow or network operation there. Create another + thread if you need to do such a thing + """ + def datachange_notification(self, node, val, data): + print("New data change event", node, val) + + def event_notification(self, event): + print("New event", event) + + +async def main(): + url = "opc.tcp://0.0.0.0:4840" + async with Client(url=url) as client: + #uri = "UA_NAMESPACE" + #idx = await client.get_namespace_index(uri) + # get a specific node knowing its node id + #var = client.get_node(ua.NodeId(1002, 2)) + #var = client.get_node("ns=3;i=2002") + #print(var) + #await var.read_data_value() # get value of node as a DataValue object + #await var.read_value() # get value of node as a python builtin + #await var.write_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type + #await var.write_value(3.9) # set node value using implicit data type + + # Now getting a variable node using its browse path + myvar = await client.nodes.root.get_child(["0:Objects", "2:Sensors", "2:qxTemperature"]) + obj = await client.nodes.root.get_child(["0:Objects", "2:Methods"]) + # subscribing to a variable node + handler = SubHandler() + sub = await client.create_subscription(500, handler) + handle = await sub.subscribe_data_change(myvar) + await asyncio.sleep(0.1) + + # we can also subscribe to events from server + await sub.subscribe_events() + # await sub.unsubscribe(handle) + # await sub.delete() + print("Here be calling da meffod!") + # calling a method on server + await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "qxtemp", "ixtemp") + print("Hejsan res") + + +if __name__ == "__main__": + asyncio.run(main()) From 22f0635a2ef2b081a760c60ab464e07bffea4684 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 11:50:31 +0100 Subject: [PATCH 59/77] Now able to connect to remote client from server. --- server/serverpi.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 5e896d7..0d1d986 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -28,13 +28,13 @@ # Returns a Boolean value, depending on if the subscription # was successful or not. @uamethod -def subscribe(parent, endpoint, qx, ix): +async def subscribe(parent, endpoint, qx, ix): print("Inside subscribe!") - #client = Client(str(endpoint)) + client = Client(str(endpoint)) try: print("Try to connect") - #client.connect() - #client.load_type_definitions() + await client.connect() + client.load_type_definitions() print("Before get node") #root = client.get_root_node() @@ -48,6 +48,7 @@ def subscribe(parent, endpoint, qx, ix): #sub = client.create_subscription(500, handler) #handle = sub.subscribe_data_change(qxvar) time.sleep(0.1) + await client.disconnect() except: pass From 1c5c29dfb2d5b5d7870a9b0eeed633f58ab92f38 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 12:19:05 +0100 Subject: [PATCH 60/77] Subscription to data variable working. --- server/serverpi.py | 17 ++++++++++++----- server/serverpi2.py | 3 ++- server/test_serverpi_client.py | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 0d1d986..13324ee 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -41,14 +41,18 @@ async def subscribe(parent, endpoint, qx, ix): #uri = "http://examples.freeopcua.github.io" #idx = client.get_namespace_index(uri) - - #qxvar = client.get_node(qx) + print("This is qx: ", qx) + qxvar = client.get_node(qx) print("After get node") - #sub = client.create_subscription(500, handler) - #handle = sub.subscribe_data_change(qxvar) + print("Creating subscription here.") + sub = await client.create_subscription(500, handler) + print("Trying to subscribe to variable here!") + handle = await sub.subscribe_data_change(qxvar) time.sleep(0.1) - await client.disconnect() + #await client.disconnect() + #print("Disconnected from client!") + print("end of sub method but not disconnected") except: pass @@ -95,9 +99,12 @@ async def go(self): #dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) lFolder = await objects.add_folder(idx, "Sensors") + print("Sensors folder: ", lFolder) zobj = await objects.add_object(idx, "Methods") + print("Methods object:", zobj) zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) + print("Temp var: ", zvar) endp = ua.Argument() endp.Name = "Endpoint" diff --git a/server/serverpi2.py b/server/serverpi2.py index 73fa4a6..ebcecc5 100644 --- a/server/serverpi2.py +++ b/server/serverpi2.py @@ -127,9 +127,10 @@ async def go(self): async with server: while True: - await asyncio.sleep(0.1) + await asyncio.sleep(2) await zvar.write_value(self.temp) self.temp = 19 + random.random() + print(self.temp) if __name__ == "__main__": diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py index 70c1f4c..e1b196a 100644 --- a/server/test_serverpi_client.py +++ b/server/test_serverpi_client.py @@ -51,7 +51,7 @@ async def main(): # await sub.delete() print("Here be calling da meffod!") # calling a method on server - await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "qxtemp", "ixtemp") + await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ixtemp") print("Hejsan res") From 2b3f354c4c80e3a91018a838d84b41b5b06ca376 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 14:41:55 +0100 Subject: [PATCH 61/77] Some clean up of duplicate code and added methods to main class. --- server/serverpi.py | 110 +++++++++++++++++++------------------------- server/serverpi2.py | 2 +- 2 files changed, 48 insertions(+), 64 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 13324ee..8d12346 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -19,42 +19,6 @@ TEMP = 19 -# This ua method is used to subscribe to a variable on -# another server. -# Inputs: -# endpoint(string): the path to server to subscribe from. -# qx(string): The variable to subscribe to -# ix(string): The variable to connect subscription to -# Returns a Boolean value, depending on if the subscription -# was successful or not. -@uamethod -async def subscribe(parent, endpoint, qx, ix): - print("Inside subscribe!") - client = Client(str(endpoint)) - try: - print("Try to connect") - await client.connect() - client.load_type_definitions() - print("Before get node") - - #root = client.get_root_node() - - #uri = "http://examples.freeopcua.github.io" - #idx = client.get_namespace_index(uri) - print("This is qx: ", qx) - qxvar = client.get_node(qx) - print("After get node") - - print("Creating subscription here.") - sub = await client.create_subscription(500, handler) - print("Trying to subscribe to variable here!") - handle = await sub.subscribe_data_change(qxvar) - time.sleep(0.1) - #await client.disconnect() - #print("Disconnected from client!") - print("end of sub method but not disconnected") - except: - pass class SubHandler(): """ @@ -80,7 +44,48 @@ class ServerPI: def __init__(self): self.temp = TEMP - self.clients = [] + self.clients = {} + + # This ua method is used to subscribe to a variable on + # another server. + # Inputs: + # endpoint(Server url as string): the path to server to subscribe from. + # qx(NodeId in string format): The variable to subscribe to + # ix(NodeId in string fromat): The variable to connect subscription to + # Returns void. + @uamethod + async def subscribe(self, parent, endpoint, qx, ix): + server = str(endpoint) + if server not in self.clients: + try: + print("Try to connect") + client = Client(server) + await client.connect() + await client.load_data_type_definitions() + self.clients[server] = (client,set()) + except: + pass + else: + client = self.clients[server][0] + + #root = client.get_root_node( + #uri = "http://examples.freeopcua.github.io" + #idx = client.get_namespace_index(uri) + qxvar = client.get_node(qx) + if qx not in self.clients[server][1]: + self.clients[server][1].add(qx) + print(len(self.clients[server][1])) + sub = await client.create_subscription(500, handler) + handle = await sub.subscribe_data_change(qxvar) + time.sleep(0.1) + + def method_var(self, name, description): + arg = ua.Argument() + arg.Name = name + arg.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + endp.ValueRank = -1 + endp.ArrayDimensions = [] + endp.Description = ua.LocalizedText(description) async def go(self): server = Server() @@ -106,32 +111,11 @@ async def go(self): zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) print("Temp var: ", zvar) - endp = ua.Argument() - endp.Name = "Endpoint" - endp.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - endp.ValueRank = -1 - endp.ArrayDimensions = [] - endp.Description = ua.LocalizedText("Address to endpoint") - qxvar = ua.Argument() - qxvar.Name = "qx" - qxvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - qxvar.ValueRank = -1 - qxvar.ArrayDimensions = [] - qxvar.Description = ua.LocalizedText("Output variable to connect to server.") - ixvar = ua.Argument() - ixvar.Name = "ix" - ixvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - ixvar.ValueRank = -1 - ixvar.ArrayDimensions = [] - ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") - res = ua.Argument() - res.Name = "Result" - res.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - res.ValueRank = -1 - res.ArrayDimensions = [] - res.Description = ua.LocalizedText("Result if variable was connected or not.") - - await zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], []) + endp = self.method_var("Endpoint", "Address to tendpoint") + qxvar = self.method_var("qx", "Output variable to connect to server.") + ixvar = self.method_var("ix", "Input variable that is to be connected to.") + + await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], []) async with server: while True: diff --git a/server/serverpi2.py b/server/serverpi2.py index ebcecc5..b40a709 100644 --- a/server/serverpi2.py +++ b/server/serverpi2.py @@ -127,7 +127,7 @@ async def go(self): async with server: while True: - await asyncio.sleep(2) + await asyncio.sleep(5) await zvar.write_value(self.temp) self.temp = 19 + random.random() print(self.temp) From 2a01fa2060d68334472265c22cac7062a57ed7b2 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 16:02:02 +0100 Subject: [PATCH 62/77] Fixed some small typos. --- server/serverpi.py | 19 ++++++++++++++----- server/test_serverpi_client.py | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 8d12346..950dcaa 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -60,17 +60,23 @@ async def subscribe(self, parent, endpoint, qx, ix): try: print("Try to connect") client = Client(server) + print("Created Client") await client.connect() + print("Connected") await client.load_data_type_definitions() + print("loaded data etc...") self.clients[server] = (client,set()) + print("Connected") except: - pass + return "Could not reach the server specified." else: + print("Already connected") client = self.clients[server][0] #root = client.get_root_node( #uri = "http://examples.freeopcua.github.io" #idx = client.get_namespace_index(uri) + qxvar = client.get_node(qx) if qx not in self.clients[server][1]: self.clients[server][1].add(qx) @@ -78,14 +84,16 @@ async def subscribe(self, parent, endpoint, qx, ix): sub = await client.create_subscription(500, handler) handle = await sub.subscribe_data_change(qxvar) time.sleep(0.1) + return "Successfully subscribed to the specified variable!" def method_var(self, name, description): arg = ua.Argument() arg.Name = name arg.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - endp.ValueRank = -1 - endp.ArrayDimensions = [] - endp.Description = ua.LocalizedText(description) + arg.ValueRank = -1 + arg.ArrayDimensions = [] + arg.Description = ua.LocalizedText(description) + return arg async def go(self): server = Server() @@ -114,8 +122,9 @@ async def go(self): endp = self.method_var("Endpoint", "Address to tendpoint") qxvar = self.method_var("qx", "Output variable to connect to server.") ixvar = self.method_var("ix", "Input variable that is to be connected to.") + ret = self.method_var("ret", "Return message for information of what happend.") - await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], []) + await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], [ret]) async with server: while True: diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py index e1b196a..2f8b6e3 100644 --- a/server/test_serverpi_client.py +++ b/server/test_serverpi_client.py @@ -51,8 +51,8 @@ async def main(): # await sub.delete() print("Here be calling da meffod!") # calling a method on server - await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ixtemp") - print("Hejsan res") + ret = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ixtemp") + print(ret) if __name__ == "__main__": From 68e983f8c8bb704df80db10384d0e128dbfcba77 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 16:21:24 +0100 Subject: [PATCH 63/77] Swapped variable names of serverpi and serverpi2 fo the the qx/ixTemperature. --- server/serverpi.py | 8 +------- server/serverpi2.py | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 950dcaa..dace09c 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -58,19 +58,13 @@ async def subscribe(self, parent, endpoint, qx, ix): server = str(endpoint) if server not in self.clients: try: - print("Try to connect") client = Client(server) - print("Created Client") await client.connect() - print("Connected") await client.load_data_type_definitions() - print("loaded data etc...") self.clients[server] = (client,set()) - print("Connected") except: return "Could not reach the server specified." else: - print("Already connected") client = self.clients[server][0] #root = client.get_root_node( @@ -116,7 +110,7 @@ async def go(self): zobj = await objects.add_object(idx, "Methods") print("Methods object:", zobj) - zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) + zvar = await lFolder.add_variable(idx, "ixTemperature", self.temp) print("Temp var: ", zvar) endp = self.method_var("Endpoint", "Address to tendpoint") diff --git a/server/serverpi2.py b/server/serverpi2.py index b40a709..b4040a9 100644 --- a/server/serverpi2.py +++ b/server/serverpi2.py @@ -96,7 +96,7 @@ async def go(self): lFolder = await objects.add_folder(idx, "Sensors") zobj = await objects.add_object(idx, "Methods") - zvar = await lFolder.add_variable(idx, "ixTemperature", self.temp) + zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) endp = ua.Argument() endp.Name = "Endpoint" From 96911bb35bc64e2534eea0a77aa29b5f04d7175b Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 3 Mar 2021 16:23:02 +0100 Subject: [PATCH 64/77] Linking and pairing has been added now. --- uaxplorer/ui_methods/MainUI.py | 40 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index deb0891..10232a5 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -199,20 +199,35 @@ def linking_servers(self): if(server2 != None and server1 != None): for dict1_key, dict1_values in nav1.items(): for value in dict1_values: - for key2,values2 in nav2.items(): - for value2 in values2: - if value[2:] == value2[2:]: - print(value, value2, dict1_key, key2) - - server1.client.connect() - f = server1.client.get_objects_node().get_children() - for i in f: - print(server1.client.get_node(i).get_browse_name()) - + #print(value, value2) + if 'ix' in value and 'qx' in value2: + server1.client.connect() + get_obj = server1.client.get_objects_node().get_children() + for i in get_obj: + function_name = server1.client.get_node(i).get_display_name()._text + print(function_name) + if('Methods' == function_name): + bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server2.Server, str(dict1_key), str(key2)) + print(bl, "Hi") + server1.client.disconnect() + if 'qx' in value and 'ix' in value2: + server2.client.connect() + get_obj = server2.client.get_objects_node().get_children() + for i in get_obj: + function_name = server2.client.get_node(i).get_display_name()._text + + if('Methods' == function_name): + bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server1.Server, str(key2), str(dict1_key)) + server2.client.disconnect() + #server1.client.connect() + #f = server1.client.get_objects_node().get_children() + #for i in f: + # kkk = server1.client.get_node(i).get_display_name().__dict__['_text'] + # print(server1.client.get_node(i).get_display_name()._text) def getValueLeft(self, val): node_name = val.data() @@ -238,13 +253,12 @@ def getValueLeft(self, val): if(root.data() == i.server_name): for j in self.ROOT_CHILDREN_NODES: if(node_name == j.node_name and root.data() == j.server_name): - i.client.connect() for d in i.client.get_node(j.node_id).get_children(): children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] for k in self.ROOT_CHILDREN_NODES: - if(children_name == k.node_name): - self.textBrowser.append("Children_name already exists!" + children_name + k.node_name) + if(children_name == k.node_name and root.data() == k.server_name): + self.textBrowser.append("Children_name already exists! " + children_name + " " + k.node_name) bool_continue = False break i.client.disconnect() From 26e958b7cf12c306c2a4f728ae61f7b679fa6513 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 16:25:51 +0100 Subject: [PATCH 65/77] Added some shit to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 8491e74..7e4945c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ server/__pycache__ uaxplorer/ui_methods/__pycache__ +uaxplorer/ui_methods/__pycache__/discovery.cpython-39.pyc +uaxplorer/ui_methods/__pycache__/server_discovery.cpython-39.pyc From dbecc1bbccd076e24b7083b125e73a041534fa45 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Wed, 3 Mar 2021 16:49:39 +0100 Subject: [PATCH 66/77] Made serverpi2 updated to the latest and also fixed the test client. --- server/serverpi2.py | 113 ++++++++++++++++----------------- server/test_serverpi_client.py | 2 +- 2 files changed, 54 insertions(+), 61 deletions(-) diff --git a/server/serverpi2.py b/server/serverpi2.py index b4040a9..195380f 100644 --- a/server/serverpi2.py +++ b/server/serverpi2.py @@ -18,39 +18,6 @@ DISCOVERY_NAME="_"+FLAT_NAME[:10]+"." TEMP = 19 - -# This ua method is used to subscribe to a variable on -# another server. -# Inputs: -# endpoint(string): the path to server to subscribe from. -# qx(string): The variable to subscribe to -# ix(string): The variable to connect subscription to -# Returns a Boolean value, depending on if the subscription -# was successful or not. -@uamethod -def subscribe(parent, endpoint, qx, ix): - print("Inside subscribe!") - client = Client(endpoint.Value) - try: - print("Try to connect") - client.connect() - client.load_type_definitions() - print("Before get node") - - #root = client.get_root_node() - - #uri = "http://examples.freeopcua.github.io" - #idx = client.get_namespace_index(uri) - - qxvar = client.get_node(qx) - print("After get node") - - sub = client.create_subscription(500, handler) - handle = sub.subscribe_data_change(qxvar) - time.sleep(0.1) - except: - pass - class SubHandler(): """ Subscription Handler. To receive events from server for a subscription @@ -75,7 +42,50 @@ class ServerPI: def __init__(self): self.temp = TEMP - self.clients = [] + self.clients = {} + + # This ua method is used to subscribe to a variable on + # another server. + # Inputs: + # endpoint(Server url as string): the path to server to subscribe from. + # qx(NodeId in string format): The variable to subscribe to + # ix(NodeId in string fromat): The variable to connect subscription to + # Returns void. + @uamethod + async def subscribe(self, parent, endpoint, qx, ix): + server = str(endpoint) + if server not in self.clients: + try: + client = Client(server) + await client.connect() + await client.load_data_type_definitions() + self.clients[server] = (client,set()) + except: + return "Could not reach the server specified." + else: + client = self.clients[server][0] + + #root = client.get_root_node( + #uri = "http://examples.freeopcua.github.io" + #idx = client.get_namespace_index(uri) + + qxvar = client.get_node(qx) + if qx not in self.clients[server][1]: + self.clients[server][1].add(qx) + print(len(self.clients[server][1])) + sub = await client.create_subscription(500, handler) + handle = await sub.subscribe_data_change(qxvar) + time.sleep(0.1) + return "Successfully subscribed to the specified variable!" + + def method_var(self, name, description): + arg = ua.Argument() + arg.Name = name + arg.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. + arg.ValueRank = -1 + arg.ArrayDimensions = [] + arg.Description = ua.LocalizedText(description) + return arg async def go(self): server = Server() @@ -94,36 +104,19 @@ async def go(self): #dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) lFolder = await objects.add_folder(idx, "Sensors") + print("Sensors folder: ", lFolder) zobj = await objects.add_object(idx, "Methods") + print("Methods object:", zobj) zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) + print("Temp var: ", zvar) + + endp = self.method_var("Endpoint", "Address to tendpoint") + qxvar = self.method_var("qx", "Output variable to connect to server.") + ixvar = self.method_var("ix", "Input variable that is to be connected to.") + ret = self.method_var("ret", "Return message for information of what happend.") - endp = ua.Argument() - endp.Name = "Endpoint" - endp.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - endp.ValueRank = -1 - endp.ArrayDimensions = [] - endp.Description = ua.LocalizedText("Address to endpoint") - qxvar = ua.Argument() - qxvar.Name = "qx" - qxvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - qxvar.ValueRank = -1 - qxvar.ArrayDimensions = [] - qxvar.Description = ua.LocalizedText("Output variable to connect to server.") - ixvar = ua.Argument() - ixvar.Name = "ix" - ixvar.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - ixvar.ValueRank = -1 - ixvar.ArrayDimensions = [] - ixvar. Description = ua.LocalizedText("Input variable that is to be connected to.") - res = ua.Argument() - res.Name = "Result" - res.DataType = ua.NodeId(ua.ObjectIds.Int64) #NodeId, and not datatype of value. We use Int64 ID's. - res.ValueRank = -1 - res.ArrayDimensions = [] - res.Description = ua.LocalizedText("Result if variable was connected or not.") - - await zobj.add_method(idx, "subscribe", subscribe, [endp, qxvar, ixvar], []) + await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], [ret]) async with server: while True: diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py index 2f8b6e3..cee40ce 100644 --- a/server/test_serverpi_client.py +++ b/server/test_serverpi_client.py @@ -37,7 +37,7 @@ async def main(): #await var.write_value(3.9) # set node value using implicit data type # Now getting a variable node using its browse path - myvar = await client.nodes.root.get_child(["0:Objects", "2:Sensors", "2:qxTemperature"]) + myvar = await client.nodes.root.get_child(["0:Objects", "2:Sensors", "2:ixTemperature"]) obj = await client.nodes.root.get_child(["0:Objects", "2:Methods"]) # subscribing to a variable node handler = SubHandler() From 60491cf259c37c1087700f6171aa9588dd806a0b Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:01:17 +0100 Subject: [PATCH 67/77] Added text --- uaxplorer/ui_methods/MainUI.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 10232a5..ab300d6 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -211,7 +211,7 @@ def linking_servers(self): print(function_name) if('Methods' == function_name): bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server2.Server, str(dict1_key), str(key2)) - print(bl, "Hi") + self.textBrowser.append(bl) server1.client.disconnect() if 'qx' in value and 'ix' in value2: server2.client.connect() @@ -221,6 +221,7 @@ def linking_servers(self): if('Methods' == function_name): bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server1.Server, str(key2), str(dict1_key)) + self.textBrowser.append(bl) server2.client.disconnect() #server1.client.connect() From f64bc5e9fece6b1a0b71ca7bbd78ec719e596d2c Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Fri, 5 Mar 2021 18:46:24 +0100 Subject: [PATCH 68/77] Server bugs have been fixed, navigating_node clean up MainUI clean up. --- server/server.py | 110 ++++++++++++++--------- uaxplorer/ui_methods/MainUI.py | 2 + uaxplorer/ui_methods/navigating_nodes.py | 14 +-- 3 files changed, 72 insertions(+), 54 deletions(-) diff --git a/server/server.py b/server/server.py index e20a8df..3c96a2c 100644 --- a/server/server.py +++ b/server/server.py @@ -1,14 +1,19 @@ #!/usr/bin/env python -#Credits to http://courses.compute.dtu.dk/02619/software/opcda_to_opcua.py -#Helped with the conversion of OPCDA to OPCUA - -import sys, asyncio, OpenOPC, decimal, time, pywintypes +# Credits to http://courses.compute.dtu.dk/02619/software/opcda_to_opcua.py +# Helped with the conversion of OPCDA to OPCUA + +import sys +import asyncio +import OpenOPC +import decimal +import time +import pywintypes from datetime import datetime from asyncua import ua, Server, uamethod from zeroconf import ServiceInfo, Zeroconf import socket -#local imports +# local imports import announce_service as sa pywintypes.datetime = pywintypes.TimeType @@ -21,40 +26,56 @@ obj_in_node = {} -#Constants +# Constants ITEM_ACCESS_RIGHTS = 5 ACCESS_READ = 0 ACCESS_WRITE = 1 ACCESS_READ_WRITE = 2 ITEM_VALUE = 2 + class SubHandler(object): """ Subscription handler to receive events from the server. """ - def datachange_notification(self, node, val, data): - p_a_string = node.get_path_as_string() #Get a list containing root, objects, opc da server + def __init__(self, n, opcda_string): + self.i = 0 + self.opcda_string = opcda_string + self.n = n + + async def final_datachange_notification(self, node, val, data): + # Get a list containing root, objects, opc da server + p_a_string = await node.get_path(as_string=True) + print(p_a_string) + #print(da.servers()[0]) da_address = '.'.join([a.split(':')[1] for a in p_a_string[3:]]) + da = OpenOPC.client() - da.connect(OPCDA_SERVER_STRING) + + da.connect(self.opcda_string) print('Datachanged ', da_address, val) da.write((da_address, val,)) da.close() + def datachange_notification(self, node, val, data): + self.i = self.i + 1 + + if(self.i == self.n): + self.datachange_notification = self.final_datachange_notification def read_value(value): - value = value[0] - if isinstance(value,decimal.Decimal): - value = float(value) - elif isinstance(value,list): - if len(value) == 0: - value = None - elif isinstance(value,tuple): - if len(value) == 0: - value = None - - return value + value = value[0] + if isinstance(value,decimal.Decimal): + value = float(value) + elif isinstance(value,list): + if len(value) == 0: + value = None + elif isinstance(value,tuple): + if len(value) == 0: + value = None + + return value async def sort_nodes_list(list, idx, root, da): @@ -89,58 +110,59 @@ async def sort_nodes_list(list, idx, root, da): if obj_in_node[ITEM_ACCESS_RIGHTS] in [ACCESS_READ]: readable_variables[node] = opcua_node - #print(opcua_node) + # print(opcua_node) if obj_in_node[ITEM_ACCESS_RIGHTS] in [ACCESS_WRITE, ACCESS_READ_WRITE]: await opcua_node.set_writable() writeable_variables[node] = opcua_node - #print(opcua_node) + # print(opcua_node) async def main(): - #We connect to the OPC-DA server (I assume there will only be one, else this will have to be changed) - #We can also check if there is a server and if there isn't one we'll run only an OPC UA server without conversion + # We connect to the OPC-DA server (I assume there will only be one, else this will have to be changed) + # We can also check if there is a server and if there isn't one we'll run only an OPC UA server without conversion da = OpenOPC.client() OPCDA_SERVER_STRING = da.servers()[0] da.connect(OPCDA_SERVER_STRING) #Connect to the first server in the array print(OPCDA_SERVER_STRING) - #Setup the server for UA + # Setup the server for UA server = Server() await server.init() - server.set_endpoint('opc.tcp://0.0.0.0:4840/freeopcua/server/') + server.set_endpoint("opc.tcp://192.168.10.196:55341") + + server.set_server_name("SErverRER") + idx = await server.register_namespace(UA_URI) root = await server.nodes.objects.add_object(idx,OPCDA_SERVER_STRING) - #We want to find the OPC-DA server nodes in aliases + # We want to find the OPC-DA server nodes in aliases nodes_list = da.list('*', recursive=True) #A list of dot-delimited strings await sort_nodes_list(nodes_list, idx, root, da) - try: - async with server: #Starting the server + async with server: #Starting the server - handler = SubHandler() #Subscribing to datachanges coming from the UA clients - sub = await server.create_subscription(500, handler) - handle = await sub.subscribe_data_change(writeable_variables.values()) - #In Robotstudio all variables are writeable, so the readable variables are empty - #This should be changed when tried in a real environment, so temporary for now - readable_vars = list(writeable_variables.keys()) #readable_variables - #print(readable_vars) + handler = SubHandler(len(writeable_variables), OPCDA_SERVER_STRING) #Subscribing to datachanges coming from the UA clients + sub = await server.create_subscription(500, handler) + await sub.subscribe_data_change(writeable_variables.values()) + # In Robotstudio all variables are writeable, so the readable variables are empty + # This should be changed when tried in a real environment, so temporary for now + readable_vars = list(writeable_variables.keys()) #readable_variables + # print(readable_vars) while True: - await asyncio.sleep(1) + for i in da.read(readable_vars): print(i) da_id = i[0] var_handler = writeable_variables[da_id] # Due to change - var_handler.set_value(read_value(i[1:])) - + await var_handler.set_value(read_value(i[1:])) + await asyncio.sleep(1) + - finally: - await server.stop() - da.close() + if __name__ == "__main__": - sa.start_service_announcement(device_name="_adda-server.") - asyncio.run(main()) \ No newline at end of file + sa.start_service_announcement(device_name="_adda-server.", iport=55341) + asyncio.run(main()) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index ab300d6..005d4a7 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -328,7 +328,9 @@ def discover_servers(self): url = dsc.Server_Discovery() url.get_servers() self.SERVER_ARR = url.get_all(0) + print(self.SERVER_ARR) servers = url.get_all_as_address() + print(servers) j = 0 diff --git a/uaxplorer/ui_methods/navigating_nodes.py b/uaxplorer/ui_methods/navigating_nodes.py index acaf7c7..03ff9ba 100644 --- a/uaxplorer/ui_methods/navigating_nodes.py +++ b/uaxplorer/ui_methods/navigating_nodes.py @@ -23,7 +23,9 @@ def get_root_nodes(self): for i in self.client.get_objects_node().get_children()[1:]: # Skipping first element as it is unnecessary, we grab all objects in a server TEMP_ARRAY.append(i) - + + + #self.client.get_objects_node().get_children( return TEMP_ARRAY def get_children_nodes(self, object_array): #Gets the children nodes from the root node and adds them into a dictonary @@ -103,12 +105,4 @@ def get_name_from_nodes(self, dict_list): #Takes in a dictonary with the Objects name_dict[self.client.get_node(key).get_browse_name().__dict__['Name']].append(self.client.get_node(value).get_browse_name().__dict__['Name']) return name_dict - - -client = Client("opc.tcp://192.168.10.196:4840") -client.connect() -navigating = Navigating_nodes(client) -print(navigating.get_children_nodes_name(navigating.get_root_nodes())) -#children_nodes = navigating.get_children_nodes(root_nodes) -#print(navigating.get_name_from_nodes(navigating.get_children_nodes(navigating.get_root_nodes()))) -#navigating.get_rootnode_nodeid_from_name(children_nodes, ['qxTrue', 'ixTemperature', 'ixTime']) \ No newline at end of file + \ No newline at end of file From ea15c54e71bda55a83ff1a72d38f508998524ab7 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 5 Mar 2021 21:28:12 +0100 Subject: [PATCH 69/77] Fixed subscription handler to be more general and can handle multiple servers(clients). --- .vscode/settings.json | 2 +- server/serverpi.py | 56 +++++++++++++++++----------------- server/serverpi2.py | 14 ++++++++- server/test_serverpi_client.py | 8 ++++- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5993f5e..eaefbe4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "python.linting.enabled": true, - "python.pythonPath": "C:\\Users\\FKV\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe" + "python.pythonPath": "/usr/sbin/python" } \ No newline at end of file diff --git a/server/serverpi.py b/server/serverpi.py index dace09c..efa61a6 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -27,23 +27,24 @@ class SubHandler(): Do not do expensive, slow or network operation there. Create another thread if you need to do such a thing """ + def __init__(self, variables, client): + self.vars = variables + self.cl = client def datachange_notification(self, node, val, data): - print("Python: New data change event", node, val) + n = self.cl.get_node(self.vars[str(node)]) + n.write_value(val) + print("Python: New data change event", n, val) def event_notification(self, event): print("Python: New event", event) -client = None -handler = SubHandler() -sub = None -handle = None - - class ServerPI: def __init__(self): self.temp = TEMP + self.ixBall = TEMP + self.ixBarrel = TEMP self.clients = {} # This ua method is used to subscribe to a variable on @@ -55,28 +56,32 @@ def __init__(self): # Returns void. @uamethod async def subscribe(self, parent, endpoint, qx, ix): + # The client tuple consists of a Client object, a SubHandler object, + # a Subscription, a list of subscribed variables, and a dictionary of + # subscribed and connected variables as strings. server = str(endpoint) - if server not in self.clients: + if server not in self.clients.keys(): try: client = Client(server) + print("After Client(server)") await client.connect() + print("After client.connect()") await client.load_data_type_definitions() - self.clients[server] = (client,set()) + print("After client.loaddatattype") + tmpvariables = {} + tmphandler = SubHandler(tmpvariables, client) + tmpsubscription = await client.create_subscription(500, tmphandler) + self.clients[server] = (client, tmphandler, tmpsubscription, [], tmpvariables) except: return "Could not reach the server specified." else: client = self.clients[server][0] - #root = client.get_root_node( - #uri = "http://examples.freeopcua.github.io" - #idx = client.get_namespace_index(uri) - qxvar = client.get_node(qx) - if qx not in self.clients[server][1]: - self.clients[server][1].add(qx) - print(len(self.clients[server][1])) - sub = await client.create_subscription(500, handler) - handle = await sub.subscribe_data_change(qxvar) + if qx not in self.clients[server][4].keys(): + self.clients[server][4][qx] = ix + subbedvar = await self.clients[server][2].subscribe_data_change(qxvar) + self.clients[server][3].append(subbedvar) time.sleep(0.1) return "Successfully subscribed to the specified variable!" @@ -103,29 +108,24 @@ async def go(self): objects = server.nodes.objects - #dev = await server.nodes.base_object_type.add_object_type(idx, FLAT_NAME) - lFolder = await objects.add_folder(idx, "Sensors") - print("Sensors folder: ", lFolder) zobj = await objects.add_object(idx, "Methods") - print("Methods object:", zobj) - zvar = await lFolder.add_variable(idx, "ixTemperature", self.temp) - print("Temp var: ", zvar) + yvar = await lFolder.add_variable(idx, "ixBall", self.ixBall) + xvar = await lFolder.add_variable(idx, "ixBarrel", self.ixBarrel) + print(zvar) + print(yvar) + print(xvar) endp = self.method_var("Endpoint", "Address to tendpoint") qxvar = self.method_var("qx", "Output variable to connect to server.") ixvar = self.method_var("ix", "Input variable that is to be connected to.") ret = self.method_var("ret", "Return message for information of what happend.") - await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], [ret]) async with server: while True: await asyncio.sleep(0.1) - await zvar.write_value(self.temp) - self.temp = 19 + random.random() - if __name__ == "__main__": print(DISCOVERY_NAME) diff --git a/server/serverpi2.py b/server/serverpi2.py index 195380f..a9195ee 100644 --- a/server/serverpi2.py +++ b/server/serverpi2.py @@ -42,6 +42,8 @@ class ServerPI: def __init__(self): self.temp = TEMP + self.qxBall = True + self.qxBarrel = True self.clients = {} # This ua method is used to subscribe to a variable on @@ -108,8 +110,12 @@ async def go(self): zobj = await objects.add_object(idx, "Methods") print("Methods object:", zobj) + xvar = await lFolder.add_variable(idx, "qxBarrel", self.qxBarrel) + yvar = await lFolder.add_variable(idx, "qxBall", self.qxBall) zvar = await lFolder.add_variable(idx, "qxTemperature", self.temp) print("Temp var: ", zvar) + print("qxBall var:", yvar) + print("qxBarrel var:", xvar) endp = self.method_var("Endpoint", "Address to tendpoint") qxvar = self.method_var("qx", "Output variable to connect to server.") @@ -120,10 +126,16 @@ async def go(self): async with server: while True: - await asyncio.sleep(5) + await asyncio.sleep(1.2) await zvar.write_value(self.temp) + await yvar.write_value(self.qxBall) + await xvar.write_value(self.qxBarrel) self.temp = 19 + random.random() + self.qxBall = not self.qxBall + self.qxBarrel = not self.qxBarrel print(self.temp) + print(self.qxBall) + print(self.qxBarrel) if __name__ == "__main__": diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py index cee40ce..9784d10 100644 --- a/server/test_serverpi_client.py +++ b/server/test_serverpi_client.py @@ -51,8 +51,14 @@ async def main(): # await sub.delete() print("Here be calling da meffod!") # calling a method on server - ret = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ixtemp") + + ret = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ns=2;i=3") + ret2 = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=4", "ns=2;i=4") + ret3 = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=5", "ns=2;i=5") + print(ret) + print(ret2) + print(ret3) if __name__ == "__main__": From 0f62d04276a43075130050c309fbaf91e94da8f4 Mon Sep 17 00:00:00 2001 From: yazdii Date: Fri, 12 Mar 2021 18:33:58 +0100 Subject: [PATCH 70/77] Created Subscription button --- uaxplorer/ui_methods/MainUI.py | 111 +++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 11 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index 005d4a7..59924b4 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -10,14 +10,17 @@ from PyQt5 import QtCore, QtGui, QtWidgets, Qt +from PyQt5.QtWidgets import QMenu from zeroconf import ServiceBrowser, Zeroconf import server_discovery as dsc import Ui_client as ui_c import client_nodes as cl_node +from opcua import client from client_nodes import StandardItem as StItem import navigating_nodes as nav import sys + class Node_storage: def __init__(self, server_name, node_name, node_id, standarditem): self.server_name = server_name @@ -51,14 +54,26 @@ def setupUi(self, MainWindow): self.lineEdit.setStyleSheet("color: rgb(0, 0, 0);") self.lineEdit.setObjectName("lineEdit") - self.groupBox = QtWidgets.QGroupBox(self.centralwidget) - self.groupBox.setGeometry(QtCore.QRect(340, 20, 611, 331)) - self.groupBox.setFlat(False) - self.groupBox.setCheckable(False) - self.groupBox.setObjectName("groupBox") - self.groupBox.setStyleSheet("color: rgb(0,0,0);") + self.tableWidget = QtWidgets.QTableWidget(self.centralwidget) + self.tableWidget.setGeometry(QtCore.QRect(340, 26, 612, 325)) + self.tableWidget.setStyleSheet("color: rgb(0, 0, 0);") + self.tableWidget.setObjectName("tableWidget") + self.tableWidget.setColumnCount(6) + self.tableWidget.setRowCount(0) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(0, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(1, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(2, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(3, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(4, item) + item = QtWidgets.QTableWidgetItem() + self.tableWidget.setHorizontalHeaderItem(5, item) - + self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget) self.textBrowser.setGeometry(QtCore.QRect(10, 360, 942, 111)) @@ -80,6 +95,11 @@ def setupUi(self, MainWindow): self.treeView = QtWidgets.QTreeView(self.centralwidget) self.treeView.setGeometry(QtCore.QRect(10, 26, 331, 325)) self.treeView.setObjectName("treeView") + self.treeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.treeView.customContextMenuRequested.connect(self.contextMenuEvent) + + + self.right_treeView = QtWidgets.QTreeView(self.centralwidget) self.right_treeView.setGeometry(QtCore.QRect(951, 26, 331, 325)) @@ -92,7 +112,7 @@ def setupUi(self, MainWindow): self.textBrowser.raise_() self.pushButton.raise_() self.treeView.raise_() - self.groupBox.raise_() + self.tableWidget.raise_() MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 912, 21)) @@ -131,6 +151,8 @@ def setupUi(self, MainWindow): self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) + + ############################### Discovery ################################ @@ -143,7 +165,7 @@ def setupUi(self, MainWindow): ###### Right hand tree ####### self.right_treeView.setModel(self.treeModel) self.right_treeView.doubleClicked.connect(self.getValueLeft) - + ## VARIABLES ##### self.ROOT_CHILDREN_NODES = [] @@ -155,7 +177,19 @@ def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(_translate("MainWindow", "CustomOPC")) self.Connect.setText(_translate("MainWindow", "Connect")) self.Discover.setText(_translate("MainWindow", "Discover")) - self.groupBox.setTitle(_translate("MainWindow", "Attribute Panel")) + item = self.tableWidget.horizontalHeaderItem(0) + item.setText(_translate("MainWindow", "NodeId")) + item = self.tableWidget.horizontalHeaderItem(1) + item.setText(_translate("MainWindow", "Value")) + item = self.tableWidget.horizontalHeaderItem(2) + item.setText(_translate("MainWindow", "DataType")) + item = self.tableWidget.horizontalHeaderItem(3) + item.setText(_translate("MainWindow", "TimeStamp")) + item = self.tableWidget.horizontalHeaderItem(4) + item.setText(_translate("MainWindow", "Quality")) + item = self.tableWidget.horizontalHeaderItem(5) + item.setText(_translate("MainWindow", "NodePath")) + self.textBrowser.setHtml(_translate("MainWindow", "\n" "\n" -"


")) + "\n" + "


")) self.pushButton.setText(_translate("MainWindow", "Disconnect")) self.pairingbutton.setText(_translate("MainWindow", "Pair")) - + self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuView.setTitle(_translate("MainWindow", "View")) - self.actionRight_Hand_tree.setText(_translate("MainWindow", "Expand a right hand tree")) - self.hide_Right_Hand_tree.setText(_translate("MainWindow", "Hide the right hand tree")) + self.actionRight_Hand_tree.setText(_translate( + "MainWindow", "Expand a right hand tree")) + self.hide_Right_Hand_tree.setText(_translate( + "MainWindow", "Hide the right hand tree")) self.closing_app.setText(_translate("MainWindow", "Quit")) - + def onitemclicked(self): item = self.treeView.currentIndex() print(item.text(0)) - def closing_application(self): QtWidgets.qApp.quit() - def linking_servers(self): + def linking_servers(self): #Linking the servers and navigating all the nodes and finding the correct qx ix subscription. server1 = None server2 = None nav1 = None @@ -287,208 +293,217 @@ def linking_servers(self): nav2 = n.get_children_nodes_name(n.get_root_nodes()) i.client.disconnect() self.textBrowser.append("Linked server 2: " + i.server_name) - - + if(server2 != None and server1 != None): for dict1_key, dict1_values in nav1.items(): for value in dict1_values: - for key2,values2 in nav2.items(): + for key2, values2 in nav2.items(): for value2 in values2: if value[2:] == value2[2:]: - #print(value, value2) + if 'ix' in value and 'qx' in value2: server1.client.connect() get_obj = server1.client.get_objects_node().get_children() for i in get_obj: - function_name = server1.client.get_node(i).get_display_name()._text + function_name = server1.client.get_node( + i).get_display_name()._text print(function_name) if('Methods' == function_name): - bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server2.Server, str(dict1_key), str(key2)) + bl = server1.client.get_node(i).call_method( + "2:subscribe", "opc.tcp://" + server2.Server, str(dict1_key), str(key2)) self.textBrowser.append(bl) server1.client.disconnect() if 'qx' in value and 'ix' in value2: server2.client.connect() get_obj = server2.client.get_objects_node().get_children() for i in get_obj: - function_name = server2.client.get_node(i).get_display_name()._text - - if('Methods' == function_name): - bl = server1.client.get_node(i).call_method("2:subscribe","opc.tcp://" + server1.Server, str(key2), str(dict1_key)) + function_name = server2.client.get_node( + i).get_display_name()._text + + if('Methods' == function_name): + bl = server1.client.get_node(i).call_method( + "2:subscribe", "opc.tcp://" + server1.Server, str(key2), str(dict1_key)) self.textBrowser.append(bl) server2.client.disconnect() - - #server1.client.connect() + + # server1.client.connect() #f = server1.client.get_objects_node().get_children() - #for i in f: + # for i in f: # kkk = server1.client.get_node(i).get_display_name().__dict__['_text'] # print(server1.client.get_node(i).get_display_name()._text) - + def getValueLeft(self, val): node_name = val.data() bool_continue = True - root = val.parent() #The root node of the node index that the user double clicks - - while(root.parent().data() != None): #We loop till we get None as data (We're at the end of the hierarchy) - root = root.parent() - - for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview + root = val.parent() # The root node of the node index that the user double clicks + + # We loop till we get None as data (We're at the end of the hierarchy) + while(root.parent().data() != None): + root = root.parent() + + for i in self.clients: # Loop through our servers and make sure it already isn't in the treeview if(i.server_name == node_name): for k in i.NODE_ID: i.client.connect() for j in self.ROOT_CHILDREN_NODES: if(i.server_name == j.server_name): - children_name = i.client.get_node(k).get_browse_name().__dict__['Name'] + children_name = i.client.get_node( + k).get_browse_name().__dict__['Name'] if(children_name == j.node_name): bool_continue = False break i.client.disconnect() - - for i in self.clients: #Loop through our servers and make sure it already isn't in the treeview + + for i in self.clients: # Loop through our servers and make sure it already isn't in the treeview if(root.data() == i.server_name): for j in self.ROOT_CHILDREN_NODES: if(node_name == j.node_name and root.data() == j.server_name): i.client.connect() for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] + children_name = i.client.get_node( + d).get_browse_name().__dict__['Name'] for k in self.ROOT_CHILDREN_NODES: if(children_name == k.node_name and root.data() == k.server_name): - self.textBrowser.append("Children_name already exists! " + children_name + " " + k.node_name) + self.textBrowser.append( + "Children_name already exists! " + children_name + " " + k.node_name) bool_continue = False - break + break i.client.disconnect() - for i in self.clients: #We add the first children to the root node + for i in self.clients: # We add the first children to the root node if(i.server_name == node_name and bool_continue == True): - + for j in i.NODE_ID: - + i.client.connect() - children_name = i.client.get_node(j).get_browse_name().__dict__['Name'] - qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + children_name = i.client.get_node( + j).get_browse_name().__dict__['Name'] + qtitem = StItem(children_name, 8, + color=QtGui.QColor(180, 180, 180)) i.ROOT_NODE.appendRow(qtitem) server_name = node_name - self.ROOT_CHILDREN_NODES.append(Node_storage(server_name,children_name, j, qtitem)) + self.ROOT_CHILDREN_NODES.append(Node_storage( + server_name, children_name, j, qtitem)) i.client.disconnect() - - - for i in self.clients: #We add the first children to the first children of the root node (and etc) + + # We add the first children to the first children of the root node (and etc) + for i in self.clients: if(root.data() == i.server_name): - + for j in self.ROOT_CHILDREN_NODES: if(node_name == j.node_name and root.data() == j.server_name and bool_continue == True): - + i.client.connect() for d in i.client.get_node(j.node_id).get_children(): - children_name = i.client.get_node(d).get_browse_name().__dict__['Name'] - qtitem = StItem(children_name, 8, color=QtGui.QColor(180, 180, 180)) + children_name = i.client.get_node( + d).get_browse_name().__dict__['Name'] + qtitem = StItem(children_name, 8, + color=QtGui.QColor(180, 180, 180)) j.standarditem.appendRow(qtitem) server_name = root.data() - self.ROOT_CHILDREN_NODES.append(Node_storage(server_name, children_name, d, qtitem)) + self.ROOT_CHILDREN_NODES.append(Node_storage( + server_name, children_name, d, qtitem)) i.client.disconnect() - def creating_right_window(self): - MainWindow.resize(1300, 513) # resizing the window to be able to fit the new treeview - self.textBrowser.resize(1272, 111) # making the textbox bigger to be able to display more information + # resizing the window to be able to fit the new treeview + MainWindow.resize(1300, 513) + # making the textbox bigger to be able to display more information + self.textBrowser.resize(1272, 111) self.right_treeView.show() - self.pairingbutton.show() - + self.pairingbutton.show() def closing_right_window(self): self.right_treeView.hide() self.textBrowser.resize(942, 111) self.pairingbutton.hide() + def manual_connection(self): #Manually create a connection to a server and append it to the arrays for navigation. + adress = self.lineEdit.text() + self.clients.append(cl_node.Client_nodes(adress, adress)) + self.rootNode.appendRow(self.clients[-1].ROOT_NODE) + self.textBrowser.append("Manually added service: " + + adress + " !Note: name will be set to the address:port!") - def manual_connection(self): - adress = self.lineEdit.text() - self.clients.append(cl_node.Client_nodes(adress, adress)) - self.rootNode.appendRow(self.clients[-1].ROOT_NODE) - self.textBrowser.append("Manually added service: " + adress + " !Note: name will be set to the address:port!") - - def contextMenuEvent(self, pos): indexes = self.treeView.indexAt(pos) print(indexes.data()) - root = indexes.parent() #The root node of the node index that the user double clicks - - while(root.parent().data() != None): #We loop till we get None as data (We're at the end of the hierarchy) - root = root.parent() + root = indexes.parent() # The root node of the node index that the user double clicks + # We loop till we get None as data (We're at the beginning of the hierarchy) + while(root.parent().data() != None): + root = root.parent() menu = QMenu() - subscribe = menu.addAction("subscribe") - - action = menu.exec_(self.treeView.viewport().mapToGlobal(pos)) - - self.tableWidget.setRowCount(self.row + 1) - + subscribe = menu.addAction("Subscribe to variable") #Note that it creates a button to any widget. Fail-safes have not been added. + + action = menu.exec_(self.treeView.viewport().mapToGlobal(pos)) #Display the action at correct position. + + self.tableWidget.setRowCount(self.row + 1) #We add another count to the tablewidget. + server_address = None nodes_id_and_name = None node_id = None - if action == subscribe: - - for i in self.clients: + if action == subscribe: #If someone presses the button then we want to iterate and find that variable and display it. + + for i in self.clients: #Find the right client and if the server_name is equals to the first treeview node (The root node) then continue if(i.server_name == root.data()): - server_address = i.Server - i.client.connect() + server_address = i.Server #Add the server to our local variable. + i.client.connect() #Connect to the UA client for the UA server. n = nav.Navigating_nodes(i.client) - nodes_id_and_name = n.get_children_nodes_name(n.get_root_nodes()) - i.client.disconnect() + nodes_id_and_name = n.get_children_nodes_name( + n.get_root_nodes()) #Lets recursively get all the nodes and its name : id + i.client.disconnect() #Disconnect from the UA client after we're done break - - for dict1_key, dict1_values in nodes_id_and_name.items(): + + for dict1_key, dict1_values in nodes_id_and_name.items(): #Let us find the node id for the variable name that we're trying to subscribe to for value in dict1_values: - if(value == indexes.data()): - node_id = dict1_key + if(value == indexes.data()): #indexes.data tells us what variable we are trying to subscribe to. + node_id = dict1_key #Lets set the local node id variable to the actual variables node id. + #dict1_key is the node id and dict1_values is the string name of the node. + - print(server_address, node_id) - self.subscriptions_array.append(Subscription_storage(root.data(), indexes.data(), node_id, server_address, self.row, self.tableWidget)) - self.row += 1 - + self.subscriptions_array.append(Subscription_storage( + root.data(), indexes.data(), node_id, server_address, self.row, self.tableWidget)) #We also store all the information (For future reference) also to create our subscription handler. + self.textBrowser.append("Created a subscription to: " + str(indexes.data()) + self.row += 1 #As we have subscribed to a variable, next subscription needs to be on the next row. - def discover_servers(self): - if len(self.clients) > 0: + def discover_servers(self): #For discovering and displaying the OPC UA servers in the UA interface. + + if len(self.clients) > 0: #We check if clients have been added and remove them if so to not create duplicate clients. self.clients.clear() - self.treeModel.removeRows(0, self.treeModel.rowCount()) + self.treeModel.removeRows(0, self.treeModel.rowCount()) - if len(self.ROOT_CHILDREN_NODES) > 0: + if len(self.ROOT_CHILDREN_NODES) > 0: #We also remove the children nodes. self.ROOT_CHILDREN_NODES.clear() - url = dsc.Server_Discovery() - url.get_servers() - self.SERVER_ARR = url.get_all(0) - print(self.SERVER_ARR) - servers = url.get_all_as_address() + url = dsc.Server_Discovery() #We create an instance of Server_discovery. + url.get_servers() #We call the get servers function to get the servers. + self.SERVER_ARR = url.get_all(0) #We get the server names (in a list) from the servers and store it. + servers = url.get_all_as_address() #We get a list of the addresses and port ex : [192.168.1.10:3249, 192.168.1.5:3249] print(servers) - j = 0 - for i in servers: - self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) - self.textBrowser.append("Service added: " + self.SERVER_ARR[j] + "- At address: " + i) + for i in servers: #We iterate through the server list + self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) #We append a Client_nodes instance into an array for further reference. + #Client_nodes arguments require the IP:PORT and server name which it stores. + self.textBrowser.append( + "Service added: " + self.SERVER_ARR[j] + "- At address: " + i) #Logger for the User interface. - j += 1 + j += 1 #We iterate through all servers but we also need to add the correct server_name which is also an array. Best to just use a counter here. - for i in self.clients: + for i in self.clients: #We add the server name to the treeview. self.rootNode.appendRow(i.ROOT_NODE) - - +if __name__ == "__main__": #Set up the User Interface window. - - - - -if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() - + sys.exit(app.exec_()) diff --git a/uaxplorer/ui_methods/Ui_client.py b/uaxplorer/ui_methods/Ui_client.py deleted file mode 100644 index 69b6b6f..0000000 --- a/uaxplorer/ui_methods/Ui_client.py +++ /dev/null @@ -1,21 +0,0 @@ -from opcua import Client, ua -import server_discovery as disc - - - -class Ui_client(): - def __init__(self, server): - self.client = Client("opc.tcp://" + server) - self.servers = server - -def create_connection(): - url = disc.Server_Discovery() - url.get_servers() - servers = url.get_all_as_address() - print(servers) - established_servers = list() - for i in servers: - established_servers.append(Ui_client(i)) - - return established_servers - diff --git a/uaxplorer/window.py b/uaxplorer/window.py deleted file mode 100644 index 62d401a..0000000 --- a/uaxplorer/window.py +++ /dev/null @@ -1,60 +0,0 @@ -from tkinter import * -from tkinter import ttk -from opcua import Client, ua - -class window(Tk): - def __init__(self): - super(window,self).__init__() - self.title("GUI") - self.geometry("600x600") - self.treetime = ttk.Treeview(self) - self.treetime.pack() - self.treetime.insert('', 0 , 'item1', text=ram) - self.treetime.insert('', 0, 'item2', text=vam) - self.treetime.insert('', 0 , 'item3', text=par) - self.treetime.insert('', 0 , 'item4', text=bam) - - self.treetime.move('item2', 'item1', 'end') - self.treetime.move('item3', 'item1', 'end') - self.treetime.move('item4', 'item1', 'end') - - -if __name__ == "__main__": - client = Client("opc.tcp://127.0.0.1:4840") - try: - client.connect() - client.load_type_definitions()# load definition of server specific structures/extension objects - - - root = client.get_root_node() - print(root) - objects = client.get_objects_node() - print("Objects node is: ", objects) - children = root.get_children() - print(children) - - rar = client.get_node("ns=2;i=1") - ram = rar.get_browse_name() - print(ram) - - var = client.get_node("ns=2;i=2") - vam = var.get_browse_name() - print(vam) - - par = client.get_node("ns=2;i=3").get_browse_name() - bam = par.__dict__['Name'] - - print(bam) - - gar = client.get_node("ns=2;i=4") - gam = gar.get_browse_name() - print(gam) - - finally: - client.disconnect() - - - - -root = window() -root.mainloop() \ No newline at end of file From 7dde28be072315e3a373879ed8f388bc04a8d34f Mon Sep 17 00:00:00 2001 From: FrictionalUnemployment <62852896+FrictionalUnemployment@users.noreply.github.com> Date: Thu, 18 Mar 2021 22:28:57 +0100 Subject: [PATCH 73/77] More bug fixes --- uaxplorer/ui_methods/MainUI.py | 145 +++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 62 deletions(-) diff --git a/uaxplorer/ui_methods/MainUI.py b/uaxplorer/ui_methods/MainUI.py index b0c0b9c..5fb1894 100644 --- a/uaxplorer/ui_methods/MainUI.py +++ b/uaxplorer/ui_methods/MainUI.py @@ -12,7 +12,6 @@ from PyQt5.QtWidgets import QMenu from zeroconf import ServiceBrowser, Zeroconf import server_discovery as dsc -import Ui_client as ui_c import client_nodes as cl_node from opcua import client, Client from client_nodes import StandardItem as StItem @@ -56,12 +55,6 @@ def __init__(self, server_name, node_name, node_id, server_address, row, tablewi # DataValue(Value:Variant(val:19,type:VariantType.Int64), StatusCode:StatusCode(Good), SourceTimestamp:2021-03-13 12:36:58.621994) # Example of outprint from var - self.tableWidget.setItem(self.row, 4, QtWidgets.QTableWidgetItem( - str(var.get_data_type_as_variant_type()))) # Get the variant type of the node - self.tableWidget.setItem(self.row, 5, QtWidgets.QTableWidgetItem( - str(var.get_data_value().SourceTimestamp))) # Get the time stamp and display - self.tableWidget.setItem(self.row, 6, QtWidgets.QTableWidgetItem( - str(var.get_data_value().StatusCode))) # Get the statuscode Good, Bad # Create a subscription handelr to update the variables value outprint. Also send in the widget and row handler = SubHandler(self.row, self.tableWidget) sub = self.client.create_subscription( @@ -78,15 +71,21 @@ def __init__(self, row, tablewidget): """ Subscription Handler. To receive events from server for a subscription data_change and event methods are called directly from receiving thread. - Do not do expensive, slow or network operation there. Create another + Do not do expensive, slow or network operation there. Create another thread if you need to do such a thing """ def datachange_notification(self, node, val, data): - #print("Python: New data change event", node, val) + # print("Python: New data change event", node, val) # When a data change happens we append the value to the widget. self.tableWidget.setItem( self.row, 3, QtWidgets.QTableWidgetItem(str(val))) + self.tableWidget.setItem(self.row, 4, QtWidgets.QTableWidgetItem( + str(data.monitored_item.Value.Value.VariantType))) # Get the variant type of the node + self.tableWidget.setItem(self.row, 5, QtWidgets.QTableWidgetItem( + str(data.monitored_item.Value.SourceTimestamp))) # Get the time stamp and display + self.tableWidget.setItem(self.row, 6, QtWidgets.QTableWidgetItem( + str(data.monitored_item.Value.StatusCode))) # Get the statuscode Good, Bad # We update the tableWidget, so that the update becomes present in the user client. self.tableWidget.viewport().update() @@ -273,7 +272,8 @@ def onitemclicked(self): def closing_application(self): QtWidgets.qApp.quit() - def linking_servers(self): #Linking the servers and navigating all the nodes and finding the correct qx ix subscription. + # Linking the servers and navigating all the nodes and finding the correct qx ix subscription. + def linking_servers(self): server1 = None server2 = None nav1 = None @@ -300,7 +300,7 @@ def linking_servers(self): #Linking the servers and navigating all the nodes and for key2, values2 in nav2.items(): for value2 in values2: if value[2:] == value2[2:]: - + if 'ix' in value and 'qx' in value2: server1.client.connect() get_obj = server1.client.get_objects_node().get_children() @@ -321,13 +321,13 @@ def linking_servers(self): #Linking the servers and navigating all the nodes and i).get_display_name()._text if('Methods' == function_name): - bl = server1.client.get_node(i).call_method( + bl = server2.client.get_node(i).call_method( "2:subscribe", "opc.tcp://" + server1.Server, str(key2), str(dict1_key)) self.textBrowser.append(bl) server2.client.disconnect() # server1.client.connect() - #f = server1.client.get_objects_node().get_children() + # f = server1.client.get_objects_node().get_children() # for i in f: # kkk = server1.client.get_node(i).get_display_name().__dict__['_text'] # print(server1.client.get_node(i).get_display_name()._text) @@ -418,13 +418,48 @@ def closing_right_window(self): self.textBrowser.resize(942, 111) self.pairingbutton.hide() - def manual_connection(self): #Manually create a connection to a server and append it to the arrays for navigation. + # Manually create a connection to a server and append it to the arrays for navigation. + def manual_connection(self): adress = self.lineEdit.text() self.clients.append(cl_node.Client_nodes(adress, adress)) self.rootNode.appendRow(self.clients[-1].ROOT_NODE) self.textBrowser.append("Manually added service: " + adress + " !Note: name will be set to the address:port!") + def discover_servers(self): # For discovering and displaying the OPC UA servers in the UA interface. + if len(self.clients) > 0: # We check if clients have been added and remove them if so to not create duplicate clients. + self.clients.clear() + self.treeModel.removeRows(0, self.treeModel.rowCount()) + + # We also remove the children nodes. + if len(self.ROOT_CHILDREN_NODES) > 0: + self.ROOT_CHILDREN_NODES.clear() + + # We create an instance of Server_discovery. + url=dsc.Server_Discovery() + # We call the get servers function to get the servers. + url.get_servers() + # We get the server names (in a list) from the servers and store it. + self.SERVER_ARR=url.get_all(0) + # We get a list of the addresses and port ex : [192.168.1.10:3249, 192.168.1.5:3249] + servers=url.get_all_as_address() + print(servers) + + j=0 + for i in servers: # We iterate through the server list + # We append a Client_nodes instance into an array for further reference. + self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) + # Client_nodes arguments require the IP:PORT and server name which it stores. + self.textBrowser.append( + "Service added: " + self.SERVER_ARR[j] + "- At address: " + i) # Logger for the User interface. + + # We iterate through all servers but we also need to add the correct server_name which is also an array. Best to just use a counter here. + j += 1 + + for i in self.clients: # We add the server name to the treeview. + self.rootNode.appendRow(i.ROOT_NODE) + #print() + def contextMenuEvent(self, pos): indexes = self.treeView.indexAt(pos) print(indexes.data()) @@ -436,73 +471,59 @@ def contextMenuEvent(self, pos): root = root.parent() menu = QMenu() - subscribe = menu.addAction("Subscribe to variable") #Note that it creates a button to any widget. Fail-safes have not been added. + # Note that it creates a button to any widget. Fail-safes have not been added. + subscribe = menu.addAction("Subscribe to variable") - action = menu.exec_(self.treeView.viewport().mapToGlobal(pos)) #Display the action at correct position. + # Display the action at correct position. + action = menu.exec_(self.treeView.viewport().mapToGlobal(pos)) - self.tableWidget.setRowCount(self.row + 1) #We add another count to the tablewidget. + # We add another count to the tablewidget. + self.tableWidget.setRowCount(self.row + 1) server_address = None nodes_id_and_name = None node_id = None - if action == subscribe: #If someone presses the button then we want to iterate and find that variable and display it. + # If someone presses the button then we want to iterate and find that variable and display it. + if(action == subscribe): - for i in self.clients: #Find the right client and if the server_name is equals to the first treeview node (The root node) then continue + # Find the right client and if the server_name is equals to the first treeview node (The root node) then continue + for i in self.clients: if(i.server_name == root.data()): - server_address = i.Server #Add the server to our local variable. - i.client.connect() #Connect to the UA client for the UA server. + # Add the server to our local variable. + server_address = i.Server + # Connect to the UA client for the UA server. + i.client.connect() n = nav.Navigating_nodes(i.client) nodes_id_and_name = n.get_children_nodes_name( - n.get_root_nodes()) #Lets recursively get all the nodes and its name : id - i.client.disconnect() #Disconnect from the UA client after we're done + n.get_root_nodes()) # Lets recursively get all the nodes and its name : id + i.client.disconnect() # Disconnect from the UA client after we're done break - for dict1_key, dict1_values in nodes_id_and_name.items(): #Let us find the node id for the variable name that we're trying to subscribe to + # Let us find the node id for the variable name that we're trying to subscribe to + for dict1_key, dict1_values in nodes_id_and_name.items(): for value in dict1_values: - if(value == indexes.data()): #indexes.data tells us what variable we are trying to subscribe to. - node_id = dict1_key #Lets set the local node id variable to the actual variables node id. - #dict1_key is the node id and dict1_values is the string name of the node. + # indexes.data tells us what variable we are trying to subscribe to. + if(value == indexes.data()): + # Lets set the local node id variable to the actual variables node id. + node_id = dict1_key + # dict1_key is the node id and dict1_values is the string name of the node. - self.subscriptions_array.append(Subscription_storage( - root.data(), indexes.data(), node_id, server_address, self.row, self.tableWidget)) #We also store all the information (For future reference) also to create our subscription handler. - self.textBrowser.append("Created a subscription to: " + str(indexes.data()) - self.row += 1 #As we have subscribed to a variable, next subscription needs to be on the next row. - - def discover_servers(self): #For discovering and displaying the OPC UA servers in the UA interface. - - if len(self.clients) > 0: #We check if clients have been added and remove them if so to not create duplicate clients. - self.clients.clear() - self.treeModel.removeRows(0, self.treeModel.rowCount()) - - if len(self.ROOT_CHILDREN_NODES) > 0: #We also remove the children nodes. - self.ROOT_CHILDREN_NODES.clear() - - url = dsc.Server_Discovery() #We create an instance of Server_discovery. - url.get_servers() #We call the get servers function to get the servers. - self.SERVER_ARR = url.get_all(0) #We get the server names (in a list) from the servers and store it. - servers = url.get_all_as_address() #We get a list of the addresses and port ex : [192.168.1.10:3249, 192.168.1.5:3249] - print(servers) - - j = 0 - for i in servers: #We iterate through the server list - self.clients.append(cl_node.Client_nodes(i, self.SERVER_ARR[j])) #We append a Client_nodes instance into an array for further reference. - #Client_nodes arguments require the IP:PORT and server name which it stores. - self.textBrowser.append( - "Service added: " + self.SERVER_ARR[j] + "- At address: " + i) #Logger for the User interface. - - j += 1 #We iterate through all servers but we also need to add the correct server_name which is also an array. Best to just use a counter here. - - for i in self.clients: #We add the server name to the treeview. - self.rootNode.appendRow(i.ROOT_NODE) + root.data(), indexes.data(), node_id, server_address, self.row, self.tableWidget)) # We also store all the information (For future reference) also to create our subscription handler. + # As we have subscribed to a variable, next subscription needs to be on the next row. + self.row += 1 + self.textBrowser.append("Created a subscription to: " + str(indexes.data())) + + -if __name__ == "__main__": #Set up the User Interface window. +# Set up the User Interface window. +if __name__ == "__main__": - app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() + app=QtWidgets.QApplication(sys.argv) + MainWindow=QtWidgets.QMainWindow() + ui=Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() From 1a541b94b385edd1bb64aecef3401db006ed024b Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 19 Mar 2021 09:32:06 +0100 Subject: [PATCH 74/77] More general version of server. --- server/serverpi.py | 110 +++++++++++++++++++++------------ server/specialized_server.py | 33 ++++++++++ server/specialized_server2.py | 33 ++++++++++ server/test_serverpi_client.py | 30 ++------- 4 files changed, 142 insertions(+), 64 deletions(-) create mode 100644 server/specialized_server.py create mode 100644 server/specialized_server2.py diff --git a/server/serverpi.py b/server/serverpi.py index efa61a6..00ebcf4 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -10,15 +10,12 @@ from asyncua import ua, uamethod, Server, Client import announce_service as sa -SERVER_NAME = "RaspPI OPC UA Server" +SERVER_NAME = "OPC UA Server" FLAT_NAME = SERVER_NAME.replace(" ", "") SERVER_PORT = 4840 -SERVER_ENDPOINT = "opc.tcp://0.0.0.0:"+str(SERVER_PORT) -UA_NAMESPACE = "hvproj:ua:"+FLAT_NAME +SERVER_ENDPOINT = "opc.tcp://0.0.0.0:" +UA_NAMESPACE = "hvproj:ua:" DISCOVERY_NAME="_"+FLAT_NAME[:10]+"." -TEMP = 19 - - class SubHandler(): """ @@ -41,11 +38,38 @@ def event_notification(self, event): class ServerPI: - def __init__(self): - self.temp = TEMP - self.ixBall = TEMP - self.ixBarrel = TEMP + def __init__(self, name=SERVER_NAME, port=SERVER_PORT): + self.server_name = name + self.flat_name = name.replace(" ", "") + self.server = None + self.server_port = port + self.ua_namespace = UA_NAMESPACE+self.flat_name + self.discovery_name = "_"+self.flat_name[:10]+"." + self.varfolder = None + self.idx = None + self.objects = None self.clients = {} + self.localvars = {} + self.callback_func = None + self.callback_vars = None + + async def init_server(self): + print("Inside init_server") + sa.start_service_announcement(device_name=self.discovery_name, iport=self.server_port) + self.server = Server() + await self.server.init() + self.server.set_endpoint(SERVER_ENDPOINT+str(self.server_port)) + self.server.set_server_name(self.server_name) + #server.set_security_policy([ + # ua.SecurityPolicyType.NoSecurity, + # ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, + # ua.SecurityPolicyType.Basic256Sha256_Sign]) + + self.idx = await self.server.register_namespace(self.ua_namespace) + self.objects = self.server.nodes.objects + self.varfolder = await self.objects.add_folder(self.idx, "Variables") + print(type(self.varfolder)) + await self.setup_sub_method() # This ua method is used to subscribe to a variable on # another server. @@ -84,7 +108,9 @@ async def subscribe(self, parent, endpoint, qx, ix): self.clients[server][3].append(subbedvar) time.sleep(0.1) return "Successfully subscribed to the specified variable!" - + + # Helper method for setting up a UA Argument, in this case + # for use in the subscription method. def method_var(self, name, description): arg = ua.Argument() arg.Name = name @@ -93,42 +119,50 @@ def method_var(self, name, description): arg.ArrayDimensions = [] arg.Description = ua.LocalizedText(description) return arg + + # Helper method for setting up the subscription method. + async def setup_sub_method(self): + methods = await self.objects.add_object(self.idx, "Methods") + print(methods) + endp = self.method_var("Endpoint", "Address to tendpoint") + qxvar = self.method_var("qx", "Output variable to connect to server.") + ixvar = self.method_var("ix", "Input variable that is to be connected to.") + ret = self.method_var("ret", "Return message for information of what happend.") + await methods.add_method(self.idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], [ret]) - async def go(self): - server = Server() - await server.init() - server.set_endpoint(SERVER_ENDPOINT) - server.set_server_name(SERVER_NAME) - #server.set_security_policy([ - # ua.SecurityPolicyType.NoSecurity, - # ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt, - # ua.SecurityPolicyType.Basic256Sha256_Sign]) + # Add a local variable to the UA Server. + async def add_variable(self, name, startvalue): + variable = await self.varfolder.add_variable(self.idx, name, startvalue) + print(variable) + self.localvars[name] = [variable, startvalue] - idx = await server.register_namespace(UA_NAMESPACE) + # Used to set a value of a variable, requires the + # name of the variable as a string and the corresponding + # python datatype value. + async def write_variable(self, name, value): + await self.localvars[name][0].write_value(value) - objects = server.nodes.objects + # Returns the current value of the local + # server variable. + def get_variable_value(self, name): + return self.localvars[name][1] - lFolder = await objects.add_folder(idx, "Sensors") - zobj = await objects.add_object(idx, "Methods") - zvar = await lFolder.add_variable(idx, "ixTemperature", self.temp) - yvar = await lFolder.add_variable(idx, "ixBall", self.ixBall) - xvar = await lFolder.add_variable(idx, "ixBarrel", self.ixBarrel) - print(zvar) - print(yvar) - print(xvar) + async def update_vars(self): + for n in self.localvars.keys(): + self.localvars[n][1] = await self.localvars[n][0].get_value() - endp = self.method_var("Endpoint", "Address to tendpoint") - qxvar = self.method_var("qx", "Output variable to connect to server.") - ixvar = self.method_var("ix", "Input variable that is to be connected to.") - ret = self.method_var("ret", "Return message for information of what happend.") - await zobj.add_method(idx, "subscribe", self.subscribe, [endp, qxvar, ixvar], [ret]) + def add_callback(self, fnc, vars=None): + self.callback_func = fnc + self.callback_vars = vars - async with server: + async def go(self): + async with self.server: while True: await asyncio.sleep(0.1) + await self.update_vars() + if self.callback_func is not None: + await self.callback_func(self.callback_vars) if __name__ == "__main__": - print(DISCOVERY_NAME) - sa.start_service_announcement(device_name=DISCOVERY_NAME, iport=SERVER_PORT) sp = ServerPI() asyncio.run(sp.go()) diff --git a/server/specialized_server.py b/server/specialized_server.py new file mode 100644 index 0000000..05c2cea --- /dev/null +++ b/server/specialized_server.py @@ -0,0 +1,33 @@ +import asyncio +import serverpi + +async def go(): + + #Variables to be sued + inBall = False + outBall = False + inBarrel = False + outBarrel = False + var_list = [inBall, outBall, inBarrel, outBarrel] + + sp = serverpi.ServerPI(name="Firsta test", port=4840) + await sp.init_server() + # Callback function to update variables outside of the OPCUA server. + # the vars is a list of variables that are available inside the OPCUA server. + async def cb_func(vars): + vars[1] = not vars[1] + vars[3] = not vars[3] + await sp.write_variable("qxBall", vars[1]) + await sp.write_variable("qxBarrel", vars[3]) + vars[2] = sp.get_variable_value("ixBarrel") + vars[0] = sp.get_variable_value("ixBall") + + await sp.add_variable("ixBall", inBall) + await sp.add_variable("ixBarrel", inBarrel) + await sp.add_variable("qxBall", outBall) + await sp.add_variable("qxBarrel", outBarrel) + sp.add_callback(cb_func, var_list) + await sp.go() + +if __name__ == "__main__": + asyncio.run(go()) diff --git a/server/specialized_server2.py b/server/specialized_server2.py new file mode 100644 index 0000000..9c2b6df --- /dev/null +++ b/server/specialized_server2.py @@ -0,0 +1,33 @@ +import asyncio +import serverpi + +async def go(): + + #Variables to be sued + inBall = False + outBall = False + inBarrel = False + outBarrel = False + var_list = [inBall, outBall, inBarrel, outBarrel] + + sp = serverpi.ServerPI(name="2Andra tvo", port=4841) + await sp.init_server() + # Callback function to update variables outside of the OPCUA server. + # the vars is a list of variables that are available inside the OPCUA server. + async def cb_func(vars): + vars[1] = not vars[1] + vars[3] = not vars[3] + await sp.write_variable("qxBall", vars[1]) + await sp.write_variable("qxBarrel", vars[3]) + vars[2] = sp.get_variable_value("ixBarrel") + vars[0] = sp.get_variable_value("ixBall") + + await sp.add_variable("ixBall", inBall) + await sp.add_variable("ixBarrel", inBarrel) + await sp.add_variable("qxBall", outBall) + await sp.add_variable("qxBarrel", outBarrel) + sp.add_callback(cb_func, var_list) + await sp.go() + +if __name__ == "__main__": + asyncio.run(go()) diff --git a/server/test_serverpi_client.py b/server/test_serverpi_client.py index 9784d10..3807329 100644 --- a/server/test_serverpi_client.py +++ b/server/test_serverpi_client.py @@ -25,40 +25,18 @@ def event_notification(self, event): async def main(): url = "opc.tcp://0.0.0.0:4840" async with Client(url=url) as client: - #uri = "UA_NAMESPACE" - #idx = await client.get_namespace_index(uri) - # get a specific node knowing its node id - #var = client.get_node(ua.NodeId(1002, 2)) - #var = client.get_node("ns=3;i=2002") - #print(var) - #await var.read_data_value() # get value of node as a DataValue object - #await var.read_value() # get value of node as a python builtin - #await var.write_value(ua.Variant([23], ua.VariantType.Int64)) #set node value using explicit data type - #await var.write_value(3.9) # set node value using implicit data type - - # Now getting a variable node using its browse path - myvar = await client.nodes.root.get_child(["0:Objects", "2:Sensors", "2:ixTemperature"]) - obj = await client.nodes.root.get_child(["0:Objects", "2:Methods"]) - # subscribing to a variable node - handler = SubHandler() - sub = await client.create_subscription(500, handler) - handle = await sub.subscribe_data_change(myvar) + #obj = await client.nodes.root.get_child(["0:Objects", "2:Methods"]) + obj = await client.nodes.root.get_child("ns=2; i=2") await asyncio.sleep(0.1) - # we can also subscribe to events from server - await sub.subscribe_events() - # await sub.unsubscribe(handle) - # await sub.delete() print("Here be calling da meffod!") # calling a method on server - ret = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=3", "ns=2;i=3") - ret2 = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=4", "ns=2;i=4") - ret3 = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=5", "ns=2;i=5") + ret = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=4", "ns=2;i=2") + ret2 = await obj.call_method("2:subscribe", "opc.tcp://0.0.0.0:4841", "ns=2;i=5", "ns=2;i=3") print(ret) print(ret2) - print(ret3) if __name__ == "__main__": From 5c3c7ed18595e100035c2d02875a38093982c5c3 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 19 Mar 2021 09:35:22 +0100 Subject: [PATCH 75/77] Fixed so local server updates variable value. --- server/serverpi.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 00ebcf4..d8e63dc 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -24,12 +24,13 @@ class SubHandler(): Do not do expensive, slow or network operation there. Create another thread if you need to do such a thing """ - def __init__(self, variables, client): + def __init__(self, variables, client, localserver): self.vars = variables self.cl = client + self.srv = localserver def datachange_notification(self, node, val, data): - n = self.cl.get_node(self.vars[str(node)]) + n = self.srv.get_node(self.vars[str(node)]) n.write_value(val) print("Python: New data change event", n, val) @@ -93,7 +94,7 @@ async def subscribe(self, parent, endpoint, qx, ix): await client.load_data_type_definitions() print("After client.loaddatattype") tmpvariables = {} - tmphandler = SubHandler(tmpvariables, client) + tmphandler = SubHandler(tmpvariables, client, self.server) tmpsubscription = await client.create_subscription(500, tmphandler) self.clients[server] = (client, tmphandler, tmpsubscription, [], tmpvariables) except: From a6ef893752a3e424b5f64f667ffa6d319c85bea3 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 19 Mar 2021 09:55:41 +0100 Subject: [PATCH 76/77] Made all variables writable. --- .gitignore | 1 + server/serverpi.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 7e4945c..3ff9f0d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ server/__pycache__ uaxplorer/ui_methods/__pycache__ uaxplorer/ui_methods/__pycache__/discovery.cpython-39.pyc uaxplorer/ui_methods/__pycache__/server_discovery.cpython-39.pyc +*.pyc diff --git a/server/serverpi.py b/server/serverpi.py index d8e63dc..8e6182a 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -134,6 +134,7 @@ async def setup_sub_method(self): # Add a local variable to the UA Server. async def add_variable(self, name, startvalue): variable = await self.varfolder.add_variable(self.idx, name, startvalue) + variable.set_writable() print(variable) self.localvars[name] = [variable, startvalue] From 3d8c9dba17b2ba2e2ecb743e011cb6de32fc1d01 Mon Sep 17 00:00:00 2001 From: Alexander Norberg Date: Fri, 19 Mar 2021 10:04:44 +0100 Subject: [PATCH 77/77] Lowered the time it checks for update. --- server/serverpi.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/serverpi.py b/server/serverpi.py index 8e6182a..72a718e 100644 --- a/server/serverpi.py +++ b/server/serverpi.py @@ -31,7 +31,7 @@ def __init__(self, variables, client, localserver): def datachange_notification(self, node, val, data): n = self.srv.get_node(self.vars[str(node)]) - n.write_value(val) + n.set_value(val) print("Python: New data change event", n, val) def event_notification(self, event): @@ -95,7 +95,7 @@ async def subscribe(self, parent, endpoint, qx, ix): print("After client.loaddatattype") tmpvariables = {} tmphandler = SubHandler(tmpvariables, client, self.server) - tmpsubscription = await client.create_subscription(500, tmphandler) + tmpsubscription = await client.create_subscription(50, tmphandler) self.clients[server] = (client, tmphandler, tmpsubscription, [], tmpvariables) except: return "Could not reach the server specified." @@ -135,7 +135,6 @@ async def setup_sub_method(self): async def add_variable(self, name, startvalue): variable = await self.varfolder.add_variable(self.idx, name, startvalue) variable.set_writable() - print(variable) self.localvars[name] = [variable, startvalue] # Used to set a value of a variable, requires the