def uahistoryread(): parser = argparse.ArgumentParser(description="Read history of a node") add_common_args(parser) parser.add_argument( "--starttime", default="", help="Start time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time" ) parser.add_argument( "--endtime", default="", help="End time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time" ) args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) starttime = str_to_datetime(args.starttime) endtime = str_to_datetime(args.endtime) print( "Reading raw history of node {} at {}; start at {}, end at {}\n".format(node, args.url, starttime, endtime) ) print_history(node.read_raw_history(starttime, endtime)) finally: client.disconnect() sys.exit(0)
def uasubscribe(): parser = argparse.ArgumentParser(description="Subscribe to a node and print results") add_common_args(parser) parser.add_argument("-t", "--eventtype", dest="eventtype", default="datachange", choices=['datachange', 'event'], help="Event type to subscribe to") args = parse_args(parser, requirenodeid=False) if args.eventtype == "datachange": _require_nodeid(parser, args) else: # FIXME: this is broken, someone may have written i=84 on purpose if args.nodeid == "i=84" and args.path == "": args.nodeid = "i=2253" client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) handler = SubHandler() sub = client.create_subscription(500, handler) if args.eventtype == "datachange": sub.subscribe_data_change(node) else: sub.subscribe_events(node) embed() finally: client.disconnect() sys.exit(0) print(args)
def uaread(): parser = argparse.ArgumentParser(description="Read attribute of a node, per default reads value of a node") add_common_args(parser) parser.add_argument("-a", "--attribute", dest="attribute", type=int, default=ua.AttributeIds.Value, help="Set attribute to read") parser.add_argument("-t", "--datatype", dest="datatype", default="python", choices=['python', 'variant', 'datavalue'], help="Data type to return") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) attr = node.get_attribute(args.attribute) if args.datatype == "python": print(attr.Value.Value) elif args.datatype == "variant": print(attr.Value) else: print(attr) finally: client.disconnect() sys.exit(0) print(args)
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 uals(): parser = argparse.ArgumentParser(description="Browse OPC-UA node and print result") add_common_args(parser) parser.add_argument("-l", dest="long_format", const=3, nargs="?", type=int, help="use a long listing format") parser.add_argument("-d", "--depth", default=1, type=int, help="Browse depth") args = parse_args(parser) if args.long_format is None: args.long_format = 1 client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) print("Browsing node {} at {}\n".format(node, args.url)) if args.long_format == 0: _lsprint_0(node, args.depth - 1) elif args.long_format == 1: _lsprint_1(node, args.depth - 1) else: _lsprint_long(node, args.depth - 1) finally: client.disconnect() sys.exit(0) print(args)
def uahistoryread(): parser = argparse.ArgumentParser(description="Read history of a node") add_common_args(parser) parser.add_argument( "--starttime", default="", help= "Start time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time" ) parser.add_argument( "--endtime", default="", help= "End time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time" ) args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) starttime = str_to_datetime(args.starttime) endtime = str_to_datetime(args.endtime) print("Reading raw history of node {} at {}; start at {}, end at {}\n". format(node, args.url, starttime, endtime)) print_history(node.read_raw_history(starttime, endtime)) finally: client.disconnect() sys.exit(0)
def uasubscribe(): parser = argparse.ArgumentParser( description="Subscribe to a node and print results") add_common_args(parser) parser.add_argument("-t", "--eventtype", dest="eventtype", default="datachange", choices=['datachange', 'event'], help="Event type to subscribe to") args = parse_args(parser, requirenodeid=False) if args.eventtype == "datachange": _require_nodeid(parser, args) else: # FIXME: this is broken, someone may have written i=84 on purpose if args.nodeid == "i=84" and args.path == "": args.nodeid = "i=2253" client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) handler = SubHandler() sub = client.create_subscription(500, handler) if args.eventtype == "datachange": sub.subscribe_data_change(node) else: sub.subscribe_events(node) embed() finally: client.disconnect() sys.exit(0) print(args)
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 uageneratestructs(): parser = argparse.ArgumentParser( description= "Generate a Python module from the xml structure definition (.bsd)") add_common_args(parser, require_node=True) parser.add_argument( "-o", "--output", dest="output_path", required=True, type=str, default=None, help="The python file to be generated.", ) args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) generators, _ = client.load_type_definitions([node]) generators[0].save_to_file(args.output_path, True) finally: client.disconnect() sys.exit(0)
def test_basic256(self): client = Client("opc.tcp://" + os.environ['TEST_IP'] + ":" + os.environ['TEST_PORT']) client.application_uri = "urn:127.0.0.1:ASNeG:FTestClient" client.set_security_string( "Basic128Rsa15,SignAndEncrypt,FTestClient.der,FTestClient.pem") client.connect() client.disconnect()
def opcua_init(request): client = Client(request.get('url', 'opc.tcp://localhost:4841'), timeout=request.get('timeout', 10)) client.set_security_string(request.get('security', '')) client.connect() return client
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_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 opcua_init(request): client = Client( request.get("url", "opc.tcp://localhost:4841"), timeout=request.get("timeout", 10), ) client.set_security_string(request.get("security", "")) client.connect() return client
def opcua_init(request): client = Client( request.get('url', 'opc.tcp://localhost:4841'), timeout=request.get('timeout', 10) ) client.set_security_string(request.get('security', '')) client.connect() return client
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 uahistoryread(): parser = argparse.ArgumentParser(description="Read history of a node") add_common_args(parser) parser.add_argument( "--starttime", default=None, help= "Start time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time - one day" ) parser.add_argument( "--endtime", default=None, help= "End time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time" ) parser.add_argument( "-e", "--events", action="store_true", help="Read event history instead of data change history") parser.add_argument("-l", "--limit", type=int, default=10, help="Maximum number of notfication to return") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) starttime = str_to_datetime(args.starttime, datetime.utcnow() - timedelta(days=1)) endtime = str_to_datetime(args.endtime, datetime.utcnow()) print( "Reading raw history of node {0} at {1}; start at {2}, end at {3}\n" .format(node, args.url, starttime, endtime)) if args.events: evs = node.read_event_history(starttime, endtime, numvalues=args.limit) for ev in evs: print(ev) else: print_history( node.read_raw_history(starttime, endtime, numvalues=args.limit)) finally: client.disconnect() sys.exit(0)
def uawrite(): parser = argparse.ArgumentParser( description="Write attribute of a node, per default write value of node" ) add_common_args(parser) parser.add_argument("-a", "--attribute", dest="attribute", type=int, default=ua.AttributeIds.Value, help="Set attribute to read") parser.add_argument("-l", "--list", "--array", dest="array", default="guess", choices=["guess", "true", "false"], help="Value is an array") parser.add_argument("-t", "--datatype", dest="datatype", default="guess", choices=[ "guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext' ], help="Data type to return") parser.add_argument("value", help="Value to be written", metavar="VALUE") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) val = _val_to_variant(args.value, args) node.set_attribute(args.attribute, ua.DataValue(val)) finally: client.disconnect() sys.exit(0) print(args)
def client_connection(index, server, security_policies_uri, printable=False): client = Client(server["address"]) try: best_endpoint = best_endpoint_selection(client, server, security_policies_uri, printable) client = Client(best_endpoint.EndpointUrl) client.application_uri = "urn:freeopcua:client" client.description = "OPCUA-Client-Kafka-Gateway" client = client_auth(client, server) policy = best_endpoint.SecurityPolicyUri.split('#')[1] if policy != "None": security_string = str(policy) + ',' + str(MessageSecurityMode(best_endpoint.SecurityMode).name) + ',client_certificate.pem' + ',client_key.pem' client.set_security_string(security_string) client.connect() return client except Exception as ex: #print(f"\nEXCEPTION in client connection: {ex.__class__, ex.args}") return
def uawrite(): parser = argparse.ArgumentParser(description="Write attribute of a node, per default write value of node") add_common_args(parser) parser.add_argument("-a", "--attribute", dest="attribute", type=int, default=ua.AttributeIds.Value, help="Set attribute to read") parser.add_argument("-l", "--list", "--array", dest="array", default="guess", choices=["guess", "true", "false"], help="Value is an array") parser.add_argument("-t", "--datatype", dest="datatype", default="guess", choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'], help="Data type to return") parser.add_argument("value", help="Value to be written", metavar="VALUE") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) val = _val_to_variant(args.value, args) node.set_attribute(args.attribute, ua.DataValue(val)) finally: client.disconnect() sys.exit(0) print(args)
def uahistoryread(): parser = argparse.ArgumentParser(description="Read history of a node") add_common_args(parser) parser.add_argument("--starttime", default=None, help="Start time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time - one day") parser.add_argument("--endtime", default=None, help="End time, formatted as YYYY-MM-DD [HH:MM[:SS]]. Default: current time") parser.add_argument("-e", "--events", action="store_true", help="Read event history instead of data change history") parser.add_argument("-l", "--limit", type=int, default=10, help="Maximum number of notfication to return") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) starttime = str_to_datetime(args.starttime, datetime.utcnow() - timedelta(days=1)) endtime = str_to_datetime(args.endtime, datetime.utcnow()) print("Reading raw history of node {0} at {1}; start at {2}, end at {3}\n".format(node, args.url, starttime, endtime)) if args.events: evs = node.read_event_history(starttime, endtime, numvalues=args.limit) for ev in evs: print(ev) else: print_history(node.read_raw_history(starttime, endtime, numvalues=args.limit)) finally: client.disconnect() sys.exit(0)
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
from opcua import Client from firebase import Firebase import csv import time # url and security url= 'opc.tcp://127.0.0.1:8080' client = Client(url) client.set_security_string("Basic256Sha256,SignAndEncrypt,certificate-example.der,private-key-example.pem") # add "server" certificate.dem file and private-key.pem file (Beckhoff_OpcUaServer.der,Beckhoff_OpcUaServer.pem) client.application_uri = "urn:example.org:FreeOpcUa:python-opcua" # use the uri of the server. (urn:BeckhoffAutomation:TcOpcUaServer) # client connection client.connect() print('CLIENT CONNECTED SUCCESSFULLY') # firebase initialization firebaseConfig = { # here the firebase configuration should be entered } firebase = Firebase(firebaseConfig) db = firebase.database() with open('historyAccess_client.csv',mode='a', newline='') as historyAccess: fieldnames= ['Temperature','Pressure','Flow','Time'] thewriter = csv.DictWriter(historyAccess,fieldnames=fieldnames) thewriter.writeheader() while True: iTemp = client.get_node('ns=2;i=3') Temperature = iTemp.get_value() print(f'Temperature is: {Temperature}') iprssure = client.get_node('ns=2;i=2') Pressure = iprssure.get_value()
class CustomClient(object): def __init__(self, server_endpoint, namespace, enable_cert, client_cert_path, client_key_path, auth_name=None, auth_password=None, debug_print=False, client_request_timeout=4): self.NAMESPACE = namespace self.DEBUG_MODE_PRINT = debug_print self.client = Client(server_endpoint, client_request_timeout) if auth_name is not None and auth_password is not None: self.client.set_user(auth_name) self.client.set_password(auth_password) if enable_cert: self.client.set_security_string("Basic256Sha256,SignAndEncrypt," + client_cert_path + "," + client_key_path) # TODO experimental( cf. client.py @line 60) # self.client.session_timeout = 10*1000 # 30h = 30*60*60*1000 # self.client.secure_channel_timeout = 10*1000 # 30h self.root = None self.idx = None def start(self): try: self.client.connect() except ConnectionError as er: print(DateHelper.get_local_datetime(), er) raise # sys.exit(1) except Exception as ex: print(DateHelper.get_local_datetime(), ex) raise # sys.exit(1) # Now getting root variable node using its browse path self.root = self.client.get_root_node() uri = self.NAMESPACE self.idx = self.client.get_namespace_index(uri) def stop(self): try: self.client.disconnect() except Exception as ex: print("Couldn't stop OPC Client because of: ", ex) def get_server_vars(self, child): # TODO raise TimeOutError when called after subscription was set up, (cf. ua_client.py: send_request) try: obj = self.root.get_child( ["0:Objects", ("{}:" + child).format(self.idx)]) # print(obj.get_browse_name()) # print(obj.get_variables()) except BadNoMatch: return None return obj.get_variables() def create_dir_on_server(self, child): # get object node objects_node = self.client.get_objects_node() # add new folder "child" first for method in objects_node.get_methods(): # print(method.get_browse_name().Name) if "ADD_NEW_OBJECTS_FOLDER" in method.get_browse_name().Name: objects_node.call_method(method, child) def register_variables_to_server(self, child, file_path): # get object node objects_node = self.client.get_objects_node() # get tags of variables and register them serverside in folder "child" mtagfile = open(file_path, 'r') tags_pf_output = format_textfile(mtagfile.readlines()) mtagfile.close() # VARIANT A for method in objects_node.get_methods(): # print(method.get_browse_name().Name) if "ADD_OPC_TAG" in method.get_browse_name().Name: for i in tags_pf_output: opctag, typ = i.split() opctag, typ = opctag.strip(), int(typ) # call method to register var objects_node.call_method(method, opctag, numbers_to_typestrings(typ), child) # VARIANT B # for i in tags_pf_output: # opctag, typ = i.split() # opctag, typ = opctag.strip(), int(typ) # # # # register vars at server # mvar = folder.add_variable(self.idx, opctag.strip(), ua.Variant(0, numbers_to_vartyps(typ))) # mvar.set_writable() # # # Test # # dv = DataValue() # # dv.Value = ua.Variant(1,numbers_to_vartyps(typ)) # # mvar.set_value(dv) @staticmethod def set_vars(observed_nodes_list, ctrl_list, value_list): """ Set new value for node. :param observed_nodes_list: list of nodes, the client subscribed to :param ctrl_list: list of nodes to update :param value_list: list of values to assign """ i = 0 for ctrl in ctrl_list: for var in observed_nodes_list: if var.nodeid == ctrl.nodeid: try: variant_type = var.get_data_value().Value.VariantType var.set_value(value_list[i], variant_type) break except Exception as ex: if type(ex).__name__ in TimeoutError.__name__: print( DateHelper.get_local_datetime(), 'TimeOutError ignored while set var in OPCClient' ) pass else: print(DateHelper.get_local_datetime(), ex) raise i += 1 # region subscription def _subscribe(self, dir_name, sub_handler, subscription, subscription_handle, list_of_nodes_to_subscribe, already_subscribed_nodes, sub_interval): """ Make a subscription for list of nodes and return handle for subscription :param dir_name: subfolder, which contains the requested nodes :param sub_handler: SubHandler which will call the update_data function :param subscription: subscription object :param subscription_handle: handle can used to unsubscribe :param list_of_nodes_to_subscribe: list of nodes/customVars :param already_subscribed_nodes: list of nodes which already within subscription :param sub_interval: time interval the subscribed node is checked (in ms) :return subscription: :return subscription_handle :return subscribed_nodes """ if subscription is not None: self._unsubscribe(subscription, subscription_handle) already_subscribed_nodes = [] all_server_nodes = self.get_server_vars(dir_name) for node in all_server_nodes: for var in list_of_nodes_to_subscribe: if node.nodeid == var.nodeid: already_subscribed_nodes.append(node) # make subscription subscription = self.client.create_subscription(sub_interval, sub_handler) subscription_handle = subscription.subscribe_data_change( already_subscribed_nodes) return subscription, subscription_handle, already_subscribed_nodes # will raise TimeoutError() - why? --> use self.subscription.delete() instead @staticmethod def _unsubscribe(self, subscription, subscription_handle): if subscription_handle is not None: # self.stop() # self.start() # self.subscription.delete() subscription.unsubscribe(subscription_handle)
def test_nocrypto_feil(self): clt = Client(self.uri_no_crypto) with self.assertRaises(ua.UaError): clt.set_security_string("Basic256,Sign,examples/certificate-example.der,examples/private-key-example.pem")
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
class Client_opc(): def __init__(self, cert_path, server_path, policy, mode, AggrObject, handle_dict): self.cert_path = cert_path #certificates path self.server_path = server_path self.policy = policy self.mode = mode self.AggrObject = AggrObject self.handle_dict = handle_dict #istantiating Client calling "Client" constructor into stack opcua, set name and calling set_security_string method in the stack def client_instantiate(self): self.client = Client(self.server_path) #instaniate client self.client.name = "AggregationClient" if ((self.policy != "None") and (self.mode != "None")): #this method take a string as input that contains policy, mode, client certificate path and private key path. The endpoint that satisfies this characteristichs is choosed self.client.set_security_string(self.policy+","+self.mode+","+self.cert_path+"client_certificate.der,"+self.cert_path+"client_private_key.pem") #SecurityPolicy, Mode, Certificate path, Private key path #This method call the "connect" method in the stack. This method creates secure channel, create the session and activate it def secure_channel_and_session_activation(self): #params requested from the client. Server can set another value based on its requirements self.client.secure_channel_timeout = 10000 self.client.session_timeout = 10000 try: self.client.connect() #create secure channel and session; activate session print("Client instantiated; secure channel and session created; session activated ") except: #if exception occures, disconnect the client self.client.disconnect() #This method disconnect the client from the server, closing session and secure channel def disconnect(self): self.client.disconnect() #This method is called when we want only to read Data def readData(self,node_id, polling_dict): #Get node node = self.client.get_node(node_id) #Client.py stack function #Get values value = node.get_data_value() #Node.py stack function print(f"Polling Service readed value : {value} ") #Set readed values in the local variables AggrVar = self.AggrObject.get_variables() for var in AggrVar: for key in polling_dict: remote_node = self.client.get_node(polling_dict[key]) if(remote_node == node and str(var.nodeid) == key): var.set_value(value) '''This method is our stack method revisitation to set our parameter values''' #This method is used in the "subscribe" method to create the filter for making the monitored item request def set_datachange_filter(self, deadband_val, deadbandtype=1): deadband_filter = ua.DataChangeFilter() deadband_filter.Trigger = ua.DataChangeTrigger(1) # send notification when status or value change deadband_filter.DeadbandType = deadbandtype #type = 0 -> notification every change; type = 1 -> absolute deadband_val is considered; type = 2 -> percent deadband_val is considered deadband_filter.DeadbandValue = deadband_val return deadband_filter '''This method is our stack method revisitation to set our parameter values''' #This method is called in the "subscribe" method for creating the monitored items for the nodes passed. def create_monitored_item(self, subscription, nodes , sampling_interval, client_handle, filter=None, queuesize = 0, discard_oldest = True, attr=ua.AttributeIds.Value): is_list = True if isinstance(nodes, Iterable): nodes = list(nodes) else: nodes = [nodes] is_list = False mirs = [] for node in nodes: mir = self.make_monitored_item_request(subscription, node, attr, sampling_interval, client_handle, filter, queuesize, discard_oldest) #making monitored item request mirs.append(mir) mids = subscription.create_monitored_items(mirs) if is_list: return mids if type(mids[0]) == ua.StatusCode: mids[0].check() return mids[0] #return a list of handles (monitored item ids) '''This method is our stack method revisitation to set our parameter values''' #This method sets our params obtained from the conf file and make the monitored item request def make_monitored_item_request(self, subscription, node, attr, sampling_interval, client_handle, filter, queuesize, discard_oldest): rv = ua.ReadValueId() rv.NodeId = node.nodeid rv.AttributeId = attr mparams = ua.MonitoringParameters() with subscription._lock: subscription._client_handle = client_handle mparams.ClientHandle = subscription._client_handle mparams.SamplingInterval = sampling_interval mparams.QueueSize = queuesize mparams.DiscardOldest = discard_oldest if filter: mparams.Filter = filter mir = ua.MonitoredItemCreateRequest() #stack request mir.ItemToMonitor = rv mir.MonitoringMode = ua.MonitoringMode.Reporting mir.RequestedParameters = mparams return mir #This method is called when we want only to subscribe to Data. It takes as input sub infos and mon_item infos from conf file def subscribe(self, monitored_nodes, sub_infos): #Create the handlers handler = [] for i in range(len(sub_infos)): handler.append(SubHandler(self.AggrObject, self.handle_dict)) #Creating a subscription for ech 'sub_infos' element in the config file (different types of subscriptions -> different parameters) sub = [] sub_index_list = [] for i in range(len(monitored_nodes)): sub_index_list.append(monitored_nodes[i]["subIndex"]) for i in range(len(sub_infos)): if(i in sub_index_list): #Set sub parameters params = ua.CreateSubscriptionParameters() params.RequestedPublishingInterval = sub_infos[i]['requested_publish_interval'] params.RequestedLifetimeCount = sub_infos[i]['requested_lifetime_count'] params.RequestedMaxKeepAliveCount = sub_infos[i]['requested_max_keepalive_timer'] params.MaxNotificationsPerPublish = sub_infos[i]['max_notif_per_publish'] params.PublishingEnabled = sub_infos[i]['publishing_enabled'] params.Priority = sub_infos[i]['priority'] #Create the subscription sub.append(self.client.create_subscription(params, handler[i])) #handle will contains mon_item_ids handle = [] Aggr_key = [] for key in self.handle_dict: Aggr_key.append(key) for i in range(len(monitored_nodes)): filter = self.set_datachange_filter(monitored_nodes[i]['deadbandval'], monitored_nodes[i]['deadbandtype']) #Set filter from config parameters setted in the config file handle.append(self.create_monitored_item(sub[monitored_nodes[i]['subIndex']], self.client.get_node(monitored_nodes[i]['nodeTomonitor']), monitored_nodes[i]['sampling_interval'] ,self.handle_dict[Aggr_key[i]], filter, monitored_nodes[i]['queue_size'], monitored_nodes[i]['discard_oldest'])) #handle = list of monitored items ids #handler.datachange_notification is called when a value of the monitored nodes has changed return sub, handle #This method take as input the subscription list and handle list of monitored items that we want to delete def delete_monit_items(self, sub, handle): for i in range(len(sub)): for mid in handle: try: #stack function called on the subscription sub[i].unsubscribe(mid) #unsubscribe to data_change/events of the selected monitored items (handle -> list of monitored items ids) except ua.uaerrors._auto.BadMonitoredItemIdInvalid: #This except is added because we call the unsubscribe stack method (delete monitored_item) for every subscription, but the monitored item is present only in one of them #So, in the other subscriptions, the BadMonitoredItemInvalid is raised, but we want to ignore this error pass #This method takes as input the subscriptions list and delete them def delete_sub(self, sub): try: for i in range(len(sub)): sub[i].delete() #Stack method call on the subscription(this method delete every monitored item in the subscription and delete the subscription) except Exception: print("An Error was occured in client.delete function!")
def uacall(): parser = argparse.ArgumentParser(description="Call method of a node") add_common_args(parser) parser.add_argument( "-m", "--method", dest="method", type=int, default=None, help= "Set method to call. If not given then (single) method of the selected node is used." ) parser.add_argument("-l", "--list", "--array", dest="array", default="guess", choices=["guess", "true", "false"], help="Value is an array") parser.add_argument("-t", "--datatype", dest="datatype", default="guess", choices=[ "guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext' ], help="Data type to return") parser.add_argument("value", help="Value to use for call to method, if any", nargs="?", metavar="VALUE") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) # val must be a tuple in order to enable method calls without arguments if (args.value is None): val = () #empty tuple else: val = (_val_to_variant(args.value, args), ) # tuple with one element # determine method to call: Either explicitly given or automatically select the method of the selected node. methods = node.get_methods() method_id = None #print( "methods=%s" % (methods) ) if (args.method is None): if (len(methods) == 0): raise ValueError( "No methods in selected node and no method given") elif (len(methods) == 1): method_id = methods[0] else: raise ValueError( "Selected node has {0:d} methods but no method given. Provide one of {1!s}" .format(*(methods))) else: for m in methods: if (m.nodeid.Identifier == args.method): method_id = m.nodeid break if (method_id is None): # last resort: method_id = ua.NodeId( identifier=args.method) #, namespaceidx=? )#, nodeidtype=?): ) #print( "method_id=%s\nval=%s" % (method_id,val) ) result_variants = node.call_method(method_id, *val) print("resulting result_variants={0!s}".format(result_variants)) finally: client.disconnect() sys.exit(0) print(args)
from opcua import Client, ua url = "opc.tcp://127.0.0.1:4840" client = Client(url) client.set_user("user1") client.set_password("pw1") client.set_security_string("Basic256Sha256,SignAndEncrypt,cert.pem,key.pem") client.application_uri = "urn:example.org:FreeOpcUa:python-opcua" client.connect() print(f"Connected to: {url}") servicelevel_node = client.get_node("ns=0;i=2267") servicelevel_node_value = servicelevel_node.get_value() print(servicelevel_node_value) client.disconnect()
def datachange_notification(self, node, val, data): print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": #from IPython import embed logging.basicConfig(level=logging.DEBUG) #client = Client("opc.tcp://*****:*****@localhost:53530/OPCUA/SimulationServer/" ) client.set_security_string( "Basic256,SignAndEncrypt,example-certificate.der,example-private-key.pem" ) 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")
Client to subscription. It will receive events from server """ def datachange_notification(self, node, val, data): print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": #from IPython import embed logging.basicConfig(level=logging.DEBUG) #client = Client("opc.tcp://*****:*****@localhost:53530/OPCUA/SimulationServer/") client.set_security_string("Basic256,SignAndEncrypt,example-certificate.der,example-private-key.pem") 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")
def FINISH_test_connect_basic256(self): c = Client(URL) c.set_security_string("basic256,sign,XXXX") c.connect() c.disconnect()
from opcua import Client import flask import time import flask url = "opc.tcp://192.168.1.33:48010" #server url client = Client(url) #set security mode and certificate. Use server's certificate instead. Certificate path need to be modified client.set_security_string( "Basic256,SignAndEncrypt,C:/Users/RSONG/Documents/InduSoft Web Studio v8.1 Projects/Project/Config/uaserver/own/studio.der,C:/Users/RSONG/Documents/InduSoft Web Studio v8.1 Projects/Project/Config/uaserver/own/studio.pem" ) #Set client uri to match server's certificate client.application_uri = "urn:DESKTOP-U34UL7P:Studio:OpcUaServer" def connect_to_server(client): connection_times = 0 while True: connection_times += 1 try: if connection_times > 100: raise ValueError('Failed to connect to opcua server') client.connect() break except: time.sleep(1) print(f"reconnecting to server, {connection_times} times") continue
def uacall(): parser = argparse.ArgumentParser(description="Call method of a node") add_common_args(parser) parser.add_argument("-m", "--method", dest="method", type=int, default=None, help="Set method to call. If not given then (single) method of the selected node is used.") parser.add_argument("-l", "--list", "--array", dest="array", default="guess", choices=["guess", "true", "false"], help="Value is an array") parser.add_argument("-t", "--datatype", dest="datatype", default="guess", choices=["guess", 'byte', 'sbyte', 'nodeid', 'expandednodeid', 'qualifiedname', 'browsename', 'string', 'float', 'double', 'int16', 'int32', "int64", 'uint16', 'uint32', 'uint64', "bool", "string", 'datetime', 'bytestring', 'xmlelement', 'statuscode', 'localizedtext'], help="Data type to return") parser.add_argument("value", help="Value to use for call to method, if any", nargs="?", metavar="VALUE") args = parse_args(parser, requirenodeid=True) client = Client(args.url, timeout=args.timeout) client.set_security_string(args.security) client.connect() try: node = get_node(client, args) # val must be a tuple in order to enable method calls without arguments if ( args.value is None ): val = () #empty tuple else: val = (_val_to_variant(args.value, args),) # tuple with one element # determine method to call: Either explicitly given or automatically select the method of the selected node. methods = node.get_methods() method_id = None #print( "methods=%s" % (methods) ) if ( args.method is None ): if ( len( methods ) == 0 ): raise ValueError( "No methods in selected node and no method given" ) elif ( len( methods ) == 1 ): method_id = methods[0] else: raise ValueError( "Selected node has {0:d} methods but no method given. Provide one of {1!s}".format(*(methods)) ) else: for m in methods: if ( m.nodeid.Identifier == args.method ): method_id = m.nodeid break if ( method_id is None): # last resort: method_id = ua.NodeId( identifier=args.method )#, namespaceidx=? )#, nodeidtype=?): ) #print( "method_id=%s\nval=%s" % (method_id,val) ) result_variants = node.call_method( method_id, *val ) print( "resulting result_variants={0!s}".format(result_variants) ) finally: client.disconnect() sys.exit(0) print(args)
def datachange_notification(self, node, val, data): print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": #from IPython import embed #logging.basicConfig(level=logging.DEBUG) client = Client("opc.tcp://192.168.10.10:4840/") client.set_user('admin') client.set_password('ca895987') client.set_security_string( "Basic256Sha256,SignAndEncrypt,opcua_client.der,opcua_client.pem") client.application_uri = "urn:example.org:FreeOpcUa:python-opcua" i = 0 try: client.connect() while i < 5: ua_LevelMaximum = client.get_node( "ns=5;s=Arp.Plc.Eclr/LevelMaximum") ua_LevelMinimum = client.get_node( "ns=5;s=Arp.Plc.Eclr/LevelMinimum") ua_robot_test = client.get_node(
import sys sys.path.insert(0, "..") import logging from IPython import embed from opcua import Client if __name__ == "__main__": logging.basicConfig(level=logging.WARNING) client = Client("opc.tcp://localhost:4840/freeopcua/server/") #client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/") client.set_security_string( "Basic256Sha256,SignAndEncrypt,certificate-example.der,private-key-example.pem" ) # client.application_uri = "urn:example.org:FreeOpcUa:python-opcua" # client.secure_channel_timeout = 10000 # client.session_timeout = 10000 try: client.connect() root = client.get_root_node() objects = client.get_objects_node() print("childs og objects are: ", objects.get_children()) embed() 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"): 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
def test_nocrypto_fail(self): clt = Client(self.uri_no_crypto) with self.assertRaises(ua.UaError): clt.set_security_string("Basic256Sha256,Sign,../examples/certificate-example.der,../examples/private-key-example.pem")
import sys sys.path.insert(0, "..") import logging from IPython import embed from opcua import Client if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG) client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/") client.set_security_string("Basic256Sha256,Sign,certificate-example.der,private-key-example.pem") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() print("childs og objects are: ", objects.get_children()) embed() finally: client.disconnect()