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 __init__(self, cacheFile=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(cacheFile) 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 create_custom_data_type(self, idx, name, basetype=ua.ObjectIds.BaseDataType, properties=None, description=None): if properties is None: properties = [] if isinstance(basetype, Node): base_t = basetype elif isinstance(basetype, ua.NodeId): base_t = Node(self.iserver.isession, basetype) else: base_t = Node(self.iserver.isession, ua.NodeId(basetype)) custom_t = base_t.add_data_type(idx, name, description) for prop in properties: datatype = None if len(prop) > 2: datatype = prop[2] custom_t.add_property(idx, prop[0], ua.get_default_value(prop[1]), varianttype=prop[1], datatype=datatype) return custom_t
def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive): addnode = ua.AddNodesItem() addnode.RequestedNewNodeId = nodeid addnode.BrowseName = rdesc.BrowseName addnode.ParentNodeId = parent_nodeid addnode.ReferenceTypeId = rdesc.ReferenceTypeId addnode.TypeDefinition = rdesc.TypeDefinition addnode.NodeClass = rdesc.NodeClass node_to_copy = Node(server, rdesc.NodeId) attrObj = getattr(ua, rdesc.NodeClass.name + "Attributes") _read_and_copy_attrs(node_to_copy, attrObj(), addnode) res = server.add_nodes([addnode])[0] added_nodes = [res.AddedNodeId] if recursive: descs = node_to_copy.get_children_descriptions() for desc in descs: nodes = _copy_node(server, res.AddedNodeId, desc, nodeid=ua.NodeId(namespaceidx=desc.NodeId.NamespaceIndex), recursive=True) added_nodes.extend(nodes) return added_nodes
def _create_custom_type(self, idx, name, basetype, properties, variables, methods): if isinstance(basetype, Node): base_t = basetype elif isinstance(basetype, ua.NodeId): base_t = Node(self.iserver.isession, basetype) else: base_t = Node(self.iserver.isession, ua.NodeId(basetype)) custom_t = base_t.add_object_type(idx, name) for prop in properties: datatype = None if len(prop) > 2: datatype = prop[2] custom_t.add_property(idx, prop[0], ua.get_default_value(prop[1]), varianttype=prop[1], datatype=datatype) for variable in variables: datatype = None if len(variable) > 2: datatype = variable[2] custom_t.add_variable(idx, variable[0], ua.get_default_value(variable[1]), varianttype=variable[1], datatype=datatype) for method in methods: custom_t.add_method(idx, method[0], method[1], method[2], method[3]) return custom_t
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()
def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive): addnode = ua.AddNodesItem() addnode.RequestedNewNodeId = nodeid addnode.BrowseName = rdesc.BrowseName addnode.ParentNodeId = parent_nodeid addnode.ReferenceTypeId = rdesc.ReferenceTypeId addnode.TypeDefinition = rdesc.TypeDefinition addnode.NodeClass = rdesc.NodeClass node_to_copy = Node(server, rdesc.NodeId) attrObj = getattr(ua, rdesc.NodeClass.name + "Attributes") _read_and_copy_attrs(node_to_copy, attrObj(), addnode) res = server.add_nodes([addnode])[0] added_nodes = [res.AddedNodeId] if recursive: descs = node_to_copy.get_children_descriptions() for desc in descs: nodes = _copy_node( server, res.AddedNodeId, desc, nodeid=ua.NodeId(namespaceidx=desc.NodeId.NamespaceIndex), recursive=True) added_nodes.extend(nodes) return added_nodes
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 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 create_custom_event_type(self, idx, name, baseetype=ua.ObjectIds.BaseEventType, properties=[]): if isinstance(baseetype, Node): base_event = baseetype elif isinstance(baseetype, ua.NodeId): base_event = Node(self.iserver.isession, baseetype) else: base_event = Node(self.iserver.isession, ua.NodeId(baseetype)) custom_event = base_event.add_subtype(idx, name) for property in properties: custom_event.add_property(idx, property[0], ua.Variant(None, property[1])) return custom_event
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 sub_monitored_items_creation(sub, monitored_items): params = ua.CreateMonitoredItemsParameters() params.SubscriptionId = sub.subscription_id params.ItemsToCreate = monitored_items params.TimestampsToReturn = ua.TimestampsToReturn.Both # insert monitored item into map to avoid notification arrive before result return # server_handle is left as None in purpose as we don't get it yet. with sub._lock: for mi in monitored_items: data = SubscriptionItemData() data.client_handle = mi.RequestedParameters.ClientHandle data.node = Node(sub.server, mi.ItemToMonitor.NodeId) data.attribute = mi.ItemToMonitor.AttributeId #TODO: Either use the filter from request or from response. Here it uses from request, in modify it uses from response data.mfilter = mi.RequestedParameters.Filter sub._monitoreditems_map[mi.RequestedParameters.ClientHandle] = data results = sub.server.create_monitored_items(params) mids = [] # process result, add server_handle, or remove it if failed with sub._lock: for idx, result in enumerate(results): mi = params.ItemsToCreate[idx] if not result.StatusCode.is_good(): del sub._monitoreditems_map[mi.RequestedParameters.ClientHandle] mids.append(result.StatusCode) continue data = sub._monitoreditems_map[mi.RequestedParameters.ClientHandle] data.server_handle = result.MonitoredItemId mids.append(result.MonitoredItemId) return mids, results
def browse_tests(client, logger): nid, subPostfixNodeIdHierarchical, subNodeIdNonHierarchical, parentNid = browseSubTree # Hierarchical referenced node childrenNids = list() for bn in subPostfixNodeIdHierarchical: childrenNids.append('ns=1;s=' + bn) print("Browsing children of node {} expecting nodes {}".format( nid, childrenNids)) n1 = client.get_node(nid) # Get all children of a node. By default hierarchical references and all node classes are returned. children = n1.get_children( ) # <=> n1.get_children(refs=33) for HierarchicalReferences # Checking number of children and their associated ids logger.add_test( 'Browse Test - number of children for Node {}. Expecting {} == {}'. format(nid, len(childrenNids), len(children)), len(childrenNids) == len(children)) # There shall not be backward references node = Node(sUri, parentNid) logger.add_test( 'Browse Test - parent {} is not in browsed children'.format(parentNid), node not in children) # Checking forward references for childNid in childrenNids: print(childNid) node = Node(sUri, childNid) logger.add_test( 'Browse Test - child {} retrieved in browsed children'.format( childNid), node in children) nonHierarchicalNids = list(subNodeIdNonHierarchical) nonHierChildren = n1.get_children(refs=32) #NonHierarchicalReferences print("Non hier children: " + str(nonHierChildren)) # Checking number of children and their associated ids logger.add_test( 'Browse Test - number of non hierarchical children for Node {}. Expecting {} == {}' .format(nid, len(nonHierarchicalNids), len(nonHierChildren)), len(nonHierarchicalNids) == len(nonHierChildren)) # Checking forward references for childNid in nonHierarchicalNids: print(childNid) node = Node(sUri, childNid) logger.add_test('Browse Test - non hierarchical child ' + childNid, node in nonHierChildren)
def copy_node(parent, node, nodeid=None, recursive=True): """ Copy a node or node tree as child of parent node """ rdesc = _rdesc_from_node(parent, node) if nodeid is None: nodeid = ua.NodeId(namespaceidx=node.nodeid.NamespaceIndex) added_nodeids = _copy_node(parent.server, parent.nodeid, rdesc, nodeid, recursive) return [Node(parent.server, nid) for nid in added_nodeids]
def set_value(node_name: str, node: Node, value: bool) -> None: """ 节点赋值 :param node_name: :param node: :param value: True -> 停机,False -> 复位 :return: None """ try: node.set_attribute(ua.AttributeIds.Value, ua.DataValue(variant=ua.Variant(value))) except BadNodeIdUnknown as b: msg = '写入{}状态信息失败, 错误信息:节点不存在!'.format(node_name) print(msg) logging.warning(msg) raise RuntimeError("写入 {} 状态信息失败: 节点不存在!".format(node_name)) except Exception as e: msg = '写入{}状态信息失败!未知错误:{}'.format(node_name, e) print(msg) logging.warning(msg) raise RuntimeError("写入 {} 状态信息失败!未知错误:{}".format(node_name, e))
def create_monitored_items(sub, dirty_nodes, attr): mirs = [] for dirty_node in dirty_nodes: try: node_var = Node(sub.server, NodeId().from_string(dirty_node["node"])) rv = ua.ReadValueId() rv.NodeId = node_var.nodeid rv.AttributeId = attr # rv.IndexRange //We leave it null, then the entire array is returned mparams = ua.MonitoringParameters() with sub._lock: sub._client_handle += 1 mparams.ClientHandle = sub._client_handle mparams.SamplingInterval = dirty_node["samplingInterval"] mparams.QueueSize = dirty_node["queueSize"] mparams.DiscardOldest = dirty_node["queueDiscardOldest"] == "True" if dirty_node["filter"] != "None": try: mfilter = DataChangeFilter() #DataChangeTrigger(0) = Status, DataChangeTrigger(1) = StatusValue, DataChangeTrigger(2) = StatusValueTimestamp mfilter.Trigger = DataChangeTrigger[dirty_node["filter"]["Trigger"]] # DeadbandType(0) = None, DeadbandType(1) = Absolute, DeadbandType(2) = Percent mfilter.DeadbandType = DeadbandType[dirty_node["filter"]["DeadbandType"]] mfilter.DeadbandValue = dirty_node["filter"]["DeadbandValue"] except: mfilter = None print(f"\n\nWARNING: Can't apply filter on node {dirty_node['node']}") else: mfilter = None if mfilter: mparams.Filter = mfilter mir = ua.MonitoredItemCreateRequest() mir.ItemToMonitor = rv mir.MonitoringMode = ua.MonitoringMode.Disabled if dirty_node["monitoringMode"] == "Disabled" else ua.MonitoringMode.Sampling if dirty_node["monitoringMode"] == "Sampling" else ua.MonitoringMode.Reporting mir.RequestedParameters = mparams mirs.append(mir) except: print("\nUnable to create monitored item for node {dirty_node['node']}") mids, results = sub_monitored_items_creation(sub, mirs) for i, r in enumerate(results): if dirty_node["samplingInterval"] != r.RevisedSamplingInterval or dirty_node["queueSize"] != r.RevisedQueueSize: print(f"\nServer has changed monitored item properties for node {dirty_node['node']}") print(f"Monitored item id: {r.MonitoredItemId}") print(f"Revised SamplingInterval: {r.RevisedSamplingInterval}") print(f"Revised QueueSize: {r.RevisedQueueSize}") print(f"Filter Result: {r.FilterResult}\n") return mids
def __init__(self): self.logger = logging.getLogger(__name__) 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) # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space from save db to disc #standard_address_space.fill_address_space_from_disk(self.aspace) # import address space directly from xml, this has preformance impact so disabled #importer = xmlimporter.XmlImporter(self.node_mgt_service) #importer.import_xml("/home/olivier/python-opcua/schemas/Opc.Ua.NodeSet2.xml") self.loop = utils.ThreadLoop() self.subscription_service = SubscriptionService(self.loop, self.aspace) # 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)) uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries)
def _create_custom_type(self, idx, name, basetype, properties, variables, methods): if isinstance(basetype, Node): base_t = basetype elif isinstance(basetype, ua.NodeId): base_t = Node(self.iserver.isession, basetype) else: base_t = Node(self.iserver.isession, ua.NodeId(basetype)) custom_t = base_t.add_object_type(idx, name) for property in properties: datatype = None if len(property) > 2: datatype = property[2] custom_t.add_property(idx, property[0], None, varianttype=property[1], datatype=datatype) for variable in variables: datatype = None if len(variable) > 2: datatype = variable[2] custom_t.add_variable(idx, variable[0], None, varianttype=variable[1], datatype=datatype) for method in methods: custom_t.add_method(idx, method[0], method[1], method[2], method[3]) return custom_t
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, 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)
class InternalServer(object): def __init__(self): self.logger = logging.getLogger(__name__) 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) # import address space from code generated from xml standard_address_space.fill_address_space(self.node_mgt_service) # import address space from save db to disc #standard_address_space.fill_address_space_from_disk(self.aspace) # import address space directly from xml, this has preformance impact so disabled #importer = xmlimporter.XmlImporter(self.node_mgt_service) #importer.import_xml("/home/olivier/python-opcua/schemas/Opc.Ua.NodeSet2.xml") self.loop = utils.ThreadLoop() self.subscription_service = SubscriptionService(self.loop, self.aspace) # 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)) uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_address_space(self, path): self.aspace.load(path) def dump_address_space(self, 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) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_StartTime)).set_value(datetime.now()) if not self.disabled_clock: self._set_current_time() def stop(self): self.logger.info("stopping internal server") self.loop.stop() def _set_current_time(self): self.current_time_node.set_value(datetime.now()) 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 appdesc.ApplicationName = server.ServerNames[0] # FIXME: select name from client locale appdesc.ApplicationType = server.ServerType appdesc.GatewayServerUri = server.GatewayServerUri appdesc.DiscoveryUrls = server.DiscoveryUrls # FIXME: select discovery uri using reachability from client network 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)
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)
def get_node(self, nodeid): """ Get node using NodeId object or a string representing a NodeId """ return Node(self.bclient, nodeid)
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)
def get_node(self, nodeid): """ Get a specific node using NodeId object or a string representing a NodeId """ return Node(self.iserver.isession, nodeid)
class InternalServer(object): def __init__(self, cacheFile=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) if cacheFile and path.isfile(cacheFile): # import address space from shelve self.aspace.load(cacheFile) 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 preformance impact so disabled # importer = xmlimporter.XmlImporter(self.node_mgt_service) # importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml", self) if cacheFile: self.aspace.dump(cacheFile) 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)) uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_address_space(self, path): self.aspace.load(path) def dump_address_space(self, 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) 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.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, cacheFile = None): self.logger = logging.getLogger(__name__) 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) if cacheFile and path.isfile(cacheFile): # import address space from shelve self.aspace.load(cacheFile) 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 preformance impact so disabled #importer = xmlimporter.XmlImporter(self.node_mgt_service) #importer.import_xml("/path/to/python-opcua/schemas/Opc.Ua.NodeSet2.xml") if cacheFile: self.aspace.dump(cacheFile) 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)) uries = ["http://opcfoundation.org/UA/"] ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray)) ns_node.set_value(uries) def load_address_space(self, path): self.aspace.load(path) def dump_address_space(self, 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) 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.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 appdesc.ApplicationName = server.ServerNames[0] # FIXME: select name from client locale appdesc.ApplicationType = server.ServerType appdesc.GatewayServerUri = server.GatewayServerUri appdesc.DiscoveryUrls = server.DiscoveryUrls # FIXME: select discovery uri using reachability from client network 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)): """ Set attribute History Read of object events to True and start storing data for history """ # to historize events of an object, first check if object supports events source_event_notifier = source.get_attribute(ua.AttributeIds.EventNotifier) if source_event_notifier.Value.Value & 1 == 1: # check bit 0 # if it supports events, turn on bit 2 (enables history read) source.set_attr_bit(ua.AttributeIds.EventNotifier, 2) # send the object to history manager self.history_manager.historize_event(source, period) 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, 2) self.history_manager.dehistorize(source)