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 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 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 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 conexion_OPC(): # Conexion con el servidor del controlador MPC = Client("opc.tcp://Labo6:16700/") # Creacion objeto OPC-UA MPC.connect() # Conexion Controlador MPC.load_type_definitions() # Conexión con el servidor RTO RTO = Client("opc.tcp://Labo6:16701/") # Creacion objeto OPC-UA RTO.connect() # Conexion Controlador RTO.load_type_definitions() # Conexion con los demas servidores SCADA = OpenOPC.client() # Creacion objeto OPC-DA PID1 = OpenOPC.client() # Creacion objeto OPC-DA PID2 = OpenOPC.client() # Creacion objeto OPC-DA SCADA.connect("LecturaOPC.1.0") PID1.connect("OPC.PID ISA (CreaOPC).1") PID2.connect("OPC.PID ISA 2 (CreaOPC).1") return [MPC, SCADA, PID1, PID2, RTO]
class opcUaClient(): def __init__(self, url): self.session = Client(url) self.session.connect() self.session.load_type_definitions( ) # load definition of server specific structures/extension objects def getArrayNodesFromOpcServer(self, tagList, resultFile): localArrayNodes = [] for x in tagList: tag = x.strip() try: var = self.session.get_node(tag) localArrayNodes.append(var) except Exception as e: errorMessage = tag + " ,\t Cannot read tag from source. Error message: " + str( e) writeMessageInFile(resultFile, errorMessage) return localArrayNodes
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) _configure_client_with_args(client, args) 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)
import sys sys.path.insert(0, "..") import time import logging from IPython import embed from opcua import Client from opcua import ua if __name__ == "__main__": logging.basicConfig(level=logging.WARN) client = Client("opc.tcp://opcua.demo-this.com:51210/UA/SampleServer") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() struct = client.get_node("ns=2;i=10239") before = struct.get_value() client.load_type_definitions( ) # scan server for custom structures and import them after = struct.get_value() embed() finally: client.disconnect()
from opcua import Client import time import opcua url = "opc.tcp://192.168.56.1:4840" client = Client(url) client.connect() print("Client connected") client.load_type_definitions() root = client.get_root_node() print("Root node is: ", root) objects = client.get_objects_node() print("Objects node is: ", objects) print("Children of root are: ", root.get_children()) uri = "OPCUA_SERVER" idx = client.get_namespace_index(uri) while True: try: Time = root.get_child( ["0:Objects", "{}:Parameters".format(idx), "{}:Time".format(idx)]) Pressure = root.get_child([ "0:Objects", "{}:Parameters".format(idx), "{}:Pressure".format(idx) ]) Temp = root.get_child([ "0:Objects", "{}:Parameters".format(idx), "{}:Temperature".format(idx)
class OpcUaClient(object): CONNECT_TIMEOUT = 15 # [sec] RETRY_DELAY = 10 # [sec] MAX_RETRIES = 3 # [-] class Decorators(object): @staticmethod def autoConnectingClient(wrappedMethod): def wrapper(obj, *args, **kwargs): for retry in range(OpcUaClient.MAX_RETRIES): try: return wrappedMethod(obj, *args, **kwargs) except ua.uaerrors.BadNoMatch: raise except Exception: pass try: obj._logger.warning( '(Re)connecting to OPC-UA service.') obj.reconnect() except ConnectionRefusedError: obj._logger.warning( 'Connection refused. Retry in 10s.'.format( OpcUaClient.RETRY_DELAY)) time.sleep(OpcUaClient.RETRY_DELAY) else: # So the exception is exposed. obj.reconnect() return wrappedMethod(obj, *args, **kwargs) return wrapper def __init__(self, serverUrl): self._logger = logging.getLogger(self.__class__.__name__) self._client = Client(serverUrl.geturl(), timeout=self.CONNECT_TIMEOUT) def __enter__(self): self.connect() return self def __exit__(self, exc_type, exc_value, traceback): self.disconnect() self._client = None @property @Decorators.autoConnectingClient def sensorList(self): return self.objectsNode.get_children() @property @Decorators.autoConnectingClient def objectsNode(self): path = [ua.QualifiedName(name='Objects', namespaceidx=0)] return self._client.get_root_node().get_child(path) def connect(self): self._client.connect() self._client.load_type_definitions() def disconnect(self): try: self._client.disconnect() except Exception: pass def reconnect(self): self.disconnect() self.connect() @Decorators.autoConnectingClient def get_browse_name(self, uaNode): return uaNode.get_browse_name() @Decorators.autoConnectingClient def get_node_class(self, uaNode): return uaNode.get_node_class() @Decorators.autoConnectingClient def get_namespace_index(self, uri): return self._client.get_namespace_index(uri) @Decorators.autoConnectingClient def get_child(self, uaNode, path): return uaNode.get_child(path) @Decorators.autoConnectingClient def read_raw_history(self, uaNode, starttime=None, endtime=None, numvalues=0, cont=None): details = ua.ReadRawModifiedDetails() details.IsReadModified = False details.StartTime = starttime or ua.get_win_epoch() details.EndTime = endtime or ua.get_win_epoch() details.NumValuesPerNode = numvalues details.ReturnBounds = True result = OpcUaClient._history_read(uaNode, details, cont) assert (result.StatusCode.is_good()) return result.HistoryData.DataValues, result.ContinuationPoint @staticmethod def _history_read(uaNode, details, cont): valueid = ua.HistoryReadValueId() valueid.NodeId = uaNode.nodeid valueid.IndexRange = '' valueid.ContinuationPoint = cont params = ua.HistoryReadParameters() params.HistoryReadDetails = details params.TimestampsToReturn = ua.TimestampsToReturn.Both params.ReleaseContinuationPoints = False params.NodesToRead.append(valueid) result = uaNode.server.history_read(params)[0] return result
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)
print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://*****:*****@localhost:4840/freeopcua/server/") #connect using a user try: client.connect() client.load_type_definitions() # load definition of server specific structures/extension objects # Client has a few methods to get proxy to UA nodes that should always be in address space such as Root or Objects root = client.get_root_node() print("Root node is: ", root) objects = client.get_objects_node() print("Objects node is: ", objects) # Node objects have methods to read and write node attributes as well as browse or populate address space print("Children of root are: ", root.get_children()) # get a specific node knowing its node id #var = client.get_node(ua.NodeId(1002, 2)) #var = client.get_node("ns=3;i=2002") #print(var) #var.get_data_value() # get value of node as a DataValue object
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 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
print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://192.168.1.112:50840/freeopcua/server/") # client = Client("opc.tcp://admin@localhost:4840/freeopcua/server/") #connect using a user try: client.connect() client.load_type_definitions( ) # Ladet Definition von serverspezifischen Strukturen / Erweiterungsobjekten # 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 Durchsuchen oder Auffüllen des Adressraums print("Children of root are: ", root.get_children()) # Holen Sie sich Informationen eines Knoten mit Kenntnis seiner Knoten-ID
set_bool(read_b12,0,1,s_floor.get_value()) plc.write_area( areas['MK'], 0,12,read_b12 ) 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")
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
import sys sys.path.insert(0, "..") import time import logging from IPython import embed from opcua import Client from opcua import ua if __name__ == "__main__": logging.basicConfig(level=logging.WARN) client = Client("opc.tcp://opcua.demo-this.com:51210/UA/SampleServer") try: client.connect() root = client.get_root_node() objects = client.get_objects_node() struct = client.get_node("ns=2;i=10239") struct_array = client.get_node("ns=2;i=10323") before = struct.get_value() before_array = struct_array.get_value() client.load_type_definitions() # scan server for custom structures and import them after = struct.get_value() after_array = struct_array.get_value() embed() finally: client.disconnect()
print("Python: New data change event", node, val) def event_notification(self, event): print("Python: New event", event) if __name__ == "__main__": logging.basicConfig(level=logging.WARN) #logger = logging.getLogger("KeepAlive") #logger.setLevel(logging.DEBUG) client = Client("opc.tcp://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)
class UaClient(object): """ OPC-Ua client specialized for the need of GUI client return exactly what GUI needs, no customization possible """ def __init__(self): self.settings = QSettings() self.client = None self._connected = False self._datachange_sub = None self._event_sub = None self._subs_dc = {} self._subs_ev = {} self.security_mode = None self.security_policy = None self.certificate_path = None self.private_key_path = None def _reset(self): self.client = None self._connected = False self._datachange_sub = None self._event_sub = None self._subs_dc = {} self._subs_ev = {} @staticmethod def get_endpoints(uri): client = Client(uri, timeout=2) edps = client.connect_and_get_server_endpoints() for i, ep in enumerate(edps, start=1): logger.info('Endpoint %s:', i) for (n, v) in endpoint_to_strings(ep): logger.info(' %s: %s', n, v) logger.info('') return edps def load_security_settings(self, uri): self.security_mode = None self.security_policy = None self.certificate_path = None self.private_key_path = None mysettings = self.settings.value("security_settings", None) if mysettings is None: return if uri in mysettings: mode, policy, cert, key = mysettings[uri] self.security_mode = mode self.security_policy = policy self.certificate_path = cert self.private_key_path = key def save_security_settings(self, uri): mysettings = self.settings.value("security_settings", None) if mysettings is None: mysettings = {} mysettings[uri] = [ self.security_mode, self.security_policy, self.certificate_path, self.private_key_path ] self.settings.setValue("security_settings", mysettings) def get_node(self, nodeid): return self.client.get_node(nodeid) def connect(self, uri): self.disconnect() logger.info("Connecting to %s with parameters %s, %s, %s, %s", uri, self.security_mode, self.security_policy, self.certificate_path, self.private_key_path) self.client = Client(uri) if self.security_mode is not None and self.security_policy is not None: self.client.set_security(getattr( crypto.security_policies, 'SecurityPolicy' + self.security_policy), self.certificate_path, self.private_key_path, mode=getattr(ua.MessageSecurityMode, self.security_mode)) self.client.connect() self._connected = True self.client.load_enums() self.client.load_type_definitions() self.save_security_settings(uri) def disconnect(self): if self._connected: print("Disconnecting from server") self._connected = False try: self.client.disconnect() finally: self._reset() def subscribe_datachange(self, node, handler): if not self._datachange_sub: self._datachange_sub = self.client.create_subscription( 500, handler) handle = self._datachange_sub.subscribe_data_change(node) self._subs_dc[node.nodeid] = handle return handle def unsubscribe_datachange(self, node): self._datachange_sub.unsubscribe(self._subs_dc[node.nodeid]) def subscribe_events(self, node, handler): if not self._event_sub: print("subscirbing with handler: ", handler, dir(handler)) self._event_sub = self.client.create_subscription(500, handler) handle = self._event_sub.subscribe_events(node) self._subs_ev[node.nodeid] = handle return handle def unsubscribe_events(self, node): self._event_sub.unsubscribe(self._subs_ev[node.nodeid]) def get_node_attrs(self, node): if not isinstance(node, Node): node = self.client.get_node(node) attrs = node.get_attributes([ ua.AttributeIds.DisplayName, ua.AttributeIds.BrowseName, ua.AttributeIds.NodeId ]) return node, [attr.Value.Value.to_string() for attr in attrs] @staticmethod def get_children(node): descs = node.get_children_descriptions() descs.sort(key=lambda x: x.BrowseName) return descs