class InternalServer(object): def __init__(self, shelffile=None, user_manager=None, session_cls=None): self.logger = logging.getLogger(__name__) self.server_callback_dispatcher = CallbackDispatcher() self.endpoints = [] self._channel_id_counter = 5 self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log self._local_discovery_service = None # lazy-loading self.aspace = AddressSpace() self.attribute_service = AttributeService(self.aspace) self.view_service = ViewService(self.aspace) self.method_service = MethodService(self.aspace) self.node_mgt_service = NodeManagementService(self.aspace) self.load_standard_address_space(shelffile) self.loop = None self.asyncio_transports = [] self.subscription_service = SubscriptionService(self.aspace) self.history_manager = HistoryManager(self) self.user_manager = user_manager # create a session to use on server side self.session_cls = session_cls or InternalSession self.isession = self.session_cls(self, self.aspace, \ self.subscription_service, "Internal", user=UserManager.User.Admin) self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self._address_space_fixes() self.setup_nodes() @property def thread_loop(self): if self.loop is None: raise Exception("InternalServer stopped: async threadloop is not running.") return self.loop @property def local_discovery_service(self): if self._local_discovery_service is None: self._local_discovery_service = LocalDiscoveryService(parent = self) for edp in self.endpoints: srvDesc = LocalDiscoveryService.ServerDescription(edp.Server) self._local_discovery_service.add_server_description(srvDesc) return self._local_discovery_service def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_standard_address_space(self, shelffile=None): if (shelffile is not None) and (os.path.isfile(shelffile) or os.path.isfile(shelffile+".db")): # import address space from shelf self.aspace.load_aspace_shelf(shelffile) else: # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space directly from xml, this has performance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) # if a cache file was supplied a shelve of the standard address space can now be built for next start up if shelffile: self.aspace.make_aspace_shelf(shelffile) def _address_space_fixes(self): """ Looks like the xml definition of address space has some error. This is a good place to fix them """ it = ua.AddReferencesItem() it.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseObjectType) it.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it.IsForward = False it.TargetNodeId = ua.NodeId(ua.ObjectIds.ObjectTypesFolder) it.TargetNodeClass = ua.NodeClass.Object it2 = ua.AddReferencesItem() it2.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseDataType) it2.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it2.IsForward = False it2.TargetNodeId = ua.NodeId(ua.ObjectIds.DataTypesFolder) it2.TargetNodeClass = ua.NodeClass.Object results = self.isession.add_references([it, it2]) params = ua.WriteParameters() for nodeid in (ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerRead, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryReadData, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryReadEvents, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerWrite, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryUpdateData, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerHistoryUpdateEvents, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerMethodCall, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerBrowse, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerRegisterNodes, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerTranslateBrowsePathsToNodeIds, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxNodesPerNodeManagement, ua.ObjectIds.Server_ServerCapabilities_OperationLimits_MaxMonitoredItemsPerCall): attr = ua.WriteValue() attr.NodeId = ua.NodeId(nodeid) attr.AttributeId = ua.AttributeIds.Value attr.Value = ua.DataValue(ua.Variant(10000, ua.VariantType.UInt32), ua.StatusCode(ua.StatusCodes.Good)) attr.Value.ServerTimestamp = datetime.utcnow() params.NodesToWrite.append(attr) result = self.isession.write(params) result[0].check() def load_address_space(self, path): """ Load address space from path """ self.aspace.load(path) def dump_address_space(self, path): """ Dump current address space to path """ self.aspace.dump(path) def start(self): self.logger.info("starting internal server") self.loop = utils.ThreadLoop() self.loop.start() self.subscription_service.set_loop(self.loop) serverState = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)) serverState.set_value(ua.uaprotocol_auto.ServerState.Running, ua.VariantType.Int32) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value(datetime.utcnow()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.isession.close_session() self.subscription_service.set_loop(None) self.history_manager.stop() if self.loop: self.loop.stop() # wait for ThreadLoop to finish before proceeding self.loop.join() self.loop.close() self.loop = None def is_running(self): return self.loop is not None def _set_current_time(self): self.current_time_node.set_value(datetime.utcnow()) self.loop.call_later(1, self._set_current_time) def get_new_channel_id(self): self._channel_id_counter += 1 return self._channel_id_counter def add_endpoint(self, endpoint): self.endpoints.append(endpoint) def get_endpoints(self, params=None, sockname=None): self.logger.info("get endpoint") if sockname: # return to client the ip address it has access to edps = [] for edp in self.endpoints: edp1 = copy(edp) url = urlparse(edp1.EndpointUrl) url = url._replace(netloc=sockname[0] + ":" + str(sockname[1])) edp1.EndpointUrl = url.geturl() edps.append(edp1) return edps return self.endpoints[:] def create_session(self, name, user=UserManager.User.Anonymous): return self.session_cls(self, self.aspace, self.subscription_service, name, user=user) def enable_history_data_change(self, node, period=timedelta(days=7), count=0): """ Set attribute Historizing of node to True and start storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(True)) node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.historize_data_change(node, period, count) def disable_history_data_change(self, node): """ Set attribute Historizing of node to False and stop storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(False)) node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.dehistorize(node) def enable_history_event(self, source, period=timedelta(days=7), count=0): """ Set attribute History Read of object events to True and start storing data for history """ event_notifier = source.get_event_notifier() if ua.EventNotifier.SubscribeToEvents not in event_notifier: raise ua.UaError("Node does not generate events", event_notifier) if ua.EventNotifier.HistoryRead not in event_notifier: event_notifier.add(ua.EventNotifier.HistoryRead) source.set_event_notifier(event_notifier) self.history_manager.historize_event(source, period, count) def disable_history_event(self, source): """ Set attribute History Read of node to False and stop storing data for history """ source.unset_attr_bit(ua.AttributeIds.EventNotifier, ua.EventNotifier.HistoryRead) self.history_manager.dehistorize(source) def subscribe_server_callback(self, event, handle): """ Create a subscription from event to handle """ self.server_callback_dispatcher.addListener(event, handle) def unsubscribe_server_callback(self, event, handle): """ Remove a subscription from event to handle """ self.server_callback_dispatcher.removeListener(event, handle) def set_attribute_value(self, nodeid, datavalue, attr=ua.AttributeIds.Value): """ directly write datavalue to the Attribute, bypasing some checks and structure creation so it is a little faster """ self.aspace.set_attribute_value(nodeid, ua.AttributeIds.Value, datavalue)
class InternalServer(object): def __init__(self, shelffile=None): self.logger = logging.getLogger(__name__) self.server_callback_dispatcher = CallbackDispatcher() self.endpoints = [] self._channel_id_counter = 5 self.allow_remote_admin = True self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log self._known_servers = {} # used if we are a discovery server self.aspace = AddressSpace() self.attribute_service = AttributeService(self.aspace) self.view_service = ViewService(self.aspace) self.method_service = MethodService(self.aspace) self.node_mgt_service = NodeManagementService(self.aspace) self.load_standard_address_space(shelffile) self.loop = utils.ThreadLoop() self.asyncio_transports = [] self.subscription_service = SubscriptionService(self.loop, self.aspace) self.history_manager = HistoryManager(self) # create a session to use on server side self.isession = InternalSession(self, self.aspace, self.subscription_service, "Internal", user=User.Admin) self.current_time_node = Node( self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self._address_space_fixes() self.setup_nodes() def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_standard_address_space(self, shelffile=None): if shelffile is not None and os.path.isfile(shelffile): # import address space from shelf self.aspace.load_aspace_shelf(shelffile) else: # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space directly from xml, this has performance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) # if a cache file was supplied a shelve of the standard address space can now be built for next start up if shelffile: self.aspace.make_aspace_shelf(shelffile) def _address_space_fixes(self): """ Looks like the xml definition of address space has some error. This is a good place to fix them """ it = ua.AddReferencesItem() it.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseObjectType) it.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it.IsForward = False it.TargetNodeId = ua.NodeId(ua.ObjectIds.ObjectTypesFolder) it.TargetNodeClass = ua.NodeClass.Object results = self.isession.add_references([it]) def load_address_space(self, path): """ Load address space from path """ self.aspace.load(path) def dump_address_space(self, path): """ Dump current address space to path """ self.aspace.dump(path) def start(self): self.logger.info("starting internal server") for edp in self.endpoints: self._known_servers[edp.Server.ApplicationUri] = ServerDesc( edp.Server) self.loop.start() Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value( 0, ua.VariantType.Int32) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value( datetime.utcnow()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.isession.close_session() self.loop.stop() self.history_manager.stop() def _set_current_time(self): self.current_time_node.set_value(datetime.utcnow()) self.loop.call_later(1, self._set_current_time) def get_new_channel_id(self): self._channel_id_counter += 1 return self._channel_id_counter def add_endpoint(self, endpoint): self.endpoints.append(endpoint) def get_endpoints(self, params=None, sockname=None): self.logger.info("get endpoint") if sockname: # return to client the ip address it has access to edps = [] for edp in self.endpoints: edp1 = copy(edp) url = urlparse(edp1.EndpointUrl) url = url._replace(netloc=sockname[0] + ":" + str(sockname[1])) edp1.EndpointUrl = url.geturl() edps.append(edp1) return edps return self.endpoints[:] def find_servers(self, params): if not params.ServerUris: return [desc.Server for desc in self._known_servers.values()] servers = [] for serv in self._known_servers.values(): serv_uri = serv.Server.ApplicationUri.split(":") for uri in params.ServerUris: uri = uri.split(":") if serv_uri[:len(uri)] == uri: servers.append(serv.Server) break return servers def register_server(self, server, conf=None): appdesc = ua.ApplicationDescription() appdesc.ApplicationUri = server.ServerUri appdesc.ProductUri = server.ProductUri # FIXME: select name from client locale appdesc.ApplicationName = server.ServerNames[0] appdesc.ApplicationType = server.ServerType appdesc.DiscoveryUrls = server.DiscoveryUrls # FIXME: select discovery uri using reachability from client network appdesc.GatewayServerUri = server.GatewayServerUri self._known_servers[server.ServerUri] = ServerDesc(appdesc, conf) def register_server2(self, params): return self.register_server(params.Server, params.DiscoveryConfiguration) def create_session(self, name, user=User.Anonymous, external=False): return InternalSession(self, self.aspace, self.subscription_service, name, user=user, external=external) def enable_history_data_change(self, node, period=timedelta(days=7), count=0): """ Set attribute Historizing of node to True and start storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(True)) node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.historize_data_change(node, period, count) def disable_history_data_change(self, node): """ Set attribute Historizing of node to False and stop storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(False)) node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.dehistorize(node) def enable_history_event(self, source, period=timedelta(days=7), count=0): """ Set attribute History Read of object events to True and start storing data for history """ event_notifier = source.get_event_notifier() if ua.EventNotifier.SubscribeToEvents not in event_notifier: raise ua.UaError("Node does not generate events", event_notifier) if ua.EventNotifier.HistoryRead not in event_notifier: event_notifier.add(ua.EventNotifier.HistoryRead) source.set_event_notifier(event_notifier) self.history_manager.historize_event(source, period, count) def disable_history_event(self, source): """ Set attribute History Read of node to False and stop storing data for history """ source.unset_attr_bit(ua.AttributeIds.EventNotifier, ua.EventNotifier.HistoryRead) self.history_manager.dehistorize(source) def subscribe_server_callback(self, event, handle): """ Create a subscription from event to handle """ self.server_callback_dispatcher.addListener(event, handle) def unsubscribe_server_callback(self, event, handle): """ Remove a subscription from event to handle """ self.server_callback_dispatcher.removeListener(event, handle)
class InternalServer(object): def __init__(self, shelffile=None): self.logger = logging.getLogger(__name__) self.server_callback_dispatcher = CallbackDispatcher() self.endpoints = [] self._channel_id_counter = 5 self.allow_remote_admin = True self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log self._known_servers = {} # used if we are a discovery server self.certificate = None self.private_key = None self.aspace = AddressSpace() self.attribute_service = AttributeService(self.aspace) self.view_service = ViewService(self.aspace) self.method_service = MethodService(self.aspace) self.node_mgt_service = NodeManagementService(self.aspace) self.load_standard_address_space(shelffile) self.loop = None self.asyncio_transports = [] self.subscription_service = SubscriptionService(self.aspace) self.history_manager = HistoryManager(self) self.user_manager = default_user_manager # defined at the end of this file # create a session to use on server side self.isession = InternalSession(self, self.aspace, self.subscription_service, "Internal", user=User.Admin) self.current_time_node = Node( self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self._address_space_fixes() self.setup_nodes() def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_standard_address_space(self, shelffile=None): if (shelffile is not None) and (os.path.isfile(shelffile) or os.path.isfile(shelffile + ".db")): # import address space from shelf self.aspace.load_aspace_shelf(shelffile) else: # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space directly from xml, this has performance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) # if a cache file was supplied a shelve of the standard address space can now be built for next start up if shelffile: self.aspace.make_aspace_shelf(shelffile) def _address_space_fixes(self): """ Looks like the xml definition of address space has some error. This is a good place to fix them """ it = ua.AddReferencesItem() it.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseObjectType) it.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it.IsForward = False it.TargetNodeId = ua.NodeId(ua.ObjectIds.ObjectTypesFolder) it.TargetNodeClass = ua.NodeClass.Object it2 = ua.AddReferencesItem() it2.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseDataType) it2.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it2.IsForward = False it2.TargetNodeId = ua.NodeId(ua.ObjectIds.DataTypesFolder) it2.TargetNodeClass = ua.NodeClass.Object results = self.isession.add_references([it, it2]) def load_address_space(self, path): """ Load address space from path """ self.aspace.load(path) def dump_address_space(self, path): """ Dump current address space to path """ self.aspace.dump(path) def start(self): self.logger.info("starting internal server") for edp in self.endpoints: self._known_servers[edp.Server.ApplicationUri] = ServerDesc( edp.Server) self.loop = utils.ThreadLoop() self.loop.start() self.subscription_service.set_loop(self.loop) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value( 0, ua.VariantType.Int32) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value( datetime.utcnow()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.isession.close_session() if self.loop: self.loop.stop() self.loop = None self.subscription_service.set_loop(None) self.history_manager.stop() def _set_current_time(self): self.current_time_node.set_value(datetime.utcnow()) self.loop.call_later(1, self._set_current_time) def get_new_channel_id(self): self._channel_id_counter += 1 return self._channel_id_counter def add_endpoint(self, endpoint): self.endpoints.append(endpoint) def get_endpoints(self, params=None, sockname=None): self.logger.info("get endpoint") if sockname: # return to client the ip address it has access to edps = [] for edp in self.endpoints: edp1 = copy(edp) url = urlparse(edp1.EndpointUrl) url = url._replace(netloc=sockname[0] + ":" + str(sockname[1])) edp1.EndpointUrl = url.geturl() edps.append(edp1) return edps return self.endpoints[:] def find_servers(self, params): if not params.ServerUris: return [desc.Server for desc in self._known_servers.values()] servers = [] for serv in self._known_servers.values(): serv_uri = serv.Server.ApplicationUri.split(":") for uri in params.ServerUris: uri = uri.split(":") if serv_uri[:len(uri)] == uri: servers.append(serv.Server) break return servers def register_server(self, server, conf=None): appdesc = ua.ApplicationDescription() appdesc.ApplicationUri = server.ServerUri appdesc.ProductUri = server.ProductUri # FIXME: select name from client locale appdesc.ApplicationName = server.ServerNames[0] appdesc.ApplicationType = server.ServerType appdesc.DiscoveryUrls = server.DiscoveryUrls # FIXME: select discovery uri using reachability from client network appdesc.GatewayServerUri = server.GatewayServerUri self._known_servers[server.ServerUri] = ServerDesc(appdesc, conf) def register_server2(self, params): return self.register_server(params.Server, params.DiscoveryConfiguration) def create_session(self, name, user=User.Anonymous, external=False): return InternalSession(self, self.aspace, self.subscription_service, name, user=user, external=external) def enable_history_data_change(self, node, period=timedelta(days=7), count=0): """ Set attribute Historizing of node to True and start storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(True)) node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.historize_data_change(node, period, count) def disable_history_data_change(self, node): """ Set attribute Historizing of node to False and stop storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(False)) node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.dehistorize(node) def enable_history_event(self, source, period=timedelta(days=7), count=0): """ Set attribute History Read of object events to True and start storing data for history """ event_notifier = source.get_event_notifier() if ua.EventNotifier.SubscribeToEvents not in event_notifier: raise ua.UaError("Node does not generate events", event_notifier) if ua.EventNotifier.HistoryRead not in event_notifier: event_notifier.add(ua.EventNotifier.HistoryRead) source.set_event_notifier(event_notifier) self.history_manager.historize_event(source, period, count) def disable_history_event(self, source): """ Set attribute History Read of node to False and stop storing data for history """ source.unset_attr_bit(ua.AttributeIds.EventNotifier, ua.EventNotifier.HistoryRead) self.history_manager.dehistorize(source) def subscribe_server_callback(self, event, handle): """ Create a subscription from event to handle """ self.server_callback_dispatcher.addListener(event, handle) def unsubscribe_server_callback(self, event, handle): """ Remove a subscription from event to handle """ self.server_callback_dispatcher.removeListener(event, handle) def set_attribute_value(self, nodeid, datavalue, attr=ua.AttributeIds.Value): """ directly write datavalue to the Attribute, bypasing some checks and structure creation so it is a little faster """ self.aspace.set_attribute_value(nodeid, ua.AttributeIds.Value, datavalue) def set_user_manager(self, user_manager): """ set up a function which that will check for authorize users. Input function takes username and password as paramters and returns True of user is allowed access, False otherwise. """ self.user_manager = user_manager def check_user_token(self, isession, token): """ unpack the username and password for the benefit of the user defined user manager """ userName = token.UserName passwd = token.Password # decrypt password is we can if str(token.EncryptionAlgorithm) != "None": if use_crypto == False: return False try: if token.EncryptionAlgorithm == "http://www.w3.org/2001/04/xmlenc#rsa-1_5": raw_pw = uacrypto.decrypt_rsa15(self.private_key, passwd) elif token.EncryptionAlgorithm == "http://www.w3.org/2001/04/xmlenc#rsa-oaep": raw_pw = uacrypto.decrypt_rsa_oaep(self.private_key, passwd) else: self.logger.warning( "Unknown password encoding '{0}'".format( token.EncryptionAlgorithm)) return False length = unpack_from('<I', raw_pw)[0] - len(isession.nonce) passwd = raw_pw[4:4 + length] passwd = passwd.decode('utf-8') except Exception as exp: self.logger.warning("Unable to decrypt password") return False # call user_manager return self.user_manager(self, isession, userName, passwd)
class InternalServer(object): def __init__(self, shelffile=None): self.logger = logging.getLogger(__name__) self.server_callback_dispatcher = CallbackDispatcher() self.endpoints = [] self._channel_id_counter = 5 self.allow_remote_admin = True self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log self._known_servers = {} # used if we are a discovery server self.aspace = AddressSpace() self.attribute_service = AttributeService(self.aspace) self.view_service = ViewService(self.aspace) self.method_service = MethodService(self.aspace) self.node_mgt_service = NodeManagementService(self.aspace) self.load_standard_address_space(shelffile) self.loop = utils.ThreadLoop() self.asyncio_transports = [] self.subscription_service = SubscriptionService(self.loop, self.aspace) self.history_manager = HistoryManager(self) # create a session to use on server side self.isession = InternalSession(self, self.aspace, self.subscription_service, "Internal", user=User.Admin) self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self.setup_nodes() def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_standard_address_space(self, shelffile=None): # check for a python shelf file, in windows the file extension is also needed for the check shelffile_win = shelffile if shelffile_win: shelffile_win += ".dat" if shelffile and (path.isfile(shelffile) or path.isfile(shelffile_win)): # import address space from shelf self.aspace.load_aspace_shelf(shelffile) else: # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space directly from xml, this has performance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) # if a cache file was supplied a shelve of the standard address space can now be built for next start up if shelffile: self.aspace.make_aspace_shelf(shelffile) def load_address_space(self, path): """ Load address space from path """ self.aspace.load(path) def dump_address_space(self, path): """ Dump current address space to path """ self.aspace.dump(path) def start(self): self.logger.info("starting internal server") for edp in self.endpoints: self._known_servers[edp.Server.ApplicationUri] = ServerDesc(edp.Server) self.loop.start() Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value(0, ua.VariantType.Int32) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value(datetime.utcnow()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.isession.close_session() self.loop.stop() self.history_manager.stop() def _set_current_time(self): self.current_time_node.set_value(datetime.utcnow()) self.loop.call_later(1, self._set_current_time) def get_new_channel_id(self): self._channel_id_counter += 1 return self._channel_id_counter def add_endpoint(self, endpoint): self.endpoints.append(endpoint) def get_endpoints(self, params=None, sockname=None): self.logger.info("get endpoint") if sockname: # return to client the ip address it has access to edps = [] for edp in self.endpoints: edp1 = copy(edp) url = urlparse(edp1.EndpointUrl) url = url._replace(netloc=sockname[0] + ":" + str(sockname[1])) edp1.EndpointUrl = url.geturl() edps.append(edp1) return edps return self.endpoints[:] def find_servers(self, params): if not params.ServerUris: return [desc.Server for desc in self._known_servers.values()] servers = [] for serv in self._known_servers.values(): serv_uri = serv.Server.ApplicationUri.split(":") for uri in params.ServerUris: uri = uri.split(":") if serv_uri[:len(uri)] == uri: servers.append(serv.Server) break return servers def register_server(self, server, conf=None): appdesc = ua.ApplicationDescription() appdesc.ApplicationUri = server.ServerUri appdesc.ProductUri = server.ProductUri # FIXME: select name from client locale appdesc.ApplicationName = server.ServerNames[0] appdesc.ApplicationType = server.ServerType appdesc.DiscoveryUrls = server.DiscoveryUrls # FIXME: select discovery uri using reachability from client network appdesc.GatewayServerUri = server.GatewayServerUri self._known_servers[server.ServerUri] = ServerDesc(appdesc, conf) def register_server2(self, params): return self.register_server(params.Server, params.DiscoveryConfiguration) def create_session(self, name, user=User.Anonymous, external=False): return InternalSession(self, self.aspace, self.subscription_service, name, user=user, external=external) def enable_history_data_change(self, node, period=timedelta(days=7), count=0): """ Set attribute Historizing of node to True and start storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(True)) node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.historize_data_change(node, period, count) def disable_history_data_change(self, node): """ Set attribute Historizing of node to False and stop storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(False)) node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.dehistorize(node) def enable_history_event(self, source, period=timedelta(days=7), count=0): """ Set attribute History Read of object events to True and start storing data for history """ event_notifier = source.get_event_notifier() if ua.EventNotifier.SubscribeToEvents not in event_notifier: raise ua.UaError("Node does not generate events", event_notifier) if ua.EventNotifier.HistoryRead not in event_notifier: event_notifier.append(ua.EventNotifier.HistoryRead) source.set_event_notifier(event_notifier) self.history_manager.historize_event(source, period, count) def disable_history_event(self, source): """ Set attribute History Read of node to False and stop storing data for history """ source.unset_attr_bit(ua.AttributeIds.EventNotifier, ua.EventNotifier.HistoryRead) self.history_manager.dehistorize(source) def subscribe_server_callback(self, event, handle): """ Create a subscription from event to handle """ self.server_callback_dispatcher.addListener(event, handle) def unsubscribe_server_callback(self, event, handle): """ Remove a subscription from event to handle """ self.server_callback_dispatcher.removeListener(event, handle)
class InternalServer(object): def __init__(self, shelffile=None, parent=None): self.logger = logging.getLogger(__name__) self._parent = parent self.server_callback_dispatcher = CallbackDispatcher() self.endpoints = [] self._channel_id_counter = 5 self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log self._local_discovery_service = None # lazy-loading self.aspace = AddressSpace() self.attribute_service = AttributeService(self.aspace) self.view_service = ViewService(self.aspace) self.method_service = MethodService(self.aspace) self.node_mgt_service = NodeManagementService(self.aspace) self.load_standard_address_space(shelffile) self.loop = None self.asyncio_transports = [] self.subscription_service = SubscriptionService(self.aspace) self.history_manager = HistoryManager(self) # create a session to use on server side self.isession = InternalSession(self, self.aspace, \ self.subscription_service, "Internal", user=UserManager.User.Admin) self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self._address_space_fixes() self.setup_nodes() @property def user_manager(self): return self._parent.user_manager @property def thread_loop(self): if self.loop is None: raise Exception("InternalServer stopped: async threadloop is not running.") return self.loop @property def local_discovery_service(self): if self._local_discovery_service is None: self._local_discovery_service = LocalDiscoveryService(parent = self) for edp in self.endpoints: srvDesc = LocalDiscoveryService.ServerDescription(edp.Server) self._local_discovery_service.add_server_description(srvDesc) return self._local_discovery_service def setup_nodes(self): """ Set up some nodes as defined by spec """ uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_standard_address_space(self, shelffile=None): if (shelffile is not None) and (os.path.isfile(shelffile) or os.path.isfile(shelffile+".db")): # import address space from shelf self.aspace.load_aspace_shelf(shelffile) else: # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space directly from xml, this has performance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) # if a cache file was supplied a shelve of the standard address space can now be built for next start up if shelffile: self.aspace.make_aspace_shelf(shelffile) def _address_space_fixes(self): """ Looks like the xml definition of address space has some error. This is a good place to fix them """ it = ua.AddReferencesItem() it.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseObjectType) it.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it.IsForward = False it.TargetNodeId = ua.NodeId(ua.ObjectIds.ObjectTypesFolder) it.TargetNodeClass = ua.NodeClass.Object it2 = ua.AddReferencesItem() it2.SourceNodeId = ua.NodeId(ua.ObjectIds.BaseDataType) it2.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes) it2.IsForward = False it2.TargetNodeId = ua.NodeId(ua.ObjectIds.DataTypesFolder) it2.TargetNodeClass = ua.NodeClass.Object results = self.isession.add_references([it, it2]) def load_address_space(self, path): """ Load address space from path """ self.aspace.load(path) def dump_address_space(self, path): """ Dump current address space to path """ self.aspace.dump(path) def start(self): self.logger.info("starting internal server") self.loop = utils.ThreadLoop() self.loop.start() self.subscription_service.set_loop(self.loop) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value(0, ua.VariantType.Int32) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value(datetime.utcnow()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.isession.close_session() if self.loop: self.loop.stop() self.loop = None self.subscription_service.set_loop(None) self.history_manager.stop() def is_running(self): return self.loop is not None def _set_current_time(self): self.current_time_node.set_value(datetime.utcnow()) self.loop.call_later(1, self._set_current_time) def get_new_channel_id(self): self._channel_id_counter += 1 return self._channel_id_counter def add_endpoint(self, endpoint): self.endpoints.append(endpoint) def get_endpoints(self, params=None, sockname=None): self.logger.info("get endpoint") if sockname: # return to client the ip address it has access to edps = [] for edp in self.endpoints: edp1 = copy(edp) url = urlparse(edp1.EndpointUrl) url = url._replace(netloc=sockname[0] + ":" + str(sockname[1])) edp1.EndpointUrl = url.geturl() edps.append(edp1) return edps return self.endpoints[:] def create_session(self, name, user=UserManager.User.Anonymous, external=False): return InternalSession(self, self.aspace, self.subscription_service, name, user=user, external=external) def enable_history_data_change(self, node, period=timedelta(days=7), count=0): """ Set attribute Historizing of node to True and start storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(True)) node.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.historize_data_change(node, period, count) def disable_history_data_change(self, node): """ Set attribute Historizing of node to False and stop storing data for history """ node.set_attribute(ua.AttributeIds.Historizing, ua.DataValue(False)) node.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.HistoryRead) node.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.HistoryRead) self.history_manager.dehistorize(node) def enable_history_event(self, source, period=timedelta(days=7), count=0): """ Set attribute History Read of object events to True and start storing data for history """ event_notifier = source.get_event_notifier() if ua.EventNotifier.SubscribeToEvents not in event_notifier: raise ua.UaError("Node does not generate events", event_notifier) if ua.EventNotifier.HistoryRead not in event_notifier: event_notifier.add(ua.EventNotifier.HistoryRead) source.set_event_notifier(event_notifier) self.history_manager.historize_event(source, period, count) def disable_history_event(self, source): """ Set attribute History Read of node to False and stop storing data for history """ source.unset_attr_bit(ua.AttributeIds.EventNotifier, ua.EventNotifier.HistoryRead) self.history_manager.dehistorize(source) def subscribe_server_callback(self, event, handle): """ Create a subscription from event to handle """ self.server_callback_dispatcher.addListener(event, handle) def unsubscribe_server_callback(self, event, handle): """ Remove a subscription from event to handle """ self.server_callback_dispatcher.removeListener(event, handle) def set_attribute_value(self, nodeid, datavalue, attr=ua.AttributeIds.Value): """ directly write datavalue to the Attribute, bypasing some checks and structure creation so it is a little faster """ self.aspace.set_attribute_value(nodeid, ua.AttributeIds.Value, datavalue)