def test_nocrypto(self): clt = Client(self.uri_no_crypto) clt.connect() try: clt.get_objects_node().get_children() finally: clt.disconnect()
class Plant(threading.Thread): def __init__(self, ip): super().__init__() self.client = Client(ip) self.objects = self.assign_object() self.heights = {'h1': 0, 'h2': 0, 'h3': 0, 'h4': 0} self.voltages = {'u1': 0, 'u2': 0} self._u = [0, 0] self.sample_time = 0.001 self.daemon = True ## Que termine de medir cuando termine el programa (debug) self.event_measure = threading.Event() def assign_object(self): self.client.connect() return self.client.get_objects_node() def get_valve_value(self): for i in range(1, 3): node = self.objects.get_child(['2:Proceso_Tanques', '2:Valvulas', f'2:Valvula{i}', '2:u']) self.voltages[f'u{i}'] = node.get_value() #NoneType object does not support item assig def get_heights(self): for i in range(1, 5): node = self.objects.get_child(['2:Proceso_Tanques', '2:Tanques', f'2:Tanque{i}', '2:h']) self.heights[f'h{i}'] = node.get_value() #NoneType object does not support item assig def suscribe_alarm(self): alarm = self.objects.get_child(['2:Proceso_Tanques', '2:Alarmas', f'2:Alarma_nivel']) handler = SubHandler() sub = self.client.create_subscription(500, handler) handle = sub.subscribe_events(alarm) time.sleep(0.1) def assign_valve_value(self): u1 = self.objects.get_child(['2:Proceso_Tanques', '2:Valvulas', f'2:Valvula1', '2:u']) u2 = self.objects.get_child(['2:Proceso_Tanques', '2:Valvulas', f'2:Valvula2', '2:u']) u1.set_value(self.u[0]) u2.set_value(self.u[1]) @property def u(self): return self._u @u.setter def u(self, value): if not None in value: self._u = list(value) else: self._u = self._u def run(self): self.suscribe_alarm() while True: if sum(self.u) > 0: self.assign_valve_value() self.u = [0, 0] self.event_measure.set() ## Revisar esto porque no se esta cumpliendo self.get_heights() time.sleep(self.sample_time)
def uaclient(): parser = argparse.ArgumentParser(description="Connect to server and start python shell. root and objects nodes are available. Node specificed in command line is available as mynode variable") add_common_args(parser) parser.add_argument("-c", "--certificate", help="set client certificate") parser.add_argument("-k", "--private_key", help="set client private key") args = parse_args(parser) client = Client(args.url, timeout=args.timeout) _configure_client_with_args(client, args) if args.certificate: client.load_client_certificate(args.certificate) if args.private_key: client.load_private_key(args.private_key) client.connect() try: root = client.get_root_node() objects = client.get_objects_node() mynode = get_node(client, args) embed() finally: client.disconnect() sys.exit(0)
def uaclient(): parser = argparse.ArgumentParser(description="Connect to server and start python shell. root and objects nodes are available. Node specificed in command line is available as mynode variable") add_common_args(parser) parser.add_argument("-c", "--certificate", help="set client certificate") parser.add_argument("-k", "--private_key", help="set client private key") args = parser.parse_args() logging.basicConfig(format="%(levelname)s: %(message)s", level=getattr(logging, args.loglevel)) client = Client(args.url, timeout=args.timeout) client.connect() if args.certificate: client.load_certificate(args.certificate) if args.private_key: client.load_certificate(args.private_key) try: root = client.get_root_node() objects = client.get_objects_node() mynode = client.get_node(args.nodeid) if args.path: mynode = mynode.get_child(args.path.split(",")) embed() finally: client.disconnect() sys.exit(0)
def uaclient(): parser = argparse.ArgumentParser( description= "Connect to server and start python shell. root and objects nodes are available. Node specificed in command line is available as mynode variable" ) add_common_args(parser) parser.add_argument("-c", "--certificate", help="set client certificate") parser.add_argument("-k", "--private_key", help="set client private key") args = parse_args(parser) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) if args.certificate: client.load_client_certificate(args.certificate) if args.private_key: client.load_private_key(args.private_key) client.connect() try: root = client.get_root_node() objects = client.get_objects_node() mynode = get_node(client, args) embed() finally: client.disconnect() sys.exit(0)
def test_basic256sha256_longkey(self): clt = Client(self.uri_crypto2) try: clt.set_security_string("Basic256Sha256,Sign,../examples/certificate-example.der,../examples/private-key-example.pem") clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def test_basic128Rsa15_encrypt(self): clt = Client(self.uri_crypto) try: clt.set_security_string("Basic128Rsa15,SignAndEncrypt,examples/certificate-example.der,examples/private-key-example.pem") clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def test_basic256(self): clt = Client(self.uri_crypto) try: clt.set_security_string("Basic256,Sign,examples/example-certificate.der,examples/example-private-key.pem") clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def test_basic128Rsa15_encrypt(self): clt = Client(self.uri_crypto) try: clt.set_security_string( "Basic128Rsa15,SignAndEncrypt,examples/certificate-example.der,examples/private-key-example.pem" ) clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def test_basic256_encrypt_success(self): clt = Client(self.uri_crypto) try: clt.set_security(security_policies.SecurityPolicyBasic256, 'examples/certificate-example.der', 'examples/private-key-example.pem', None, ua.MessageSecurityMode.SignAndEncrypt) clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def test_basic256_encrypt_success(self): clt = Client(self.uri_crypto) try: clt.set_security(security_policies.SecurityPolicyBasic256, 'examples/certificate-example.der', 'examples/private-key-example.pem', None, ua.MessageSecurityMode.SignAndEncrypt ) clt.connect() self.assertTrue(clt.get_objects_node().get_children()) finally: clt.disconnect()
def start_uaprogramm(self, name, serverurl, port, uamethod="start_demoprogramm"): """ ruft die UA Methode über den im Docker erzeugten Client auf """ # Suche, ob der Container existiert clientname = name + "client" self._refresh_registry() container = self.registry.get(clientname) if container: connectstring = "opc.tcp://" + serverurl + ":" + port + "/freeopcua/server/" client = Client(connectstring) # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") try: client.connect() client.load_type_definitions() # # Der Client verfügt über einige Methoden, um einen Proxy für UA-Knoten abzurufen, # die sich immer im Adressraum befinden sollten, z. B. Root oder Objects root = client.get_root_node() print("Root node is: ", root) objects = client.get_objects_node() print("Objects node is: ", objects) # Knotenobjekte verfügen über Methoden zum Lesen und Schreiben von Knotenattributen sowie zum Duchsuchen # des Adressraums print("Children of root are: ", root.get_children()) # Der Adressraum idx uri = "http://freeopcua.github.io" idx = client.get_namespace_index(uri) # Jetzt wird ein variabler Knoten über den Suchpfad abgerufen myvar = root.get_child([ "0:Objects", "{}:MyObject".format(idx), "{}:MyVariable".format(idx) ]) obj = root.get_child(["0:Objects", "{}:MyObject".format(idx)]) # Aufrufen unserer übergebenen Methode res = obj.call_method(uamethod.format(idx)) finally: client.disconnect()
def test_udf_call_builtins(self): client = Client("opc.tcp://localhost:4840") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() obj = root.get_child(["0:Objects", "2:DataProcPlugin"]) print("\nSet UDF to udf_call_builtins.py and wait for 10 secs") ret = obj.call_method("2:set_udf_file", "udf_call_builtins.py") self.assertEqual(ret, ['0', 'Success']) time.sleep(10) except BaseException: self.assertTrue(False) finally: client.disconnect()
def download_xst(subband: int, integration_time_s: int, url: str = 'localhost', port: int = 50000): """ Download cross correlation statistics Args: subband (int): Subband number integration_time_s (int): Integration time in seconds url (str): URL to connect to, defaults to 'localhost' port (str): Port to connect to, defaults to 50000 Returns: Tuple[datetime.datetime, np.ndarray, int]: UTC time, visibilities (shape n_ant x n_ant), RCU mode Raises: RuntimeError: if in mixed RCU mode """ client = Client("opc.tcp://{}:{}/".format(url, port), timeout=1000) client.connect() client.load_type_definitions() objects = client.get_objects_node() idx = client.get_namespace_index(DEFAULT_URI) obj = client.get_root_node().get_child( ["0:Objects", "{}:StationMetrics".format(idx), "{}:RCU".format(idx)]) obstime, visibilities_opc, rcu_modes = obj.call_method( "{}:record_cross".format(idx), subband, integration_time_s) client.close_session() client.close_secure_channel() rcu_modes_on = set([mode for mode in rcu_modes if mode != '0']) if len(rcu_modes_on) == 1: rcu_mode = int(rcu_modes_on.pop()) elif len(rcu_modes_on) == 0: rcu_mode = 0 else: raise RuntimeError( "Multiple nonzero RCU modes are used, that's not supported yet") assert (len(visibilities_opc) == 2) # Real and complex part visibilities = np.array(visibilities_opc)[0] + 1j * np.array( visibilities_opc[1]) return obstime, visibilities, rcu_mode
def run(self): print('Thread running') url = "opc.tcp://10.8.0.14:4840" client = Client(url) client.connect() print("Client Connected") client.load_type_definitions() #while True: root = client.get_root_node() print("Root 노드: ", root) objects = client.get_objects_node() print("오브젝트: ", objects) print("Children of root are: ", objects.get_children())
def test_0_ua_client(self): client = Client("opc.tcp://localhost:4840") try: client.connect() root = client.get_root_node() print("Root node is: ", root.__dict__) print("Children of root are: ", root.get_children()) objects = client.get_objects_node() print("Objects node is: ", objects) print("Children of objects are: ", objects.get_children()) obj = root.get_child(["0:Objects", "2:DataProcPlugin"]) print("dataprocplugin obj is ", obj) except BaseException: self.assertTrue( False, msg="Error checking DataProcPlugin in OPC-UA registry") finally: client.disconnect()
def test_1_set_udf_enabled(self): client = Client("opc.tcp://localhost:4840") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() obj = root.get_child(["0:Objects", "2:DataProcPlugin"]) print("\nDisable UDF and wait for 10 secs") ret = obj.call_method("2:set_udf_enabled", "false") self.assertEqual(ret, ['0', 'Success']) time.sleep(10) print("\nRe-enable UDF and wait for 10 secs") ret = obj.call_method("2:set_udf_enabled", "true") self.assertEqual(ret, ['0', 'Success']) time.sleep(10) except BaseException: self.assertTrue(False) finally: client.disconnect()
def main(): srv_url = "opc.tcp://127.0.0.1:XXXX/FearFactory/supervision_unit/" client = Client(srv_url) client.connect() root = client.get_root_node() objs = client.get_objects_node() # Subscribe to the main board event_obj = root.get_child("2:MainBoard") handler = SubscriptionHandler() subscription = client.create_subscription(500, handler) event_type = root.get_child( ["0:Types", "0:EventTypes", "0:BaseEventType", "2:MainBoardNotif"]) hilt = subscription.subscribe_events(event_obj, event_type) #subscription.unsubscribe(hilt) #subscription.delete() # Uncomment if you want to interact with server using IPython embed()
def lambda_handler(event, context): """ Handler for the AWS Lambda function. :param event: AWS Lambda uses this parameter to pass in event data to the handler. This parameter is usually of the Python dict type. It can also be list, str, int, float, or NoneType type. :param context: The AWS Lambda event context. To learn more about what is included in the context, see https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html. """ logger.debug('Handling event: %s', json.dumps(event)) client = Client(SERVER_URL) try: client.connect() root = client.get_root_node() logger.debug("Root node is: %s", root) objects = client.get_objects_node() logger.debug("Objects node is: %s", objects) # Node objects have methods to read and write node attributes as well as browse or populate address space logger.debug("Children of root are: %s", root.get_children()) logger.debug('Creating subscription...') subscription_handler = SubscriptionHandler() subscription = client.create_subscription(500, subscription_handler) logger.info('Finished creating subscription to %s', SERVER_URL) logger.debug('Subscribing to data changes...') variable = root.get_child(["0:Objects", "2:MyObject", "2:MyVariable"]) handle = subscription.subscribe_data_change(variable) logger.info('Subscribed to %s', handle) while True: time.sleep(1) finally: client.disconnect()
class OpcUaConnector(Thread, Connector): def __init__(self, gateway, config, connector_type): self._connector_type = connector_type self.statistics = {'MessagesReceived': 0, 'MessagesSent': 0} super().__init__() self.__gateway = gateway self.__server_conf = config.get("server") self.__interest_nodes = [] self.__available_object_resources = {} self.__show_map = self.__server_conf.get("showMap", False) self.__previous_scan_time = 0 for mapping in self.__server_conf["mapping"]: if mapping.get("deviceNodePattern") is not None: self.__interest_nodes.append( {mapping["deviceNodePattern"]: mapping}) else: log.error( "deviceNodePattern in mapping: %s - not found, add property deviceNodePattern to processing this mapping", dumps(mapping)) if "opc.tcp" not in self.__server_conf.get("url"): opcua_url = "opc.tcp://" + self.__server_conf.get("url") else: opcua_url = self.__server_conf.get("url") self.client = Client( opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) if self.__server_conf["identity"]["type"] == "cert.PEM": try: ca_cert = self.__server_conf["identity"].get("caCert") private_key = self.__server_conf["identity"].get("privateKey") cert = self.__server_conf["identity"].get("cert") security_mode = self.__server_conf["identity"].get( "mode", "SignAndEncrypt") policy = self.__server_conf["security"] if cert is None or private_key is None: log.exception( "Error in ssl configuration - cert or privateKey parameter not found" ) raise security_string = policy + ',' + security_mode + ',' + cert + ',' + private_key if ca_cert is not None: security_string = security_string + ',' + ca_cert self.client.set_security_string(security_string) except Exception as e: log.exception(e) if self.__server_conf["identity"].get("username"): self.client.set_user( self.__server_conf["identity"].get("username")) if self.__server_conf["identity"].get("password"): self.client.set_password( self.__server_conf["identity"].get("password")) self.setName( self.__server_conf.get( "name", 'OPC-UA ' + ''.join(choice(ascii_lowercase) for _ in range(5))) + " Connector") self.__opcua_nodes = {} self._subscribed = {} self.data_to_send = [] self.__sub_handler = SubHandler(self) self.__stopped = False self.__connected = False self.daemon = True def is_connected(self): return self.__connected def open(self): self.__stopped = False self.start() log.info("Starting OPC-UA Connector") def run(self): while not self.__connected: try: self.__connected = self.client.connect() try: self.client.load_type_definitions() except Exception as e: log.debug(e) log.debug("Error on loading type definitions.") log.debug(self.client.get_namespace_array()[-1]) log.debug( self.client.get_namespace_index( self.client.get_namespace_array()[-1])) except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except OSError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: log.debug("error on connection to OPC-UA server.") log.error(e) time.sleep(10) else: self.__connected = True log.info("OPC-UA connector %s connected to server %s", self.get_name(), self.__server_conf.get("url")) self.__opcua_nodes["root"] = self.client.get_objects_node() self.__opcua_nodes["objects"] = self.client.get_objects_node() if not self.__server_conf.get("disableSubscriptions", False): self.__sub = self.client.create_subscription( self.__server_conf.get("subCheckPeriodInMillis", 500), self.__sub_handler) else: self.__sub = False self.__scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 log.debug('Subscriptions: %s', self.subscribed) log.debug("Available methods: %s", self.__available_object_resources) while not self.__stopped: try: time.sleep(.1) self.__check_connection() if not self.__connected and not self.__stopped: self.client.connect() elif not self.__stopped: if self.__server_conf.get( "disableSubscriptions", False ) and time.time( ) * 1000 - self.__previous_scan_time > self.__server_conf.get( "scanPeriodInMillis", 60000): self.__scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 if self.data_to_send: self.__gateway.send_to_storage(self.get_name(), self.data_to_send.pop()) if self.__stopped: self.close() break except (KeyboardInterrupt, SystemExit): self.close() raise except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: self.close() log.exception(e) def __check_connection(self): try: node = self.client.get_root_node() node.get_children() self.__connected = True except ConnectionRefusedError: self.__connected = False self._subscribed = {} self.__sub = None except OSError: self.__connected = False self._subscribed = {} self.__sub = None except TimeoutError: self.__connected = False self._subscribed = {} self.__sub = None except AttributeError: self.__connected = False self._subscribed = {} self.__sub = None except Exception as e: self.__connected = False self._subscribed = {} self.__sub = None log.exception(e) def close(self): self.__stopped = True if self.__connected: self.client.disconnect() self.__connected = False log.info('%s has been stopped.', self.get_name()) def get_name(self): return self.name def on_attributes_update(self, content): log.debug(content) try: for server_variables in self.__available_object_resources[ content["device"]]['variables']: for attribute in content["data"]: for variable in server_variables: if attribute == variable: server_variables[variable].set_value( content["data"][variable]) except Exception as e: log.exception(e) def server_side_rpc_handler(self, content): try: for method in self.__available_object_resources[ content["device"]]['methods']: rpc_method = content["data"].get("method") if rpc_method is not None and method.get( rpc_method) is not None: arguments_from_config = method["arguments"] arguments = content["data"].get( "params") if content["data"].get( "params") is not None else arguments_from_config try: if type(arguments) is list: result = method["node"].call_method( method[rpc_method], *arguments) elif arguments is not None: result = method["node"].call_method( method[rpc_method], arguments) else: result = method["node"].call_method( method[rpc_method]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { content["data"]["method"]: result, "code": 200 }) log.debug("method %s result is: %s", method[rpc_method], result) except Exception as e: log.exception(e) self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], { "error": str(e), "code": 500 }) else: log.error("Method %s not found for device %s", rpc_method, content["device"]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { "error": "%s - Method not found" % (rpc_method), "code": 404 }) except Exception as e: log.exception(e) def __scan_nodes_from_config(self): try: if self.__interest_nodes: for device_object in self.__interest_nodes: for current_device in device_object: try: device_configuration = device_object[ current_device] devices_info_array = self.__search_general_info( device_configuration) for device_info in devices_info_array: if device_info is not None and device_info.get( "deviceNode") is not None: self.__search_nodes_and_subscribe( device_info) self.__save_methods(device_info) self.__search_attribute_update_variables( device_info) else: log.error( "Device node is None, please check your configuration." ) log.debug( "Current device node is: %s", str( device_configuration.get( "deviceNodePattern"))) break except Exception as e: log.exception(e) log.debug(self.__interest_nodes) except Exception as e: log.exception(e) def __search_nodes_and_subscribe(self, device_info): information_types = { "attributes": "attributes", "timeseries": "telemetry" } for information_type in information_types: for information in device_info["configuration"][information_type]: information_key = information["key"] config_path = TBUtility.get_value(information["path"], get_tag=True) information_path = self._check_path(config_path, device_info["deviceNode"]) information["path"] = '${%s}' % information_path information_nodes = [] self.__search_node(device_info["deviceNode"], information_path, result=information_nodes) for information_node in information_nodes: if information_node is not None: information_value = information_node.get_value() log.debug( "Node for %s \"%s\" with path: %s - FOUND! Current values is: %s", information_type, information_key, information_path, str(information_value)) if device_info.get("uplink_converter") is None: configuration = { **device_info["configuration"], "deviceName": device_info["deviceName"], "deviceType": device_info["deviceType"] } if device_info["configuration"].get( 'converter') is None: converter = OpcUaUplinkConverter(configuration) else: converter = TBUtility.check_and_import( self._connector_type, configuration) device_info["uplink_converter"] = converter else: converter = device_info["uplink_converter"] self.subscribed[information_node] = { "converter": converter, "path": information_path, "config_path": config_path } if not device_info.get( information_types[information_type]): device_info[ information_types[information_type]] = [] converted_data = converter.convert( (config_path, information_path), information_value) self.statistics['MessagesReceived'] += 1 self.data_to_send.append(converted_data) self.statistics['MessagesSent'] += 1 if self.__sub is None: self.__sub = self.client.create_subscription( self.__server_conf.get( "subCheckPeriodInMillis", 500), self.__sub_handler) if self.__sub: self.__sub.subscribe_data_change(information_node) log.debug("Added subscription to node: %s", str(information_node)) log.debug("Data to ThingsBoard: %s", converted_data) else: log.error( "Node for %s \"%s\" with path %s - NOT FOUND!", information_type, information_key, information_path) def __save_methods(self, device_info): try: if self.__available_object_resources.get( device_info["deviceName"]) is None: self.__available_object_resources[ device_info["deviceName"]] = {} if self.__available_object_resources[ device_info["deviceName"]].get("methods") is None: self.__available_object_resources[ device_info["deviceName"]]["methods"] = [] if device_info["configuration"].get("rpc_methods"): node = device_info["deviceNode"] for method_object in device_info["configuration"][ "rpc_methods"]: method_node_path = self._check_path( method_object["method"], node) methods = [] self.__search_node(node, method_node_path, True, result=methods) for method in methods: if method is not None: node_method_name = method.get_display_name().Text self.__available_object_resources[ device_info["deviceName"]]["methods"].append({ node_method_name: method, "node": node, "arguments": method_object.get("arguments") }) else: log.error( "Node for method with path %s - NOT FOUND!", method_node_path) except Exception as e: log.exception(e) def __search_attribute_update_variables(self, device_info): try: if device_info["configuration"].get("attributes_updates"): node = device_info["deviceNode"] device_name = device_info["deviceName"] if self.__available_object_resources.get(device_name) is None: self.__available_object_resources[device_name] = {} if self.__available_object_resources[device_name].get( "variables") is None: self.__available_object_resources[device_name][ "variables"] = [] for attribute_update in device_info["configuration"][ "attributes_updates"]: attribute_path = self._check_path( attribute_update["attributeOnDevice"], node) attribute_nodes = [] self.__search_node(node, attribute_path, result=attribute_nodes) for attribute_node in attribute_nodes: if attribute_node is not None: self.__available_object_resources[device_name][ "variables"].append({ attribute_update["attributeOnThingsBoard"]: attribute_node }) else: log.error( "Attribute update node with path \"%s\" - NOT FOUND!", attribute_path) except Exception as e: log.exception(e) def __search_general_info(self, device): result = [] match_devices = [] self.__search_node(self.__opcua_nodes["root"], TBUtility.get_value(device["deviceNodePattern"], get_tag=True), result=match_devices) for device_node in match_devices: if device_node is not None: result_device_dict = { "deviceName": None, "deviceType": None, "deviceNode": device_node, "configuration": deepcopy(device) } name_pattern_config = device["deviceNamePattern"] name_expression = TBUtility.get_value(name_pattern_config, get_tag=True) if "${" in name_pattern_config and "}" in name_pattern_config: log.debug("Looking for device name") name_path = self._check_path(name_expression, device_node) device_name_node = [] self.__search_node(device_node, name_path, result=device_name_node) device_name_node = device_name_node[0] if device_name_node is not None: device_name_from_node = device_name_node.get_value() full_device_name = name_pattern_config.replace( "${" + name_expression + "}", str(device_name_from_node)).replace( name_expression, str(device_name_from_node)) else: log.error( "Device name node not found with expression: %s", name_expression) return else: full_device_name = name_expression result_device_dict["deviceName"] = full_device_name log.debug("Device name: %s", full_device_name) if device.get("deviceTypePattern"): device_type_expression = TBUtility.get_value( device["deviceTypePattern"], get_tag=True) if "${" in device_type_expression and "}" in device_type_expression: type_path = self._check_path(device_type_expression, device_node) device_type_node = [] self.__search_node(device_node, type_path, result=device_type_node) device_type_node = device_type_node[0] if device_type_node is not None: device_type = device_type_node.get_value() full_device_type = device_type_expression.replace( "${" + device_type_expression + "}", device_type).replace(device_type_expression, device_type) else: log.error( "Device type node not found with expression: %s", device_type_expression) full_device_type = "default" else: full_device_type = device_type_expression result_device_dict["deviceType"] = full_device_type log.debug("Device type: %s", full_device_type) else: result_device_dict["deviceType"] = "default" result.append(result_device_dict) else: log.error( "Device node not found with expression: %s", TBUtility.get_value(device["deviceNodePattern"], get_tag=True)) return result def __search_node(self, current_node, fullpath, search_method=False, result=[]): fullpath_pattern = regex.compile(fullpath) try: for child_node in current_node.get_children(): new_node = self.client.get_node(child_node) new_node_path = '\\\\.'.join( char.split(":")[1] for char in new_node.get_path(200000, True)) if self.__show_map: log.debug("SHOW MAP: Current node path: %s", new_node_path) new_node_class = new_node.get_node_class() # regex_fullmatch = re.fullmatch(fullpath, new_node_path.replace('\\\\.', '.')) or new_node_path.replace('\\\\', '\\') == fullpath regex_fullmatch = regex.fullmatch(fullpath_pattern, new_node_path.replace('\\\\.', '.')) or \ new_node_path.replace('\\\\', '\\') == fullpath.replace('\\\\', '\\') or \ new_node_path.replace('\\\\', '\\') == fullpath regex_search = fullpath_pattern.fullmatch(new_node_path.replace('\\\\.', '.'), partial=True) or \ new_node_path.replace('\\\\', '\\') in fullpath.replace('\\\\', '\\') # regex_search = re.search(new_node_path, fullpath.replace('\\\\', '\\')) if regex_fullmatch: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", new_node_path.replace('\\\\', '\\')) result.append(new_node) elif regex_search: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", new_node_path) if new_node_class == ua.NodeClass.Object: if self.__show_map: log.debug("SHOW MAP: Search in %s", new_node_path) self.__search_node(new_node, fullpath, result=result) elif new_node_class == ua.NodeClass.Variable: log.debug("Found in %s", new_node_path) result.append(new_node) elif new_node_class == ua.NodeClass.Method and search_method: log.debug("Found in %s", new_node_path) result.append(new_node) except Exception as e: log.exception(e) def _check_path(self, config_path, node): if re.search("^root", config_path.lower()) is None: node_path = '\\\\.'.join( char.split(":")[1] for char in node.get_path(200000, True)) if config_path[-3:] != '\\.': information_path = node_path + '\\\\.' + config_path.replace( '\\', '\\\\') else: information_path = node_path + config_path.replace( '\\', '\\\\') else: information_path = config_path return information_path[:] @property def subscribed(self): return self._subscribed
set_bool(read_b12,0,2,t_floor.get_value()) plc.write_area( areas['MK'], 0,12,read_b12 ) time.sleep(.01) if __name__ == "__main__": try: opc_client.connect() except Exception as identifier: print("OPCUA Error:") print(identifier) else: opc_client.load_type_definitions() root = opc_client.get_root_node() objects = opc_client.get_objects_node() idx = opc_client.get_namespace_index(opc_uri) obj = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx)]) # dest = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Destination".format(idx)]) dest = opc_client.get_node("ns=2;i=6") # disp = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Displacement".format(idx)]) disp = opc_client.get_node("ns=2;i=3") # spd = root.get_child(["0:Objects", "{}:PLC_S71200".format(idx), "{}:Speed".format(idx)]) aspd = opc_client.get_node("ns=2;i=4") lspd = opc_client.get_node("ns=2;i=5") f_floor = opc_client.get_node("ns=2;i=13") s_floor = opc_client.get_node("ns=2;i=14")
if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://192.168.0.1:4840") # client = Client("opc.tcp://0.0.0.0:4840") # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user try: client.connect() # 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 # temp = client.get_objects_node().get_child(["3:PLC_2", "3:DataBlocksGlobal", "3:dbPackML", "3:Machine_1", "3:Status", "3:MachSpeed"]) temp = client.get_objects_node().get_child(["2:Parameters", "2:Temperature"]) 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
from opcua import Client from opcua import ua cliente = Client("opc.tcp://localhost:4840/freeopcua/server/") print(type(cliente)) try: print(2) cliente.connect() print("hola") objectsNode = cliente.get_objects_node() h1 = objectsNode.get_child(['1:Proceso Tanques', '1:Tanques','Tanque1','1:h']) valor = h1.get_value() except: cliente.disconnect()
class OpcClient(): """A simplified OpClient. This is a opc client that can be leveraged to subscribe to the OPC data change notifications. You should interact with the client using * add_notification_handler * subscribe_variable * unsubscribe_variable So typically: ``` # setup client cl = OpcClient(...) # define handler def handler(update): # data update : {'node': <node>, 'timestamp': <datetime>, 'value': <value>, 'data': <data>} # do something with update here # add handler cl.add_notification_handler(handler) # subscribe to variable cl.subscribe_variable(cl.get_node("ns=3;i=2002")) time.sleep(100) cl.disconnect() ``` *Note* Make sure to call disconnect before shutting down in order to ensure a clean close. See: https://github.com/FreeOpcUa/python-opcua Attributes ---------- _address : str The connection string address _address_obfuscated : str The connection string address obfuscating a potnetial password _client : OPCLibClient The opc lib client _subscription : ua.Subscription The ua subscription handle that can be used to create new subscritions or unsubscribe _sub_handles : dict A dictionary mapping ua.Node variables to subscription handles to be able to unsubscribe from them again _handlers : list A list of handler functions """ def __init__(self, host, port, path='/', username=None, password=None): """Create a new OpClient. This will create a new opc client that can be leveraged to subscribe to the OPC data change notifications. Parameters ---------- host : str The host name of the opc server. port : int The port of the opc server. path : str The path to add to the server as base username : str|None A username to use for authorization. password : str|None A password to use for authorization. """ self._client = None self._subscription = None self._sub_handles = {} self._handlers = [] # setup authorization auth = '' if username is not None: auth = '{}:{}@'.format(username, password) # define address self._address = self._address_obfuscated = "opc.tcp://{}{}:{}{}".format( auth, host, port, path) if username is not None and password is not None: self._address_obfuscated = self._address.replace(password, '***') Logger.trace('Created OpcClient', fields={'address': self._address_obfuscated}) # setup client self.init_client() # setup subscriptions self.init_subscriptions() def init_client(self): """Initialize the client. This will connect to the client using the address. This is required to be called before anything else interacting with the opc server is done. """ try: self._client = OPCLibClient(self._address) self._client.connect() self._client.load_type_definitions() self.namespace = self._client.get_namespace_array() Logger.trace('Connection established') except Exception as e: Logger.error('Failed connecting to address "{}"'.format( self._address_obfuscated)) Logger.error('{}'.format(e)) raise e def log_info(self): root = self._client.get_root_node() objects = self._client.get_objects_node() children = root.get_children() # print '{}:{}'.format(namespace[children[1].nodeid.NamespaceIndex], children[1].nodeid.Identifier) # print self._client.get_node("ns=0;i=86") Logger.debug("Retrieved Client Info", fields={ 'root': root, 'objects': objects, 'children': children, 'namespace': self.namespace }) def full_uri(self, node): """Resolve the full uri of a ua.Node. Parameters ---------- node : ua.Node A node from the OPC interface. Returns ------- str : The namespace prefixed full uri of the entity. """ return '{}:{}'.format(self.namespace[node.nodeid.NamespaceIndex], node.nodeid.Identifier) def get_by_uri(self, uri): """Resolve the full uri to a ua.Node. Parameters ---------- uri : str The uri to the ua.Node. Returns ------- ua.Node : The resolved node. """ ns_match = None for ns in self.namespace: if '{}:'.format(ns) in uri: ns_match = ns break if ns_match is None: raise RuntimeError('Namespace of "{}" not available'.format(uri)) ns_idx = self._client.get_namespace_index(ns_match) identifier = uri.replace('{}:'.format(ns_match), '') try: int(identifier) is_int = True except: is_int = False substituted = 'ns={};{}={}'.format(ns_idx, 'i' if is_int else 's', identifier) return self._client.get_node(substituted) def init_subscriptions(self): """Initialize the subscriptions. This will initialize a subscription handler and afterwards will be ready to subscribe to specific variables. """ if self._client is None: raise RuntimeError('Client not initialized yet.') self._subscription = self._client.create_subscription(500, self) Logger.trace('Base Subscription established') def subscribe_variable(self, variable): """Subscribe to a OPC variable. This will subscribe the client to the given variable and any data change notification will be published. Parameters ---------- variable : ua.Node The ua node, e.g. client.get_node(ua.NodeId(1002, 2)) or client.get_node("ns=3;i=2002") """ if self._subscription is None: raise RuntimeError('Subscriptions not initialized yet.') if variable in self._sub_handles.keys(): Logger.info('Already subscribed to "{}"'.format(variable)) return self._sub_handles[variable] = self._subscription.subscribe_data_change( variable) def unsubscribe_variable(self, variable): """Unsubscribe from a OPC variable. This will unsubscribe the client from the given variable. Parameters ---------- variable : ua.Node The ua node, e.g. client.get_node(ua.NodeId(1002, 2)) or client.get_node("ns=3;i=2002") """ if self._subscription is None: raise RuntimeError('Subscriptions not initialized yet.') if variable not in self._sub_handles.keys(): Logger.info('Not subscribed to "{}"'.format(variable)) return self._subscription.unsubscribe(self._sub_handles[variable]) del self._sub_handles[variable] def disconnect(self): """Disconnect the client. """ if self._client is None: return try: self._client.disconnect() except Exception: pass def datachange_notification(self, node, value, data): """Receiver from the OPC client. This gets called by OPC client on subscription notification. Parameter --------- node : ua.Node The node from which we received the data. value : any The value notification data : any The data of the notification """ timestamp = datetime.now() if hasattr(data, 'MonitoredItemNotification') and \ hasattr(data.MonitoredItemNotification, 'SourceTimestamp'): timestamp = data.MonitoredItemNotification.SourceTimestamp Logger.debug('OpcClient: Received data change notification', fields={ 'node': node, 'value': value, 'data': data, 'timestamp': timestamp.isoformat() }) # send update to handlers update = { 'node': node, 'value': value, 'data': data, 'timestamp': timestamp } for hdl in self._handlers: hdl(update) def add_notification_handler(self, handler): """Add a handler function. This handler `def handler(update)` will be called upon reception of a notification from the OPC Client. The update will have the following data: ```{ 'node': <node>, 'value': <value>, 'data': <data>, 'timestamp': <timestamp> }``` """ self._handlers.append(handler)
class AdminTestClient(unittest.TestCase, CommonTests): ''' Run common tests on client side Of course we need a server so we start a server in another process using python Process module Tests that can only be run on client side must be defined here ''' @classmethod def setUpClass(self): # start our own server self.srv = Server() self.srv.set_endpoint('opc.tcp://*****:*****@localhost:%d' % port_num1) self.clt.connect() self.opc = self.clt # start anonymous client self.ro_clt = Client('opc.tcp://localhost:%d' % port_num1) self.ro_clt.connect() @classmethod def tearDownClass(self): #stop our clients self.ro_clt.disconnect() self.clt.disconnect() # stop the server self.srv.stop() def test_service_fault(self): request = ua.ReadRequest() request.TypeId = ua.FourByteNodeId(999) # bad type! with self.assertRaises(Exception): self.clt.bclient._send_request(request) def test_objects_anonymous(self): objects = self.ro_clt.get_objects_node() with self.assertRaises(Exception): objects.set_attribute(ua.AttributeIds.WriteMask, ua.DataValue(999)) with self.assertRaises(Exception): f = objects.add_folder(3, 'MyFolder') def test_folder_anonymous(self): objects = self.clt.get_objects_node() f = objects.add_folder(3, 'MyFolderRO') f_ro = self.ro_clt.get_node(f.nodeid) self.assertEqual(f, f_ro) with self.assertRaises(Exception): f2 = f_ro.add_folder(3, 'MyFolder2') def test_variable_anonymous(self): objects = self.clt.get_objects_node() v = objects.add_variable(3, 'MyROVariable', 6) v.set_value(4) #this should work v_ro = self.ro_clt.get_node(v.nodeid) with self.assertRaises(Exception): v_ro.set_value(2) self.assertEqual(v_ro.get_value(), 4) v.set_writable(True) v_ro.set_value(2) #now it should work self.assertEqual(v_ro.get_value(), 2) v.set_writable(False) with self.assertRaises(Exception): v_ro.set_value(9) self.assertEqual(v_ro.get_value(), 2)
if x not in badAttributes: print('Attribute %d: ' %x, node.get_attribute(x)) if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://192.168.0.1:4840") try: client.connect() # Getting Root node information print("Root node is: ", client.get_root_node()) print("Objects node is: ", client.get_objects_node()) print("Children of root are: ", client.get_root_node().get_children()) # get a specific node knowing its node id pathNumber_node = client.get_node('ns=3;s="M_PathNumber"') testSignal_node = client.get_node('ns=3;s="M_TestSignal"') ShouldBeFalse = client.get_node('ns=3;s="M_ShouldBeFalse"') alwaysTrue = client.get_node('ns=3;s="AlwaysTRUE"') # Creating subscription handler handler = SubHandler() sub = client.create_subscription(500, handler) handle = sub.subscribe_data_change(pathNumber_node) sub.subscribe_events() print_node_info(pathNumber_node)
class TestClient(unittest.TestCase, CommonTests, SubscriptionTests): ''' Run common tests on client side Of course we need a server so we start also start a server Tests that can only be run on client side must be defined in this class ''' @classmethod def setUpClass(self): # start our own server self.srv = Server() self.srv.set_endpoint('opc.tcp://*****:*****@localhost:%d' % port_num1, timeout=10) self.clt.connect() self.opc = self.clt # start anonymous client self.ro_clt = Client('opc.tcp://localhost:%d' % port_num1) self.ro_clt.connect() @classmethod def tearDownClass(self): #stop our clients self.ro_clt.disconnect() self.clt.disconnect() # stop the server self.srv.stop() def test_service_fault(self): request = ua.ReadRequest() request.TypeId = ua.FourByteNodeId(999) # bad type! with self.assertRaises(ua.UaStatusCodeError): self.clt.uaclient._uasocket.send_request(request) def test_objects_anonymous(self): objects = self.ro_clt.get_objects_node() with self.assertRaises(ua.UaStatusCodeError): objects.set_attribute(ua.AttributeIds.WriteMask, ua.DataValue(999)) with self.assertRaises(ua.UaStatusCodeError): f = objects.add_folder(3, 'MyFolder') def test_folder_anonymous(self): objects = self.clt.get_objects_node() f = objects.add_folder(3, 'MyFolderRO') f_ro = self.ro_clt.get_node(f.nodeid) self.assertEqual(f, f_ro) with self.assertRaises(ua.UaStatusCodeError): f2 = f_ro.add_folder(3, 'MyFolder2') def test_variable_anonymous(self): objects = self.clt.get_objects_node() v = objects.add_variable(3, 'MyROVariable', 6) v.set_value(4) #this should work v_ro = self.ro_clt.get_node(v.nodeid) with self.assertRaises(ua.UaStatusCodeError): v_ro.set_value(2) self.assertEqual(v_ro.get_value(), 4) v.set_writable(True) v_ro.set_value(2) #now it should work self.assertEqual(v_ro.get_value(), 2) v.set_writable(False) with self.assertRaises(ua.UaStatusCodeError): v_ro.set_value(9) self.assertEqual(v_ro.get_value(), 2) def test_context_manager(self): """ Context manager calls connect() and disconnect() """ state = [0] def increment_state(self, *args, **kwargs): state[0] += 1 # create client and replace instance methods with dummy methods client = Client('opc.tcp://dummy_address:10000') client.connect = increment_state.__get__(client) client.disconnect = increment_state.__get__(client) assert state[0] == 0 with client: # test if client connected self.assertEqual(state[0], 1) # test if client disconnected self.assertEqual(state[0], 2)
''' def event_notification(self, event): print() ''' #SubHandler Over if __name__ == "__main__": logging.basicConfig(level=logging.WARN) client = Client("opc.tcp://127.0.0.1:49320") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() #之後用該objects取得寫入的Tag tag1 = client.get_node("ns=2;s=Channel1.Device1.Tag1") #Read用 #write begin var = objects.get_child(["2:Channel1", "2:Device1", "2:Tag2"]) #get_chird +[path] #使用ua,必須先import ua / code: "from opcua import ua" dv = ua.DataValue(ua.Variant( 7878, ua.VariantType.UInt16)) #宣告dv 指定為ua的DataValue,並指定數值以及資料型態 var.set_value(dv) #write tag = set_value(經由ua轉換得到的數值/型態) #write end handler = SubHandler() sub = client.create_subscription(1000, handler) handle1 = sub.subscribe_data_change(tag1) from IPython import embed
class AgentOPCUA(Agent): """Defines an Agent communicating via OPCUA.""" def __init__(self, hyperparams): """Initializes the agent. Args: hyperparams: Dictionary of hyperparameters. """ Agent.__init__(self, hyperparams) self.x0 = None self.dt = self._hyperparams['dt'] self.scaler = self._hyperparams['scaler'] self.action_scaler = self._hyperparams['action_scaler'] self.actuator_overrides = self._hyperparams['override_actuator'] self.sensor_overrides = self._hyperparams['override_sensor'] self.signals = self._hyperparams['send_signal'] self.sensors = self._hyperparams['opc_ua_states_include'] self.actuators = self.u_data_types self.client = None self.pool = ThreadPool(len(self.sensors)) self.debug = False def __init_opcua(self): if self.client is not None: # Close existing connection try: self.client.disconnect() except ua.uaerrors.UaError: pass self.client = Client(self._hyperparams['opc-ua_server']) self.client.connect() try: # Get Nodes self.opcua_vars = {} for sensor in (self.sensors + self.actuators): self.opcua_vars[sensor] = self.__get_node(sensor.value) for signal in self.signals: self.opcua_vars[signal['signal']] = self.__get_node( signal['signal']) except Exception: # Close connection in case of error self.close() raise def __get_node(self, sensor): browse_name = sensor.split(',') try: return self.client.get_objects_node().get_child(browse_name) except ua.uaerrors.BadNoMatch: logging.critical("Node not found: '%s'" % (sensor)) raise def transform(self, sensor, sensor_data): """Scales sates according to the scaler.""" data_idx = self._x_data_idx[sensor] return (sensor_data - self.scaler.mean_[data_idx]) / self.scaler.scale_[data_idx] def inverse_transform(self, actuator, actuator_data): """Inverse actions according to the scaler.""" data_idx = self._u_data_idx[actuator] return actuator_data * self.action_scaler.scale_[ data_idx] + self.action_scaler.mean_[data_idx] def read_sensor(self, sensor): """Reads an OPCUA sensor. Args: sensor: Sensor identifier (as specified in hyperparams file) Returns: sensor_data: Array of sensor readings. """ sensor_data = self.opcua_vars[sensor].get_value() if np.isscalar(sensor_data): # Wrap scalars into single element arrays sensor_data = [sensor_data] return self.transform(sensor, np.asarray(sensor_data)) def write_actuator(self, actuator, data): """Sets an OPCUA actuator. Args: actuator: Actuator identifier (as specified in hyperparams file) data: Value to set the actuator to. Arrays of length one are unboxed to a scalar. """ if len(data) == 1: data = data.item() data = ua.Variant(self.inverse_transform(actuator, data), ua.VariantType.Float) try: self.opcua_vars[actuator].set_data_value(data) except ua.uaerrors._auto.BadTypeMismatch: logging.critical( "Failed to write %r to %r of type %r" % (data, actuator, self.opcua_vars[actuator].get_data_type())) raise def reset(self, cond): """Reset the agent for a particular experiment condition. Args: condition: An index into hyperparams['reset_conditions']. """ if not self.debug: input('Press Enter to confirm reset') def sample(self, policy, condition, save=True, noisy=True, reset_cond=None, **kwargs): """Performs agent reset and rolls out given policy to collect a sample. Args: policy: A Policy object. condition: Which condition setup to run. save: Whether or not to store the trial into the samples. noisy: Whether or not to use noise during sampling. reset_cond: The initial condition to reset the agent into. Returns: sample: A Sample object. """ # Get a new sample sample = Sample(self) sample_ok = False while not sample_ok: if not self.debug: self.reset(reset_cond) self.__init_opcua() if noisy: noise = generate_noise(self.T, self.dU, self._hyperparams) else: noise = None # Execute policy over a time period of [0,T] start = time.time() for t in range(self.T): # Read sensors and store sensor data in sample def store_sensor(sensor): sample.set(sensor, self.read_sensor(sensor), t) self.pool.map(store_sensor, self.sensors) # Override sensors for override in self.sensor_overrides: if override['condition'](t): sensor = override['sensor'] sample.set(sensor, override['value'](sample, t), t) print('X_%02d' % t, sample.get_X(t)) # Get action U_t = policy.act(sample.get_X(t), sample.get_obs(t), t, noise) # Override actuators for override in self.actuator_overrides: if override['condition'](t): actuator = override['actuator'] U_t[self._u_data_idx[actuator]] = np.copy( override['value']) # Send signals self.send_signals(t) # Perform action for actuator in self._u_data_idx: self.write_actuator(actuator, U_t[self._u_data_idx[actuator]]) sample.set(ACTION, U_t, t) print('U_%02d' % t, U_t) # Check if agent is keeping up sleep_time = start + (t + 1) * self.dt - time.time() if sleep_time < 0: logging.critical("Agent can't keep up. %fs behind." % sleep_time) elif sleep_time < self.dt / 2: logging.warning( "Agent may not keep up (%.0f percent busy)" % (((self.dt - sleep_time) / self.dt) * 100)) # Wait for next timestep if sleep_time > 0 and not self.debug: time.sleep(sleep_time) if save: self._samples[condition].append(sample) self.finalize_sample() sample_ok = self.debug or input('Continue?') == 'y' if not sample_ok: print('Repeating') return sample def finalize_sample(self): """Adds additional waiting time to allow agent to reset. Sends final signals.""" if not self.debug: time.sleep(10) self.send_signals(self.T) def send_signals(self, t): """Sends edge-controlled OPCUA signals.""" if not self.debug: for signal in self.signals: if signal['condition'](t): self.send_signal(signal, True) thread.start_new_thread(self.send_signal, (signal, False, 2)) def send_signal(self, signal, value, sleep=0): """Sends an edge-controlled OPCUA signal.""" if sleep > 0: time.sleep(sleep) opcua_var = self.opcua_vars[signal['signal']] print("Signal", signal['signal'], value) opcua_var.set_data_value(ua.Variant(value, ua.VariantType.Boolean)) def close(self): """Releases any resources the agent may hold.""" if self.client is not None: self.client.disconnect()
class OpcUaConnector(Thread, Connector): def __init__(self, gateway, config, connector_type): self.__connector_type = connector_type self.statistics = {'MessagesReceived': 0, 'MessagesSent': 0} super().__init__() self.__gateway = gateway self.__server_conf = config.get("server") self.__interest_nodes = [] self.__available_object_resources = {} for mapping in self.__server_conf["mapping"]: if mapping.get("deviceNodePattern") is not None: self.__interest_nodes.append( {mapping["deviceNodePattern"]: mapping}) else: log.error( "deviceNodePattern in mapping: %s - not found, add property deviceNodePattern to processing this mapping", dumps(mapping)) if "opc.tcp" not in self.__server_conf.get("url"): opcua_url = "opc.tcp://" + self.__server_conf.get("url") else: opcua_url = self.__server_conf.get("url") self.client = Client( opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) if self.__server_conf["identity"]["type"] == "cert.PEM": try: ca_cert = self.__server_conf["identity"].get("caCert") private_key = self.__server_conf["identity"].get("privateKey") cert = self.__server_conf["identity"].get("cert") security_mode = self.__server_conf["identity"].get( "mode", "SignAndEncrypt") policy = self.__server_conf["security"] if cert is None or private_key is None: log.exception( "Error in ssl configuration - cert or privateKey parameter not found" ) raise security_string = policy + ',' + security_mode + ',' + cert + ',' + private_key if ca_cert is not None: security_string = security_string + ',' + ca_cert self.client.set_security_string(security_string) except Exception as e: log.exception(e) if self.__server_conf["identity"].get("username"): self.client.set_user( self.__server_conf["identity"].get("username")) if self.__server_conf["identity"].get("password"): self.client.set_password( self.__server_conf["identity"].get("password")) self.setName( self.__server_conf.get( "name", 'OPC-UA Default ' + ''.join(choice(ascii_lowercase) for _ in range(5))) + " Connector") self.__opcua_nodes = {} self._subscribed = {} self.data_to_send = [] self.__sub_handler = SubHandler(self) self.__stopped = False self.__connected = False self.daemon = True def is_connected(self): return self.__connected def open(self): self.__stopped = False self.start() log.info("Starting OPC-UA Connector") def run(self): while not self.__connected: try: self.__connected = self.client.connect() self.client.load_type_definitions() log.debug(self.client.get_namespace_array()[-1]) log.debug( self.client.get_namespace_index( self.client.get_namespace_array()[-1])) except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: log.debug("error on connection to OPC-UA server.") log.error(e) time.sleep(10) else: self.__connected = True log.info("OPC-UA connector %s connected to server %s", self.get_name(), self.__server_conf.get("url")) self.__opcua_nodes["root"] = self.client.get_root_node() self.__opcua_nodes["objects"] = self.client.get_objects_node() sub = self.client.create_subscription( self.__server_conf.get("scanPeriodInMillis", 500), self.__sub_handler) self.__search_name(self.__opcua_nodes["objects"], 2) self.__search_tags(self.__opcua_nodes["objects"], 2, sub) log.debug('Subscriptions: %s', self.subscribed) log.debug("Available methods: %s", self.__available_object_resources) while True: try: time.sleep(1) if self.data_to_send: self.__gateway.send_to_storage(self.get_name(), self.data_to_send.pop()) if self.__stopped: break except (KeyboardInterrupt, SystemExit): self.close() raise except Exception as e: self.close() log.exception(e) def close(self): self.__stopped = True self.client.disconnect() self.__connected = False log.info('%s has been stopped.', self.get_name()) def get_name(self): return self.name def on_attributes_update(self, content): log.debug(content) try: for server_variables in self.__available_object_resources[ content["device"]]['variables']: for attribute in content["data"]: for variable in server_variables: if attribute == variable: server_variables[variable].set_value( content["data"][variable]) except Exception as e: log.exception(e) def server_side_rpc_handler(self, content): try: for method in self.__available_object_resources[ content["device"]]['methods']: rpc_method = content["data"].get("method") if rpc_method is not None and method.get( rpc_method) is not None: arguments = content["data"].get("params") if type(arguments) is list: result = method["node"].call_method( method[rpc_method], *arguments) elif arguments is not None: result = method["node"].call_method( method[rpc_method], arguments) else: result = method["node"].call_method(method[rpc_method]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], {content["data"]["method"]: result}) log.debug("method %s result is: %s", method[rpc_method], result) except Exception as e: log.exception(e) def __search_name(self, node, recursion_level): try: for childId in node.get_children(): ch = self.client.get_node(childId) current_var_path = '.'.join( x.split(":")[1] for x in ch.get_path(20000, True)) if self.__interest_nodes: if ch.get_node_class() == ua.NodeClass.Object: for interest_node in self.__interest_nodes: for int_node in interest_node: subrecursion_level = recursion_level if subrecursion_level != recursion_level + len( interest_node[int_node] ["deviceNamePattern"].split("\\.")): if ch.get_display_name().Text in TBUtility.get_value(interest_node[int_node]["deviceNamePattern"], get_tag=True).split('.') or \ re.search(TBUtility.get_value(interest_node[int_node]["deviceNodePattern"], get_tag=True), ch.get_display_name().Text): self.__search_name( ch, subrecursion_level + 1) else: return elif ch.get_node_class() == ua.NodeClass.Variable: try: for interest_node in self.__interest_nodes: for int_node in interest_node: if interest_node[int_node].get( "deviceName") is None: try: name_pattern = TBUtility.get_value( interest_node[int_node] ["deviceNamePattern"], get_tag=True) log.debug(current_var_path) device_name_node = re.search( name_pattern.split('\\.')[-1], current_var_path) if device_name_node is not None: device_name = ch.get_value() if "${" + name_pattern + "}" in interest_node[ int_node][ "deviceNamePattern"]: full_device_name = interest_node[ int_node][ "deviceNamePattern"].replace( "${" + name_pattern + "}", device_name) elif device_name in interest_node[ int_node][ "deviceNamePattern"]: full_device_name = interest_node[ int_node][ "deviceNamePattern"].replace( name_pattern, device_name) else: log.error( "Name pattern not found." ) break interest_node[int_node][ "deviceName"] = full_device_name if self.__available_object_resources.get( full_device_name ) is None: self.__available_object_resources[ full_device_name] = { 'methods': [], 'variables': [] } if not self.__gateway.get_devices( ).get(full_device_name): self.__gateway.add_device( full_device_name, {"connector": None}) self.__gateway.update_device( full_device_name, "connector", self) else: try: if re.search( int_node.split( '\\.') [recursion_level - 2], ch. get_display_name( ).Text): self.__search_name( ch, recursion_level + 1) except IndexError: if re.search( int_node.split( '\\.')[-1], ch. get_display_name( ).Text): self.__search_name( ch, recursion_level + 1) except Exception as e: log.exception(e) else: break except BadWaitingForInitialData: pass elif not self.__interest_nodes: log.error( "Nodes in mapping not found, check your settings.") except Exception as e: log.exception(e) def __search_tags(self, node, recursion_level, sub=None): try: for childId in node.get_children(): ch = self.client.get_node(childId) current_var_path = '.'.join( x.split(":")[1] for x in ch.get_path(20000, True)) if self.__interest_nodes: if ch.get_node_class() == ua.NodeClass.Object: for interest_node in self.__interest_nodes: for int_node in interest_node: try: name_to_check = int_node.split('\\.')[ recursion_level - 1] if '\\.' in int_node else int_node name_to_check = int_node.split( '.' )[recursion_level - 1] if '.' in int_node else name_to_check except IndexError: name_to_check = int_node.split( '\\.' )[-1] if '\\.' in int_node else int_node name_to_check = int_node.split( '.' )[-1] if '.' in int_node else name_to_check if re.search(name_to_check, ch.get_display_name().Text): try: methods = ch.get_methods() for method in methods: self.__available_object_resources[ interest_node[int_node] ["deviceName"]][ "methods"].append({ method.get_display_name( ).Text: method, "node": ch }) except Exception as e: log.exception(e) for tag in interest_node[int_node][ "timeseries"] + interest_node[ int_node]["attributes"]: subrecursion_level = recursion_level if subrecursion_level != recursion_level + len( tag["path"].split("\\.")): self.__search_tags( ch, subrecursion_level + 1, sub) else: return self.__search_tags(ch, recursion_level + 1, sub) elif ch.get_node_class() == ua.NodeClass.Variable: try: for interest_node in self.__interest_nodes: for int_node in interest_node: if interest_node[int_node].get( "attributes_updates"): try: for attribute_update in interest_node[ int_node][ "attributes_updates"]: if attribute_update[ "attributeOnDevice"] == ch.get_display_name( ).Text: self.__available_object_resources[ interest_node[int_node] ["deviceName"]][ 'variables'].append({ attribute_update["attributeOnThingsBoard"]: ch, }) except Exception as e: log.exception(e) name_to_check = int_node.split( '\\.' )[-1] if '\\.' in int_node else int_node name_to_check = int_node.split( '.' )[-1] if '.' in int_node else name_to_check if re.search( name_to_check.replace('$', ''), current_var_path): tags = [] if interest_node[int_node].get( "attributes"): tags.extend(interest_node[int_node] ["attributes"]) if interest_node[int_node].get( "timeseries"): tags.extend(interest_node[int_node] ["timeseries"]) for tag in tags: target = TBUtility.get_value( tag["path"], get_tag=True) try: tag_name_for_check = target.split( '\\.' )[recursion_level - 1] if '\\.' in target else target tag_name_for_check = target.split( '.' )[recursion_level - 1] if '.' in target else tag_name_for_check except IndexError: tag_name_for_check = target.split( '\\.' )[-1] if '\\.' in target else target tag_name_for_check = target.split( '.' )[-1] if '.' in target else tag_name_for_check current_node_name = ch.get_display_name( ).Text if current_node_name == tag_name_for_check: sub.subscribe_data_change(ch) if interest_node[int_node].get( "uplink_converter" ) is None: if interest_node[ int_node].get( 'converter' ) is None: converter = OpcUaUplinkConverter( interest_node[ int_node]) else: converter = TBUtility.check_and_import( self. __connector_type, interest_node[ int_node] ['converter']) interest_node[int_node][ "uplink_converter"] = converter else: converter = interest_node[ int_node][ "uplink_converter"] self.subscribed[ch] = { "converter": converter, "path": current_var_path } else: return except BadWaitingForInitialData: pass elif not self.__interest_nodes: log.error( "Nodes in mapping not found, check your settings.") except Exception as e: log.exception(e) @property def subscribed(self): return self._subscribed
class Cliente(): def __init__(self, direccion, suscribir_eventos, SubHandler): self.direccion = direccion self.client = Client(direccion) self.alturas = {'H1': 0, 'H2': 0, 'H3':0, 'H4':0} self.temperaturas = {'T1': 0, 'T2': 0, 'T3':0, 'T4':0} self.valvulas = {'valvula1':0, 'valvula2':0} self.razones = {'razon1':0, 'razon2': 0} self.subscribir_eventos = suscribir_eventos self.periodo = 100 # cantidad de milisegundos para revisar las variables subscritas self.SubHandlerClass = SubHandler def Instanciacion(self): self.root = self.client.get_root_node() self.objects = self.client.get_objects_node() self.Tanques = self.objects.get_child(['2:Proceso_Tanques','2:Tanques']) self.Valvulas = self.objects.get_child(['2:Proceso_Tanques', '2:Valvulas']) self.Razones = self.objects.get_child(['2:Proceso_Tanques', '2:Razones']) # Obtención de las alturas self.alturas['H1'] = self.Tanques.get_child(['2:Tanque1', '2:h']) self.alturas['H2'] = self.Tanques.get_child(['2:Tanque2', '2:h']) self.alturas['H3'] = self.Tanques.get_child(['2:Tanque3', '2:h']) self.alturas['H4'] = self.Tanques.get_child(['2:Tanque4', '2:h']) # Obtención de temperaturas self.temperaturas['T1'] = self.Tanques.get_child(['2:Tanque1', '2:T']) self.temperaturas['T2'] = self.Tanques.get_child(['2:Tanque2', '2:T']) self.temperaturas['T3'] = self.Tanques.get_child(['2:Tanque3', '2:T']) self.temperaturas['T4'] = self.Tanques.get_child(['2:Tanque4', '2:T']) # Obtención de los pumps self.valvulas['valvula1'] = self.Valvulas.get_child(['2:Valvula1', '2:u']) self.valvulas['valvula2'] = self.Valvulas.get_child(['2:Valvula2', '2:u']) # Obtención de los switches self.razones['razon1'] = self.Razones.get_child(['2:Razon1', '2:gamma']) self.razones['razon2'] = self.Razones.get_child(['2:Razon2', '2:gamma']) # Evento (alarma en este caso) if self.subscribir_eventos: self.myevent = self.root.get_child(["0:Types", "0:EventTypes", "0:BaseEventType", "2:Alarma_nivel"])#Tipo de evento self.obj_event = self.objects.get_child(['2:Proceso_Tanques', '2:Alarmas', '2:Alarma_nivel'])#Objeto Evento self.handler_event = self.SubHandlerClass() self.sub_event = self.client.create_subscription(self.periodo, self.handler_event)#Subscripción al evento self.handle_event = self.sub_event.subscribe_events(self.obj_event, self.myevent) def subscribir_cv(self): # Subscripción a las variables controladas self.handler_cv = self.SubHandlerClass() self.sub_cv = self.client.create_subscription(self.periodo, self.handler_cv) for key, var in self.alturas.items(): self.sub_cv.subscribe_data_change(var) for key, var in self.temperaturas.items(): self.sub_cv.subscribe_data_change(var) def subscribir_mv(self): # Subscripación a las variables manipuladas self.handler_mv = self.SubHandlerClass() self.sub_mv = self.client.create_subscription(self.periodo, self.handler_mv) for key, var in self.valvulas.items(): self.sub_mv.subscribe_data_change(var) for key, var in self.razones.items(): self.sub_mv.subscribe_data_change(var) def conectar(self): self.client.connect() self.objects = self.client.get_objects_node() print('Cliente OPCUA se ha conectado') self.Instanciacion() #self.client.disconnect() #print('Cliente no se ha podido conectar') #cliente = Cliente("opc.tcp://localhost:4840/freeopcua/server/", suscribir_eventos=True, SubHandlerClass=SubHandler) #cliente.conectar() #cliente.subscribir_mv() # Se subscribe a las variables manipuladas
class TankSystem: def __init__(self): self.client = None self.objects_node = None self.root_node = None self.connected = False self.sub_handler = SubHandler() self.max_size = 600 self.past_values = { 'time': collections.deque(maxlen=self.max_size), 'tank_1': collections.deque(maxlen=self.max_size), 'tank_2': collections.deque(maxlen=self.max_size), 'tank_3': collections.deque(maxlen=self.max_size), 'tank_4': collections.deque(maxlen=self.max_size), 'valve_1': collections.deque(maxlen=self.max_size), 'valve_2': collections.deque(maxlen=self.max_size), } self.seconds = time.time() self.start_time = time.time() self.logging_time = 1 def connect(self): if not self.connected: # self.client = Client('opc.tcp://192.168.1.23:4840/freeopcua/server/') self.client = Client( 'opc.tcp://192.168.1.23:4840/freeopcua/server/') self.client.connect() self.objects_node = self.client.get_objects_node() self.connected = True self.log_values() self.root_node = self.client.get_root_node() myevent = self.root_node.get_child([ '0:Types', '0:EventTypes', '0:BaseEventType', '2:Alarma_nivel' ]) obj = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Alarmas', '2:Alarma_nivel']) sub = self.client.create_subscription(100, self.sub_handler) handle = sub.subscribe_events(obj, myevent) # sub.unsubscribe(handle) # sub.delete() def disconnect(self): if self.connected: self.connected = False self.client.disconnect() self.client = None self.objects_node = None self.past_values = { 'time': collections.deque(maxlen=self.max_size), 'tank_1': collections.deque(maxlen=self.max_size), 'tank_2': collections.deque(maxlen=self.max_size), 'tank_3': collections.deque(maxlen=self.max_size), 'tank_4': collections.deque(maxlen=self.max_size), 'valve_1': collections.deque(maxlen=self.max_size), 'valve_2': collections.deque(maxlen=self.max_size), } @property def tank_1(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Tanques', '2:Tanque1', '2:h']) value = node.get_value() return value @property def tank_2(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Tanques', '2:Tanque2', '2:h']) value = node.get_value() return value @property def tank_3(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Tanques', '2:Tanque3', '2:h']) value = node.get_value() return value @property def tank_4(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Tanques', '2:Tanque4', '2:h']) value = node.get_value() return value @property def valve_1(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Valvulas', '2:Valvula1', '2:u']) value = node.get_value() return value @valve_1.setter def valve_1(self, value): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Valvulas', '2:Valvula1', '2:u']) node.set_value(value) @property def valve_2(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Valvulas', '2:Valvula2', '2:u']) value = node.get_value() return value @valve_2.setter def valve_2(self, value): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Valvulas', '2:Valvula2', '2:u']) node.set_value(value) @property def gamma_1(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Razones', '2:Razon1', '2:gamma']) value = node.get_value() return value @gamma_1.setter def gamma_1(self, value): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Razones', '2:Razon1', '2:gamma']) node.set_value(value) @property def gamma_2(self): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Razones', '2:Razon2', '2:gamma']) value = node.get_value() return value @gamma_2.setter def gamma_2(self, value): node = self.objects_node.get_child( ['2:Proceso_Tanques', '2:Razones', '2:Razon2', '2:gamma']) node.set_value(value) def log_values(self): if self.connected: self.seconds = time.time() self.past_values['time'].append( round(time.time() - self.start_time, 3)) self.past_values['tank_1'].append(round(self.tank_1, 3)) self.past_values['tank_2'].append(round(self.tank_2, 3)) self.past_values['tank_3'].append(round(self.tank_3, 3)) self.past_values['tank_4'].append(round(self.tank_4, 3)) self.past_values['valve_1'].append(round(self.valve_1, 3)) self.past_values['valve_2'].append(round(self.valve_2, 3))
class OpcUaConnector(Thread, Connector): def __init__(self, gateway, config, connector_type): self._connector_type = connector_type self.statistics = {'MessagesReceived': 0, 'MessagesSent': 0} super().__init__() self.__gateway = gateway self.__server_conf = config.get("server") self.__interest_nodes = [] self.__available_object_resources = {} self.__show_map = self.__server_conf.get("showMap", False) self.__previous_scan_time = 0 for mapping in self.__server_conf["mapping"]: if mapping.get("deviceNodePattern") is not None: self.__interest_nodes.append( {mapping["deviceNodePattern"]: mapping}) else: log.error( "deviceNodePattern in mapping: %s - not found, add property deviceNodePattern to processing this mapping", dumps(mapping)) if "opc.tcp" not in self.__server_conf.get("url"): self.__opcua_url = "opc.tcp://" + self.__server_conf.get("url") else: self.__opcua_url = self.__server_conf.get("url") self.client = Client( self.__opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) if self.__server_conf["identity"].get("type") == "cert.PEM": try: ca_cert = self.__server_conf["identity"].get("caCert") private_key = self.__server_conf["identity"].get("privateKey") cert = self.__server_conf["identity"].get("cert") security_mode = self.__server_conf["identity"].get( "mode", "SignAndEncrypt") policy = self.__server_conf["security"] if cert is None or private_key is None: log.exception( "Error in ssl configuration - cert or privateKey parameter not found" ) raise RuntimeError( "Error in ssl configuration - cert or privateKey parameter not found" ) security_string = policy + ',' + security_mode + ',' + cert + ',' + private_key if ca_cert is not None: security_string = security_string + ',' + ca_cert self.client.set_security_string(security_string) except Exception as e: log.exception(e) if self.__server_conf["identity"].get("username"): self.client.set_user( self.__server_conf["identity"].get("username")) if self.__server_conf["identity"].get("password"): self.client.set_password( self.__server_conf["identity"].get("password")) self.setName( self.__server_conf.get( "name", 'OPC-UA ' + ''.join(choice(ascii_lowercase) for _ in range(5))) + " Connector") self.__opcua_nodes = {} self._subscribed = {} self.__sub = None self.__sub_handler = SubHandler(self) self.data_to_send = [] self.__stopped = False self.__connected = False self.daemon = True def is_connected(self): return self.__connected def open(self): self.__stopped = False self.start() log.info("Starting OPC-UA Connector") def run(self): while not self.__connected: try: self.client.connect() try: self.client.load_type_definitions() except Exception as e: log.debug(e) log.debug("Error on loading type definitions.") log.debug(self.client.get_namespace_array()[-1]) log.debug( self.client.get_namespace_index( self.client.get_namespace_array()[-1])) except ConnectionRefusedError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except OSError: log.error( "Connection refused on connection to OPC-UA server with url %s", self.__server_conf.get("url")) time.sleep(10) except Exception as e: log.debug("error on connection to OPC-UA server.") log.error(e) time.sleep(10) else: self.__connected = True log.info("OPC-UA connector %s connected to server %s", self.get_name(), self.__server_conf.get("url")) self.__initialize_client() while not self.__stopped: try: time.sleep(.1) self.__check_connection() if not self.__connected and not self.__stopped: self.client.connect() self.__initialize_client() log.info("Reconnected to the OPC-UA server - %s", self.__server_conf.get("url")) elif not self.__stopped: if self.__server_conf.get( "disableSubscriptions", False ) and time.time( ) * 1000 - self.__previous_scan_time > self.__server_conf.get( "scanPeriodInMillis", 60000): self.scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 # giusguerrini, 2020-09-24: Fix: flush event set and send all data to platform, # so data_to_send doesn't grow indefinitely in case of more than one value change # per cycle, and platform doesn't lose events. # NOTE: possible performance improvement: use a map to store only one event per # variable to reduce frequency of messages to platform. while self.data_to_send: self.__gateway.send_to_storage(self.get_name(), self.data_to_send.pop()) if self.__stopped: self.close() break except (KeyboardInterrupt, SystemExit): self.close() raise except FuturesTimeoutError: self.__check_connection() except Exception as e: log.error( "Connection failed on connection to OPC-UA server with url %s", self.__server_conf.get("url")) log.exception(e) self.client = Client( self.__opcua_url, timeout=self.__server_conf.get("timeoutInMillis", 4000) / 1000) self._subscribed = {} self.__available_object_resources = {} time.sleep(10) def __check_connection(self): try: node = self.client.get_root_node() node.get_children() if not self.__server_conf.get("disableSubscriptions", False) and ( not self.__connected or not self.subscribed): self.__sub = self.client.create_subscription( self.__server_conf.get("subCheckPeriodInMillis", 500), self.__sub_handler) self.__connected = True except ConnectionRefusedError: self.__connected = False self._subscribed = {} self.__available_object_resources = {} self.__sub = None except OSError: self.__connected = False self._subscribed = {} self.__available_object_resources = {} self.__sub = None except FuturesTimeoutError: self.__connected = False self._subscribed = {} self.__available_object_resources = {} self.__sub = None except AttributeError: self.__connected = False self._subscribed = {} self.__available_object_resources = {} self.__sub = None except Exception as e: self.__connected = False self._subscribed = {} self.__available_object_resources = {} self.__sub = None log.exception(e) def close(self): self.__stopped = True if self.__connected: self.client.disconnect() self.__connected = False log.info('%s has been stopped.', self.get_name()) def get_name(self): return self.name def on_attributes_update(self, content): log.debug(content) try: for server_variables in self.__available_object_resources[ content["device"]]['variables']: for attribute in content["data"]: for variable in server_variables: if attribute == variable: try: server_variables[variable].set_value( content["data"][variable]) except Exception: server_variables[variable].set_attribute( ua.AttributeIds.Value, ua.DataValue(content["data"][variable])) except Exception as e: log.exception(e) def server_side_rpc_handler(self, content): try: rpc_method = content["data"].get("method") for method in self.__available_object_resources[ content["device"]]['methods']: if rpc_method is not None and method.get( rpc_method) is not None: arguments_from_config = method["arguments"] arguments = content["data"].get( "params") if content["data"].get( "params") is not None else arguments_from_config try: if isinstance(arguments, list): result = method["node"].call_method( method[rpc_method], *arguments) elif arguments is not None: try: result = method["node"].call_method( method[rpc_method], arguments) except ua.UaStatusCodeError as e: if "BadTypeMismatch" in str(e) and isinstance( arguments, int): result = method["node"].call_method( method[rpc_method], float(arguments)) else: result = method["node"].call_method( method[rpc_method]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { content["data"]["method"]: result, "code": 200 }) log.debug("method %s result is: %s", method[rpc_method], result) except Exception as e: log.exception(e) self.__gateway.send_rpc_reply(content["device"], content["data"]["id"], { "error": str(e), "code": 500 }) else: log.error("Method %s not found for device %s", rpc_method, content["device"]) self.__gateway.send_rpc_reply( content["device"], content["data"]["id"], { "error": "%s - Method not found" % (rpc_method), "code": 404 }) except Exception as e: log.exception(e) def __initialize_client(self): self.__opcua_nodes["root"] = self.client.get_objects_node() self.__opcua_nodes["objects"] = self.client.get_objects_node() self.scan_nodes_from_config() self.__previous_scan_time = time.time() * 1000 log.debug('Subscriptions: %s', self.subscribed) log.debug("Available methods: %s", self.__available_object_resources) def scan_nodes_from_config(self): try: if self.__interest_nodes: for device_object in self.__interest_nodes: for current_device in device_object: try: device_configuration = device_object[ current_device] devices_info_array = self.__search_general_info( device_configuration) for device_info in devices_info_array: if device_info is not None and device_info.get( "deviceNode") is not None: self.__search_nodes_and_subscribe( device_info) self.__save_methods(device_info) self.__search_attribute_update_variables( device_info) else: log.error( "Device node is None, please check your configuration." ) log.debug( "Current device node is: %s", str( device_configuration.get( "deviceNodePattern"))) break except BrokenPipeError: log.debug("Broken Pipe. Connection lost.") except OSError: log.debug("Stop on scanning.") except FuturesTimeoutError: self.__check_connection() except Exception as e: log.exception(e) log.debug(self.__interest_nodes) except Exception as e: log.exception(e) def __search_nodes_and_subscribe(self, device_info): sub_nodes = [] information_types = { "attributes": "attributes", "timeseries": "telemetry" } for information_type in information_types: for information in device_info["configuration"][information_type]: information_key = information["key"] config_path = TBUtility.get_value(information["path"], get_tag=True) information_path = self._check_path(config_path, device_info["deviceNode"]) information["path"] = '${%s}' % information_path information_nodes = [] self.__search_node(device_info["deviceNode"], information_path, result=information_nodes) for information_node in information_nodes: if information_node is not None: try: information_value = information_node.get_value() except: log.error("Err get_value: %s", str(information_node)) continue log.debug( "Node for %s \"%s\" with path: %s - FOUND! Current values is: %s", information_type, information_key, information_path, str(information_value)) if device_info.get("uplink_converter") is None: configuration = { **device_info["configuration"], "deviceName": device_info["deviceName"], "deviceType": device_info["deviceType"] } if device_info["configuration"].get( 'converter') is None: converter = OpcUaUplinkConverter(configuration) else: converter = TBModuleLoader.import_module( self._connector_type, configuration) device_info["uplink_converter"] = converter else: converter = device_info["uplink_converter"] self.subscribed[information_node] = { "converter": converter, "path": information_path, "config_path": config_path } if not device_info.get( information_types[information_type]): device_info[ information_types[information_type]] = [] converted_data = converter.convert( (config_path, information_path), information_value) self.statistics['MessagesReceived'] = self.statistics[ 'MessagesReceived'] + 1 self.data_to_send.append(converted_data) self.statistics['MessagesSent'] = self.statistics[ 'MessagesSent'] + 1 log.debug("Data to ThingsBoard: %s", converted_data) if not self.__server_conf.get("disableSubscriptions", False): sub_nodes.append(information_node) else: log.error( "Node for %s \"%s\" with path %s - NOT FOUND!", information_type, information_key, information_path) if not self.__server_conf.get("disableSubscriptions", False): if self.__sub is None: self.__sub = self.client.create_subscription( self.__server_conf.get("subCheckPeriodInMillis", 500), self.__sub_handler) if sub_nodes: self.__sub.subscribe_data_change(sub_nodes) log.debug("Added subscription to nodes: %s", str(sub_nodes)) def __save_methods(self, device_info): try: if self.__available_object_resources.get( device_info["deviceName"]) is None: self.__available_object_resources[ device_info["deviceName"]] = {} if self.__available_object_resources[ device_info["deviceName"]].get("methods") is None: self.__available_object_resources[ device_info["deviceName"]]["methods"] = [] if device_info["configuration"].get("rpc_methods", []): node = device_info["deviceNode"] for method_object in device_info["configuration"][ "rpc_methods"]: method_node_path = self._check_path( method_object["method"], node) methods = [] self.__search_node(node, method_node_path, True, result=methods) for method in methods: if method is not None: node_method_name = method.get_display_name().Text self.__available_object_resources[ device_info["deviceName"]]["methods"].append({ node_method_name: method, "node": node, "arguments": method_object.get("arguments") }) else: log.error( "Node for method with path %s - NOT FOUND!", method_node_path) except Exception as e: log.exception(e) def __search_attribute_update_variables(self, device_info): try: if device_info["configuration"].get("attributes_updates", []): node = device_info["deviceNode"] device_name = device_info["deviceName"] if self.__available_object_resources.get(device_name) is None: self.__available_object_resources[device_name] = {} if self.__available_object_resources[device_name].get( "variables") is None: self.__available_object_resources[device_name][ "variables"] = [] for attribute_update in device_info["configuration"][ "attributes_updates"]: attribute_path = self._check_path( attribute_update["attributeOnDevice"], node) attribute_nodes = [] self.__search_node(node, attribute_path, result=attribute_nodes) for attribute_node in attribute_nodes: if attribute_node is not None: self.__available_object_resources[device_name][ "variables"].append({ attribute_update["attributeOnThingsBoard"]: attribute_node }) else: log.error( "Attribute update node with path \"%s\" - NOT FOUND!", attribute_path) except Exception as e: log.exception(e) def __search_general_info(self, device): result = [] match_devices = [] self.__search_node(self.__opcua_nodes["root"], TBUtility.get_value(device["deviceNodePattern"], get_tag=True), result=match_devices) for device_node in match_devices: if device_node is not None: result_device_dict = { "deviceName": None, "deviceType": None, "deviceNode": device_node, "configuration": deepcopy(device) } name_pattern_config = device["deviceNamePattern"] name_expression = TBUtility.get_value(name_pattern_config, get_tag=True) if "${" in name_pattern_config and "}" in name_pattern_config: log.debug("Looking for device name") device_name_from_node = "" if name_expression == "$DisplayName": device_name_from_node = device_node.get_display_name( ).Text elif name_expression == "$BrowseName": device_name_from_node = device_node.get_browse_name( ).Name elif name_expression == "$NodeId.Identifier": device_name_from_node = str( device_node.nodeid.Identifier) else: name_path = self._check_path(name_expression, device_node) device_name_node = [] self.__search_node(device_node, name_path, result=device_name_node) device_name_node = device_name_node[0] if device_name_node is not None: device_name_from_node = device_name_node.get_value( ) if device_name_from_node == "": log.error( "Device name node not found with expression: %s", name_expression) return None full_device_name = name_pattern_config.replace( "${" + name_expression + "}", str(device_name_from_node)).replace( name_expression, str(device_name_from_node)) else: full_device_name = name_expression result_device_dict["deviceName"] = full_device_name log.debug("Device name: %s", full_device_name) if device.get("deviceTypePattern"): device_type_expression = TBUtility.get_value( device["deviceTypePattern"], get_tag=True) if "${" in device_type_expression and "}" in device_type_expression: type_path = self._check_path(device_type_expression, device_node) device_type_node = [] self.__search_node(device_node, type_path, result=device_type_node) device_type_node = device_type_node[0] if device_type_node is not None: device_type = device_type_node.get_value() full_device_type = device_type_expression.replace( "${" + device_type_expression + "}", device_type).replace(device_type_expression, device_type) else: log.error( "Device type node not found with expression: %s", device_type_expression) full_device_type = "default" else: full_device_type = device_type_expression result_device_dict["deviceType"] = full_device_type log.debug("Device type: %s", full_device_type) else: result_device_dict["deviceType"] = "default" result.append(result_device_dict) else: log.error( "Device node not found with expression: %s", TBUtility.get_value(device["deviceNodePattern"], get_tag=True)) return result # # get fullpath of node as string # # this is verry slow # path = '\\.'.join(char.split(":")[1] for char in node.get_path(200000, True)) # i think we don't need \\. # def get_node_path(self, node): ID = node.nodeid.Identifier if ID == 85: return 'Root.Objects' # this is Root if type(ID) == int: ID = node.get_browse_name( ).Name # for int Identifer we take browsename return 'Root.Objects.' + ID def __search_node(self, current_node, fullpath, search_method=False, result=None): if result is None: result = [] try: if regex.match(r"ns=\d*;[isgb]=.*", fullpath, regex.IGNORECASE): if self.__show_map: log.debug("Looking for node with config") node = self.client.get_node(fullpath) if node is None: log.warning("NODE NOT FOUND - using configuration %s", fullpath) else: log.debug("Found in %s", node) result.append(node) else: fullpath_pattern = regex.compile(fullpath) full1 = fullpath.replace('\\\\.', '.') #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True)) current_node_path = self.get_node_path(current_node) # we are allways the parent child_node_parent_class = current_node.get_node_class() new_parent = current_node for child_node in current_node.get_children(): new_node_class = child_node.get_node_class() # this will not change you can do it outside th loop # basis Description of node.get_parent() function, sometime child_node.get_parent() return None #new_parent = child_node.get_parent() #if (new_parent is None): # child_node_parent_class = current_node.get_node_class() #else: # child_node_parent_class = child_node.get_parent().get_node_class() #current_node_path = '\\.'.join(char.split(":")[1] for char in current_node.get_path(200000, True)) #new_node_path = '\\\\.'.join(char.split(":")[1] for char in child_node.get_path(200000, True)) new_node_path = self.get_node_path(child_node) if child_node_parent_class == ua.NodeClass.View and new_parent is not None: parent_path = self.get_node_path(new_parent) #parent_path = '\\.'.join(char.split(":")[1] for char in new_parent.get_path(200000, True)) fullpath = fullpath.replace(current_node_path, parent_path) nnp1 = new_node_path.replace('\\\\.', '.') nnp2 = new_node_path.replace('\\\\', '\\') if self.__show_map: log.debug("SHOW MAP: Current node path: %s", new_node_path) regex_fullmatch = regex.fullmatch(fullpath_pattern, nnp1) or \ nnp2 == full1 or \ nnp2 == fullpath or \ nnp1 == full1 if regex_fullmatch: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", nnp2) result.append(child_node) else: regex_search = fullpath_pattern.fullmatch(nnp1, partial=True) or \ nnp2 in full1 or \ nnp1 in full1 if regex_search: if self.__show_map: log.debug( "SHOW MAP: Current node path: %s - NODE FOUND", new_node_path) if new_node_class == ua.NodeClass.Object: if self.__show_map: log.debug("SHOW MAP: Search in %s", new_node_path) self.__search_node(child_node, fullpath, result=result) elif new_node_class == ua.NodeClass.Variable: log.debug("Found in %s", new_node_path) result.append(child_node) elif new_node_class == ua.NodeClass.Method and search_method: log.debug("Found in %s", new_node_path) result.append(child_node) except CancelledError: log.error( "Request during search has been canceled by the OPC-UA server." ) except BrokenPipeError: log.error("Broken Pipe. Connection lost.") except OSError: log.debug("Stop on scanning.") except Exception as e: log.exception(e) def _check_path(self, config_path, node): if regex.match(r"ns=\d*;[isgb]=.*", config_path, regex.IGNORECASE): return config_path if re.search(r"^root", config_path.lower()) is None: node_path = self.get_node_path(node) #node_path = '\\\\.'.join(char.split(":")[1] for char in node.get_path(200000, True)) if config_path[-3:] != '\\.': information_path = node_path + '\\\\.' + config_path.replace( '\\', '\\\\') else: information_path = node_path + config_path.replace( '\\', '\\\\') else: information_path = config_path result = information_path[:] return result @property def subscribed(self): return self._subscribed
class Plant(threading.Thread): def __init__(self, ip): super().__init__() self.client = Client(ip) self.objects = self.assign_object() self.heights = {'h1': 0, 'h2': 0, 'h3': 0, 'h4': 0} self._u = [0, 0] self.sample_time = 0.001 self.daemon = True ## Que termine de medir cuando termine el programa (debug) self.event_measure = threading.Event() def assign_object(self): """Conecta el cliente y retorna el objeto donde se encuentra la planta en el servidor""" self.client.connect() return self.client.get_objects_node() def get_heights(self): """Obtiene las alturas y las almacena en un diccionario con indices h1, h2, h3, h4""" for i in range(1, 5): node = self.objects.get_child([ '2:Parametros', f'2:Altura {i}' ]) ## '2:Proceso_Tanques', '2:Tanques', f'2:Tanque{i}', '2:h' self.heights[f'h{i}'] = node.get_value( ) #NoneType object does not support item assig def get_show_heights(self): return [x for x in self.heights.values()] def suscribe_alarm(self): """Suscribe la alarma que esta overwrited en la clase SubHandler""" alarm = self.objects.get_child( ['2:Proceso_Tanques', '2:Alarmas', f'2:Alarma_nivel']) handler = SubHandler() sub = self.client.create_subscription(500, handler) handle = sub.subscribe_events(alarm) time.sleep(0.1) def assign_valve_value(self): """Constantemente, asigna las variablaes almacenadas en el atributo self.u, a la planta""" u1 = self.objects.get_child([ '2:Parametros', '2: Válvula 1' ]) ## '2:Proceso_Tanques', '2:Valvulas', f'2:Valvula1', '2:u' u2 = self.objects.get_child([ '2:Parametros', '2: Válvula 2' ]) ## '2:Proceso_Tanques', '2:Valvulas', f'2:Valvula2', '2:u' u1.set_value(self.u[0]) u2.set_value(self.u[1]) @property def u(self): return self._u @u.setter def u(self, value): if not None in value: self._u = list(value) else: self._u = self._u def run(self): # self.suscribe_alarm() // no hay alarma en simulacion while True: if sum(self.u) > 0: self.assign_valve_value() self.u = [0, 0] self.event_measure.set( ) ## Revisar esto porque no se esta cumpliendo self.get_heights() time.sleep(self.sample_time)
if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://192.168.157.244:40840") # 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() #for t in obje 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
def event(self, handle, event): print("Python: New event", handle, event) if __name__ == "__main__": #from IPython import embed logging.basicConfig(level=logging.WARN) client = Client("opc.tcp://*****:*****@localhost:53530/OPCUA/SimulationServer/") try: client.connect() root = client.get_root_node() print("Root is", root) print("childs of root are: ", root.get_children()) print("name of root is", root.get_browse_name()) objects = client.get_objects_node() print("childs og objects are: ", objects.get_children()) myfloat = client.get_node("ns=4;s=Float") mydouble = client.get_node("ns=4;s=Double") myint64 = client.get_node("ns=4;s=Int64") myuint64 = client.get_node("ns=4;s=UInt64") myint32 = client.get_node("ns=4;s=Int32") myuint32 = client.get_node("ns=4;s=UInt32") var = client.get_node(ua.NodeId("Random1", 5)) print("var is: ", var) print("value of var is: ", var.get_value()) var.set_value(ua.Variant([23], ua.VariantType.Double)) print("setting float value") myfloat.set_value(ua.Variant(1.234, ua.VariantType.Float)) print("reading float value: ", myfloat.get_value())
class OPCPLC(PLC): """PLC con comunicación OPC UA. Args:: address (str): Dirección IP (o nombre) del controlador. port (int): Puerto de conexión. interval (float): Intervalo de actualización en segundos. Attributes: subscription: Grupo al que se suscriben las variables. handler: Manejador de las actualizaciones de los valores. objects: Nodo de objetos. tagbynodeid (Tag{}): Diccionario de variables por identificador de nodo. client: Cliente OPC UA. opctype (VariantType{}): Principales tipos de datos OPC. """ opctype={ 1:ua.VariantType.Boolean, 2:ua.VariantType.SByte, 3:ua.VariantType.Byte, 4:ua.VariantType.Int16, 5:ua.VariantType.UInt16, 6:ua.VariantType.Int32, 7:ua.VariantType.UInt32, 8:ua.VariantType.Int64, 9:ua.VariantType.UInt64, 10:ua.VariantType.Float, 11:ua.VariantType.Double, 12:ua.VariantType.String, 13:ua.VariantType.DateTime} class Memory(PLC.Memory): ''' Representacióne un área de memoria. No usada en OPC. Args: plc (PLC): Controlador al que pertenece. Attributes: tag (tag{}): Diccionario de variables ordenadas por nombre. tagbyaddress (tag{}): Diccionario de variables ordenadas por dirección. ''' class Tag(PLC.Memory.Tag): ''' Variable Args: memory (Memory): Memoria a la que pertenece (no usada en OPC). key (str): Nombre. description (str): Descripción. address: Dirección. Se pasa la ruta hasta el nodo con el carácter '\' como separación. Los nodos vienen precedidos del tipo en forma de entero, separados por dos puntos. Por ejemplo, una dirección podria ser "2:Data\2:Static\2:Scalar\2:Variable". Para más información de una estructura en particular, una vez conectado llamar al método print_tree(). Attributes: node: Nodo OPC asociado. type: Tipo de dato. value: Valor. subscriptor (Subcriptor[]): Objetos suscritos a los cambios. ''' def __init__(self, memory:PLC.Memory, key:str, description:str="", address=None): self.node=None self.type=None super().__init__(memory,key,description,address) def opcsubscribe(self): ''' Suscripción al nodo OPC. ''' plc=self.memory.plc address=self.address.split("\\") self.node=plc.objects.get_child(address) self.type=int(self.node.get_data_type().Identifier) plc.tagbynodeid[self.node.nodeid.Identifier]=self plc.subscription.subscribe_data_change(self.node) self.node.get_value() def set(self,value): ''' Modifica o asigna el valor de una variable. Args: value: Nuevo valor de la variable. ''' try: if self.type==1: self.node.set_value(ua.Variant(bool(value),OPCPLC.opctype[self.type])) if self.type>=2 and self.type<=9: self.node.set_value(ua.Variant(int(value),OPCPLC.opctype[self.type])) if self.type>=10 and self.type<=11: self.node.set_value(ua.Variant(float(value),OPCPLC.opctype[self.type])) if self.type==12: self.node.set_value(ua.Variant(str(value),OPCPLC.opctype[self.type])) if self.type==13: self.node.set_value(ua.Variant(datetime.strptime(value,"%Y-%m-%d %H:%M:%S"),OPCPLC.opctype[self.type])) self.__update(value) except Exception as e: printexception(e,"Error in assignment. Tag="+self.key+", Value="+value) def __init__(self, address:str, port:int=502, interval:float=3): super().__init__() self.address=address self.port=port self.interval=interval self.handler=OPCPLC.Handler(self) self.subscription=None self.objects=None self.tagbynodeid={} self.client = Client("opc.tcp://"+self.address+":"+str(self.port)+"/") self.create("") def connect(self): ''' Conexión con el controlador ''' try: self.client.connect() self.objects=self.client.get_objects_node() self.subscription=self.client.create_subscription(self.interval, self.handler) for key_memory in self.memory: memory=self.get(key_memory) for key_tag in memory: memory.get(key_tag).opcsubscribe() self.connected=True except Exception as e: printexception(e,"Error connecting to OPC server") def __tree(self, root, level:int=0): ''' Recursión para imprimir el árbol de nodos. Args: root: nodo raíz en la recursión. level (int): Nivel de recursión. ''' nodes=root.get_children() for node in nodes: node2=self.client.get_node(node.nodeid) name=node2.get_browse_name().to_string() print((' '*level)+name) self.tree(node, level+1) def print_tree(self): ''' Imprime la estructura de nodos. ''' self.__tree(self.client.get_objects_node()) class Handler(object): ''' Manejador de los cambios en los valores de los nodos. Args: plc (PLC): Controlador. ''' def __init__(self,plc): self.plc=plc def datachange_notification(self, node, val, data): ''' Método llamado cuando cambia el valor de un nodo. Args: node: Nodo. val: Valor. data: Datos. ''' self.plc.tagbynodeid[node.nodeid.Identifier].update(val)