async def _read_history(self, details, rv): """ determine if the history read is for a data changes or events; then read the history for that node """ result = ua.HistoryReadResult() if isinstance(details, ua.ReadRawModifiedDetails): if details.IsReadModified: result.HistoryData = ua.HistoryModifiedData() # we do not support modified history by design so we return what we have else: result.HistoryData = ua.HistoryData() dv, cont = await self._read_datavalue_history(rv, details) result.HistoryData.DataValues = dv result.ContinuationPoint = cont elif isinstance(details, ua.ReadEventDetails): result.HistoryData = ua.HistoryEvent() # FIXME: filter is a cumbersome type, maybe transform it something easier # to handle for storage ev, cont = await self._read_event_history(rv, details) result.HistoryData.Events = ev result.ContinuationPoint = cont else: # we do not currently support the other types, clients can process data themselves result.StatusCode = ua.StatusCode(ua.StatusCodes.BadNotImplemented) return result
async def read_node_history(self, node_id, start, end, nb_values): table = self._get_table_name(node_id) start_time, end_time, order, limit = self._get_bounds( start, end, nb_values) cont = None results = [] # select values from the database; recreate UA Variant from binary try: async with self._db.execute( f'SELECT * FROM "{table}" WHERE "SourceTimestamp" BETWEEN ? AND ? ' f'ORDER BY "_Id" {order} LIMIT ?', ( start_time, end_time, limit, ), ) as cursor: async for row in cursor: # rebuild the data value object dv = ua.DataValue( variant_from_binary(Buffer(row[6])), ServerTimestamp=row[1], SourceTimestamp=row[2], StatusCode_=ua.StatusCode(row[3]), ) results.append(dv) except aiosqlite.Error as e: self.logger.error("Historizing SQL Read Error for %s: %s", node_id, e) if len(results) > self.max_history_data_response_size: cont = results[self.max_history_data_response_size].SourceTimestamp results = results[:self.max_history_data_response_size] return results, cont
def activate_session(self, params, peer_certificate): self.logger.info('activate session') result = ua.ActivateSessionResult() if self.state != SessionState.Created: raise ServiceError(ua.StatusCodes.BadSessionIdInvalid) if InternalSession._current_connections >= InternalSession.max_connections: raise ServiceError(ua.StatusCodes.BadMaxConnectionsReached) self.nonce = create_nonce(32) result.ServerNonce = self.nonce for _ in params.ClientSoftwareCertificates: result.Results.append(ua.StatusCode()) self.state = SessionState.Activated InternalSession._current_connections += 1 id_token = params.UserIdentityToken if self.iserver.user_manager is not None: if isinstance(id_token, ua.UserNameIdentityToken): username = id_token.UserName password = id_token.Password else: username, password = None, None user = self.iserver.user_manager.get_user( self.iserver, username=username, password=password, certificate=peer_certificate) if user is None: raise ServiceError(ua.StatusCodes.BadUserAccessDenied) else: self.user = user self.logger.info("Activated internal session %s for user %s", self.name, self.user) return result
def string_to_val(string, vtype): """ Convert back a string to a python or python-asyncua object Note: no error checking is done here, supplying null strings could raise exceptions (datetime and guid) """ string = string.strip() if string.startswith("["): string = string[1:-1] var = [] for s in string.split(","): s = s.strip() val = string_to_val(s, vtype) var.append(val) return var if vtype == ua.VariantType.Null: val = None elif vtype == ua.VariantType.Boolean: if string in ("True", "true", "on", "On", "1"): val = True else: val = False elif vtype in (ua.VariantType.SByte, ua.VariantType.Int16, ua.VariantType.Int32, ua.VariantType.Int64): if not string: val = 0 else: val = int(string) elif vtype in (ua.VariantType.Byte, ua.VariantType.UInt16, ua.VariantType.UInt32, ua.VariantType.UInt64): if not string: val = 0 else: val = int(string) elif vtype in (ua.VariantType.Float, ua.VariantType.Double): if not string: val = 0.0 else: val = float(string) elif vtype == ua.VariantType.XmlElement: val = ua.XmlElement(string) elif vtype == ua.VariantType.String: val = string elif vtype == ua.VariantType.ByteString: val = string.encode() elif vtype in (ua.VariantType.NodeId, ua.VariantType.ExpandedNodeId): val = ua.NodeId.from_string(string) elif vtype == ua.VariantType.QualifiedName: val = ua.QualifiedName.from_string(string) elif vtype == ua.VariantType.DateTime: val = parser.parse(string) elif vtype == ua.VariantType.LocalizedText: val = ua.LocalizedText.from_string(string) elif vtype == ua.VariantType.StatusCode: val = ua.StatusCode(string) elif vtype == ua.VariantType.Guid: val = uuid.UUID(string) else: # FIXME: Some types are probably missing! raise NotImplementedError return val
async def write(self, params, user=User.Admin): #self.logger.debug("write %s as user %s", params, user) res = [] for writevalue in params.NodesToWrite: if user != User.Admin: if writevalue.AttributeId != ua.AttributeIds.Value: res.append(ua.StatusCode(ua.StatusCodes.BadUserAccessDenied)) continue al = self._aspace.read_attribute_value(writevalue.NodeId, ua.AttributeIds.AccessLevel) ual = self._aspace.read_attribute_value(writevalue.NodeId, ua.AttributeIds.UserAccessLevel) if not al.StatusCode.is_good() or not ua.ua_binary.test_bit( al.Value.Value, ua.AccessLevel.CurrentWrite) or not ua.ua_binary.test_bit( ual.Value.Value, ua.AccessLevel.CurrentWrite): res.append(ua.StatusCode(ua.StatusCodes.BadUserAccessDenied)) continue res.append(await self._aspace.write_attribute_value(writevalue.NodeId, writevalue.AttributeId, writevalue.Value)) return res
def func(parent, variant: Variant): print("func method call with parameters: ", variant.Value) ret = False if not isinstance(variant.Value, int): return ua.StatusCode(StatusCodes.BadInvalidArgument) if variant.Value % 2 == 0: ret = True return [ua.Variant(ret, ua.VariantType.Boolean)]
def _delete_node_callbacks(self, nodedata): if ua.AttributeIds.Value in nodedata.attributes: for handle, callback in list(nodedata.attributes[ua.AttributeIds.Value].datachange_callbacks.items()): try: callback(handle, None, ua.StatusCode(ua.StatusCodes.BadNodeIdUnknown)) self._aspace.delete_datachange_callback(handle) except Exception as ex: self.logger.exception("Error calling delete node callback callback %s, %s, %s", nodedata, ua.AttributeIds.Value, ex)
async def delete_subscriptions(self, ids): self.logger.info("delete subscriptions: %s", ids) res = [] existing_subs = [] for i in ids: sub = self.subscriptions.pop(i, None) if sub is None: res.append(ua.StatusCode(ua.StatusCodes.BadSubscriptionIdInvalid)) else: existing_subs.append(sub) res.append(ua.StatusCode()) stop_results = await asyncio.gather( *[sub.stop() for sub in existing_subs], return_exceptions=True ) for stop_result in stop_results: if isinstance(res, Exception): self.logger.warning("Exception while stopping subscription", exc_info=stop_result) return res
def delete_monitored_items(self, params): self.logger.info("delete monitored items") if params.SubscriptionId not in self.subscriptions: res = [] for _ in params.MonitoredItemIds: res.append(ua.StatusCode(ua.StatusCodes.BadSubscriptionIdInvalid)) return res return self.subscriptions[params.SubscriptionId].monitored_item_srv.delete_monitored_items( params.MonitoredItemIds)
def _translate_browsepath_to_nodeid(self, path): #self.logger.debug("looking at path: %s", path) res = ua.BrowsePathResult() if path.StartingNode not in self._aspace: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid) return res current = path.StartingNode for el in path.RelativePath.Elements: nodeid = self._find_element_in_node(el, current) if not nodeid: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNoMatch) return res current = nodeid target = ua.BrowsePathTarget() target.TargetId = current target.RemainingPathIndex = 4294967295 res.Targets = [target] return res
def _delete_node(self, item, user): if user != User.Admin: return ua.StatusCode(ua.StatusCodes.BadUserAccessDenied) if item.NodeId not in self._aspace: self.logger.warning("DeleteNodesItem: NodeId %s does not exists", item.NodeId) return ua.StatusCode(ua.StatusCodes.BadNodeIdUnknown) if item.DeleteTargetReferences: for elem in self._aspace.keys(): for rdesc in self._aspace[elem].references: if rdesc.NodeId == item.NodeId: self._aspace[elem].references.remove(rdesc) self._delete_node_callbacks(self._aspace[item.NodeId]) del (self._aspace[item.NodeId]) return ua.StatusCode()
def modify_monitored_items(self, params): self.logger.info("modify monitored items") if params.SubscriptionId not in self.subscriptions: res = [] for _ in params.ItemsToModify: result = ua.MonitoredItemModifyResult() result.StatusCode = ua.StatusCode(ua.StatusCodes.BadSubscriptionIdInvalid) res.append(result) return res return self.subscriptions[params.SubscriptionId].monitored_item_srv.modify_monitored_items(params)
async def create_monitored_items(self, params: ua.CreateMonitoredItemsParameters): self.logger.info("create monitored items") if params.SubscriptionId not in self.subscriptions: res = [] for _ in params.ItemsToCreate: response = ua.MonitoredItemCreateResult() response.StatusCode = ua.StatusCode(ua.StatusCodes.BadSubscriptionIdInvalid) res.append(response) return res return await self.subscriptions[params.SubscriptionId].monitored_item_srv.create_monitored_items(params)
def _browse(self, desc): res = ua.BrowseResult() if desc.NodeId not in self._aspace: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid) return res node = self._aspace[desc.NodeId] for ref in node.references: if not self._is_suitable_ref(desc, ref): continue res.References.append(ref) return res
async def write_attribute_value(self, nodeid, attr, value): # self.logger.debug("set attr val: %s %s %s", nodeid, attr, value) node = self._nodes.get(nodeid, None) if node is None: return ua.StatusCode(ua.StatusCodes.BadNodeIdUnknown) attval = node.attributes.get(attr, None) if attval is None: return ua.StatusCode(ua.StatusCodes.BadAttributeIdInvalid) old = attval.value attval.value = value cbs = [] if old.Value != value.Value: # only send call callback when a value change has happend cbs = list(attval.datachange_callbacks.items()) for k, v in cbs: try: await v(k, value) except Exception as ex: self.logger.exception("Error calling datachange callback %s, %s, %s", k, v, ex) return ua.StatusCode()
def update_history(self, params): """ Update history for a node This is the part AttributeService, but implemented as its own service since it requires more logic than other attribute service methods """ results = [] for _ in params.HistoryUpdateDetails: result = ua.HistoryUpdateResult() # we do not accept to rewrite history result.StatusCode = ua.StatusCode(ua.StatusCodes.BadNotWritable) results.append(results) return results
async def process_message(self, seqhdr, body): """ Process incoming messages. """ typeid = nodeid_from_binary(body) requesthdr = struct_from_binary(ua.RequestHeader, body) _logger.debug('process_message %r %r', typeid, requesthdr) try: return await self._process_message(typeid, requesthdr, seqhdr, body) except ServiceError as e: status = ua.StatusCode(e.code) response = ua.ServiceFault() response.ResponseHeader.ServiceResult = status _logger.error("sending service fault response: %s (%s)", status.doc, status.name) self.send_response(requesthdr.RequestHandle, seqhdr, response) return True except Exception: _logger.exception('Error while processing message') response = ua.ServiceFault() response.ResponseHeader.ServiceResult = ua.StatusCode(ua.StatusCodes.BadInternalError) self.send_response(requesthdr.RequestHandle, seqhdr, response) return True
def _translate_browsepath_to_nodeid(self, path): # self.logger.debug("looking at path: %s", path) res = ua.BrowsePathResult() if not path.RelativePath.Elements[-1].TargetName: # OPC UA Part 4: Services, 5.8.4 TranslateBrowsePathsToNodeIds # it's unclear if this the check should also handle empty strings res.StatusCode = ua.StatusCode(ua.StatusCodes.BadBrowseNameInvalid) return res if path.StartingNode not in self._aspace: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid) return res target_nodeids = self._navigate(path.StartingNode, path.RelativePath.Elements) if not target_nodeids: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNoMatch) return res for nodeid in target_nodeids: target = ua.BrowsePathTarget() target.TargetId = nodeid target.RemainingPathIndex = 4294967295 res.Targets.append(target) # FIXME: might need to order these one way or another return res
def _translate_browsepath_to_nodeid(self, path): #self.logger.debug("looking at path: %s", path) res = ua.BrowsePathResult() if not path.RelativePath.Elements[-1].TargetName: # OPC UA Part 4: Services, 5.8.4 TranslateBrowsePathsToNodeIds # it's unclear if this the check should also handle empty strings res.StatusCode = ua.StatusCode(ua.StatusCodes.BadBrowseNameInvalid) return res if path.StartingNode not in self._aspace: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid) return res current = path.StartingNode for el in path.RelativePath.Elements: nodeid = self._find_element_in_node(el, current) if not nodeid: res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNoMatch) return res current = nodeid target = ua.BrowsePathTarget() target.TargetId = current target.RemainingPathIndex = 4294967295 res.Targets = [target] return res
async def process_message(self, seqhdr, body): """ Process incoming messages. """ typeid = nodeid_from_binary(body) requesthdr = struct_from_binary(ua.RequestHeader, body) _logger.debug('process_message %r %r', typeid, requesthdr) try: return await self._process_message(typeid, requesthdr, seqhdr, body) except ServiceError as e: status = ua.StatusCode(e.code) response = ua.ServiceFault() response.ResponseHeader.ServiceResult = status _logger.error("sending service fault response: %s (%s)", status.doc, status.name) self.send_response(requesthdr.RequestHandle, seqhdr, response) return True except ua.uaerrors.BadUserAccessDenied as e: if self.session: user = self.session.user else: user = '******' _logger.warning( "%s attempted to do something they are not permitted to do", user) response = ua.ServiceFault() response.ResponseHeader.ServiceResult = ua.StatusCode( ua.StatusCodes.BadUserAccessDenied) self.send_response(requesthdr.RequestHandle, seqhdr, response) except Exception: _logger.exception('Error while processing message') response = ua.ServiceFault() response.ResponseHeader.ServiceResult = ua.StatusCode( ua.StatusCodes.BadInternalError) self.send_response(requesthdr.RequestHandle, seqhdr, response) return True
def activate_session(self, params): self.logger.info('activate session') result = ua.ActivateSessionResult() if self.state != SessionState.Created: raise ServiceError(ua.StatusCodes.BadSessionIdInvalid) self.nonce = create_nonce(32) result.ServerNonce = self.nonce for _ in params.ClientSoftwareCertificates: result.Results.append(ua.StatusCode()) self.state = SessionState.Activated id_token = params.UserIdentityToken if isinstance(id_token, ua.UserNameIdentityToken): if self.iserver.check_user_token(self, id_token) is False: raise ServiceError(ua.StatusCodes.BadUserAccessDenied) self.logger.info("Activated internal session %s for user %s", self.name, self.user) return result
def publish_results(self): """ Publish all enqueued data changes, events and status changes though the callback. """ if self._publish_cycles_count > self.data.RevisedLifetimeCount: self.logger.warning("Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)", self, self._publish_cycles_count, self.data.RevisedLifetimeCount) # FIXME this will never be send since we do not have publish request anyway self.monitored_item_srv.trigger_statuschange(ua.StatusCode(ua.StatusCodes.BadTimeout)) result = None if self.has_published_results(): self._publish_cycles_count += 1 result = self._pop_publish_result() if result is not None: self.logger.info('publish_results for %s', self.data.SubscriptionId) self.pub_result_callback(result)
async 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)) await ns_node.write_value(uries) 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), StatusCode_=ua.StatusCode(ua.StatusCodes.Good), ServerTimestamp=datetime.utcnow(), ) params.NodesToWrite.append(attr) result = await self.isession.write(params) result[0].check()
def _create_events_monitored_item(self, params: ua.MonitoredItemCreateRequest): self.logger.info("request to subscribe to events for node %s and attribute %s", params.ItemToMonitor.NodeId, params.ItemToMonitor.AttributeId) result, mdata = self._make_monitored_item_common(params) ev_notify_byte = self.aspace.read_attribute_value(params.ItemToMonitor.NodeId, ua.AttributeIds.EventNotifier).Value.Value if ev_notify_byte is None or not ua.ua_binary.test_bit(ev_notify_byte, ua.EventNotifier.SubscribeToEvents): result.StatusCode = ua.StatusCode(ua.StatusCodes.BadServiceUnsupported) return result # result.FilterResult = ua.EventFilterResult() # spec says we can ignore if not error mdata.where_clause_evaluator = WhereClauseEvaluator(self.logger, self.aspace, mdata.filter.WhereClause) self._commit_monitored_item(result, mdata) if params.ItemToMonitor.NodeId not in self._monitored_events: self._monitored_events[params.ItemToMonitor.NodeId] = [] self._monitored_events[params.ItemToMonitor.NodeId].append(result.MonitoredItemId) return result
def publish_results(self): if self._publish_cycles_count > self.data.RevisedLifetimeCount: self.logger.warning( "Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)", self, self._publish_cycles_count, self.data.RevisedLifetimeCount) # FIXME this will never be send since we do not have publish request anyway self.monitored_item_srv.trigger_statuschange( ua.StatusCode(ua.StatusCodes.BadTimeout)) result = None if self.has_published_results(): # FIXME: should we pop a publish request here? or we do not care? self._publish_cycles_count += 1 result = self._pop_publish_result() if result is not None: if inspect.iscoroutinefunction(self.callback): self.subservice.loop.create_task(self.callback(result)) else: self.callback(result)
async def publish_results(self): """ Publish all enqueued data changes, events and status changes though the callback. """ if self._publish_cycles_count > self.data.RevisedLifetimeCount: self.logger.warning( "Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)", self, self._publish_cycles_count, self.data.RevisedLifetimeCount) # FIXME this will never be send since we do not have publish request anyway await self.monitored_item_srv.trigger_statuschange( ua.StatusCode(ua.StatusCodes.BadTimeout)) result = None if self.has_published_results(): if not self.no_acks: self._publish_cycles_count += 1 result = self._pop_publish_result() if result is not None: # self.logger.info('publish_results for %s', self.data.SubscriptionId) # The callback can be: # Subscription.publish_callback -> server internal subscription # UaProcessor.forward_publish_response -> client subscription await self.pub_result_callback(result)
async def _process_message(self, typeid, requesthdr, seqhdr, body): if typeid in [ ua.NodeId( ua.ObjectIds.CreateSessionRequest_Encoding_DefaultBinary), ua.NodeId( ua.ObjectIds.ActivateSessionRequest_Encoding_DefaultBinary) ]: # The connection is first created without a user being attached, and then during activation the user = None elif self.session is None: raise ua.uaerrors.BadUserAccessDenied else: user = self.session.user if self._connection.security_policy.permissions is not None: if self._connection.security_policy.permissions.check_validity( user, typeid, body) is False: raise ua.uaerrors.BadUserAccessDenied if typeid == ua.NodeId( ua.ObjectIds.CreateSessionRequest_Encoding_DefaultBinary): _logger.info("Create session request (%s)", user) params = struct_from_binary(ua.CreateSessionParameters, body) # create the session on server self.session = self.iserver.create_session(self.name, external=True) # get a session creation result to send back sessiondata = await self.session.create_session( params, sockname=self.sockname) response = ua.CreateSessionResponse() response.Parameters = sessiondata response.Parameters.ServerCertificate = self._connection.security_policy.host_certificate if self._connection.security_policy.peer_certificate is None: data = params.ClientNonce else: data = self._connection.security_policy.peer_certificate + params.ClientNonce response.Parameters.ServerSignature.Signature = \ self._connection.security_policy.asymmetric_cryptography.signature(data) response.Parameters.ServerSignature.Algorithm = self._connection.security_policy.AsymmetricSignatureURI #_logger.info("sending create session response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.CloseSessionRequest_Encoding_DefaultBinary): _logger.info("Close session request (%s)", user) if self.session: deletesubs = ua.ua_binary.Primitives.Boolean.unpack(body) await self.session.close_session(deletesubs) else: _logger.info("Request to close non-existing session (%s)", user) response = ua.CloseSessionResponse() _logger.info("sending close session response (%s)", user) self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.ActivateSessionRequest_Encoding_DefaultBinary): _logger.info("Activate session request (%s)", user) params = struct_from_binary(ua.ActivateSessionParameters, body) if not self.session: _logger.info("request to activate non-existing session (%s)", user) raise ServiceError(ua.StatusCodes.BadSessionIdInvalid) if self._connection.security_policy.host_certificate is None: data = self.session.nonce else: data = self._connection.security_policy.host_certificate + self.session.nonce self._connection.security_policy.asymmetric_cryptography.verify( data, params.ClientSignature.Signature) result = self.session.activate_session( params, self._connection.security_policy.peer_certificate) response = ua.ActivateSessionResponse() response.Parameters = result #_logger.info("sending read response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.ReadRequest_Encoding_DefaultBinary): _logger.info("Read request (%s)", user) params = struct_from_binary(ua.ReadParameters, body) results = await self.session.read(params) response = ua.ReadResponse() response.Results = results #_logger.info("sending read response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.WriteRequest_Encoding_DefaultBinary): _logger.info("Write request (%s)", user) params = struct_from_binary(ua.WriteParameters, body) results = await self.session.write(params) response = ua.WriteResponse() response.Results = results #_logger.info("sending write response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.BrowseRequest_Encoding_DefaultBinary): _logger.info("Browse request (%s)", user) params = struct_from_binary(ua.BrowseParameters, body) results = await self.session.browse(params) response = ua.BrowseResponse() response.Results = results #_logger.info("sending browse response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.GetEndpointsRequest_Encoding_DefaultBinary): _logger.info("get endpoints request (%s)", user) params = struct_from_binary(ua.GetEndpointsParameters, body) endpoints = await self.iserver.get_endpoints( params, sockname=self.sockname) response = ua.GetEndpointsResponse() response.Endpoints = endpoints #_logger.info("sending get endpoints response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.FindServersRequest_Encoding_DefaultBinary): _logger.info("find servers request (%s)", user) params = struct_from_binary(ua.FindServersParameters, body) servers = self.iserver.find_servers(params) response = ua.FindServersResponse() response.Servers = servers #_logger.info("sending find servers response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.RegisterServerRequest_Encoding_DefaultBinary): _logger.info("register server request %s", user) serv = struct_from_binary(ua.RegisteredServer, body) self.iserver.register_server(serv) response = ua.RegisterServerResponse() #_logger.info("sending register server response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.RegisterServer2Request_Encoding_DefaultBinary): _logger.info("register server 2 request %s", user) params = struct_from_binary(ua.RegisterServer2Parameters, body) results = self.iserver.register_server2(params) response = ua.RegisterServer2Response() response.ConfigurationResults = results #_logger.info("sending register server 2 response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds. TranslateBrowsePathsToNodeIdsRequest_Encoding_DefaultBinary): _logger.info("translate browsepaths to nodeids request (%s)", user) params = struct_from_binary( ua.TranslateBrowsePathsToNodeIdsParameters, body) paths = await self.session.translate_browsepaths_to_nodeids( params.BrowsePaths) response = ua.TranslateBrowsePathsToNodeIdsResponse() response.Results = paths #_logger.info("sending translate browsepaths to nodeids response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.AddNodesRequest_Encoding_DefaultBinary): _logger.info("add nodes request (%s)", user) params = struct_from_binary(ua.AddNodesParameters, body) results = await self.session.add_nodes(params.NodesToAdd) response = ua.AddNodesResponse() response.Results = results #_logger.info("sending add node response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.DeleteNodesRequest_Encoding_DefaultBinary): _logger.info("delete nodes request (%s)", user) params = struct_from_binary(ua.DeleteNodesParameters, body) results = await self.session.delete_nodes(params) response = ua.DeleteNodesResponse() response.Results = results #_logger.info("sending delete node response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.AddReferencesRequest_Encoding_DefaultBinary): _logger.info("add references request (%s)", user) params = struct_from_binary(ua.AddReferencesParameters, body) results = await self.session.add_references(params.ReferencesToAdd) response = ua.AddReferencesResponse() response.Results = results #_logger.info("sending add references response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.DeleteReferencesRequest_Encoding_DefaultBinary): _logger.info("delete references request (%s)", user) params = struct_from_binary(ua.DeleteReferencesParameters, body) results = await self.session.delete_references( params.ReferencesToDelete) response = ua.DeleteReferencesResponse() response.Parameters.Results = results #_logger.info("sending delete references response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.CreateSubscriptionRequest_Encoding_DefaultBinary): _logger.info("create subscription request (%s)", user) params = struct_from_binary(ua.CreateSubscriptionParameters, body) result = await self.session.create_subscription( params, callback=self.forward_publish_response) response = ua.CreateSubscriptionResponse() response.Parameters = result #_logger.info("sending create subscription response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.DeleteSubscriptionsRequest_Encoding_DefaultBinary ): _logger.info("delete subscriptions request (%s)", user) params = struct_from_binary(ua.DeleteSubscriptionsParameters, body) results = await self.session.delete_subscriptions( params.SubscriptionIds) response = ua.DeleteSubscriptionsResponse() response.Results = results #_logger.info("sending delete subscription response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.CreateMonitoredItemsRequest_Encoding_DefaultBinary ): _logger.info("create monitored items request (%s)", user) params = struct_from_binary(ua.CreateMonitoredItemsParameters, body) results = await self.session.create_monitored_items(params) response = ua.CreateMonitoredItemsResponse() response.Results = results #_logger.info("sending create monitored items response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.ModifyMonitoredItemsRequest_Encoding_DefaultBinary ): _logger.info("modify monitored items request (%s)", user) params = struct_from_binary(ua.ModifyMonitoredItemsParameters, body) results = await self.session.modify_monitored_items(params) response = ua.ModifyMonitoredItemsResponse() response.Results = results #_logger.info("sending modify monitored items response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.DeleteMonitoredItemsRequest_Encoding_DefaultBinary ): _logger.info("delete monitored items request (%s)", user) params = struct_from_binary(ua.DeleteMonitoredItemsParameters, body) results = await self.session.delete_monitored_items(params) response = ua.DeleteMonitoredItemsResponse() response.Results = results #_logger.info("sending delete monitored items response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.HistoryReadRequest_Encoding_DefaultBinary): _logger.info("history read request (%s)", user) params = struct_from_binary(ua.HistoryReadParameters, body) results = await self.session.history_read(params) response = ua.HistoryReadResponse() response.Results = results #_logger.info("sending history read response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.RegisterNodesRequest_Encoding_DefaultBinary): _logger.info("register nodes request (%s)", user) params = struct_from_binary(ua.RegisterNodesParameters, body) _logger.info("Node registration not implemented") response = ua.RegisterNodesResponse() response.Parameters.RegisteredNodeIds = params.NodesToRegister #_logger.info("sending register nodes response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.UnregisterNodesRequest_Encoding_DefaultBinary): _logger.info("unregister nodes request (%s)", user) params = struct_from_binary(ua.UnregisterNodesParameters, body) response = ua.UnregisterNodesResponse() #_logger.info("sending unregister nodes response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.PublishRequest_Encoding_DefaultBinary): _logger.debug("publish request (%s)", user) if not self.session: return False params = struct_from_binary(ua.PublishParameters, body) data = PublishRequestData(requesthdr=requesthdr, seqhdr=seqhdr) # Store the Publish Request (will be used to send publish answers from server) self._publish_requests.append(data) # If there is an enqueued result forward it immediately while self._publish_results: result = self._publish_results.popleft() if result.SubscriptionId not in self.session.subscription_service.active_subscription_ids: # Discard the result if the subscription is no longer active continue await self.forward_publish_response(result) break self.session.publish(params.SubscriptionAcknowledgements) #_logger.debug("publish forward to server") elif typeid == ua.NodeId( ua.ObjectIds.RepublishRequest_Encoding_DefaultBinary): _logger.info("re-publish request (%s)", user) params = struct_from_binary(ua.RepublishParameters, body) msg = self.session.republish(params) response = ua.RepublishResponse() response.NotificationMessage = msg self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.CloseSecureChannelRequest_Encoding_DefaultBinary): _logger.info("close secure channel request (%s)", user) self._connection.close() response = ua.CloseSecureChannelResponse() self.send_response(requesthdr.RequestHandle, seqhdr, response) return False elif typeid == ua.NodeId( ua.ObjectIds.CallRequest_Encoding_DefaultBinary): _logger.info("call request (%s)", user) params = struct_from_binary(ua.CallParameters, body) results = await self.session.call(params.MethodsToCall) response = ua.CallResponse() response.Results = results self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.SetMonitoringModeRequest_Encoding_DefaultBinary): _logger.info("set monitoring mode request (%s)", user) params = struct_from_binary(ua.SetMonitoringModeParameters, body) # FIXME: Implement SetMonitoringMode # For now send dummy results to keep clients happy response = ua.SetMonitoringModeResponse() results = ua.SetMonitoringModeResult() ids = params.MonitoredItemIds statuses = [ua.StatusCode(ua.StatusCodes.Good) for node_id in ids] results.Results = statuses response.Parameters = results _logger.info("sending set monitoring mode response") self.send_response(requesthdr.RequestHandle, seqhdr, response) elif typeid == ua.NodeId( ua.ObjectIds.SetPublishingModeRequest_Encoding_DefaultBinary): _logger.info("set publishing mode request (%s)", user) params = struct_from_binary(ua.SetPublishingModeParameters, body) # FIXME: Implement SetPublishingMode # For now send dummy results to keep clients happy response = ua.SetPublishingModeResponse() results = ua.SetPublishingModeResult() ids = params.SubscriptionIds statuses = [ua.StatusCode(ua.StatusCodes.Good) for node_id in ids] results.Results = statuses response.Parameters = results _logger.info("sending set publishing mode response") self.send_response(requesthdr.RequestHandle, seqhdr, response) else: _logger.warning("Unknown message received %s (%s)", typeid, user) raise ServiceError(ua.StatusCodes.BadServiceUnsupported) return True
def _add_node(self, item, user, check=True): #self.logger.debug("Adding node %s %s", item.RequestedNewNodeId, item.BrowseName) result = ua.AddNodesResult() if not user == User.Admin: result.StatusCode = ua.StatusCode( ua.StatusCodes.BadUserAccessDenied) return result if item.RequestedNewNodeId.has_null_identifier(): # If Identifier of requested NodeId is null we generate a new NodeId using # the namespace of the nodeid, this is an extention of the spec to allow # to requests the server to generate a new nodeid in a specified namespace #self.logger.debug("RequestedNewNodeId has null identifier, generating Identifier") item.RequestedNewNodeId = self._aspace.generate_nodeid( item.RequestedNewNodeId.NamespaceIndex) else: if item.RequestedNewNodeId in self._aspace: self.logger.warning( "AddNodesItem: Requested NodeId %s already exists", item.RequestedNewNodeId) result.StatusCode = ua.StatusCode( ua.StatusCodes.BadNodeIdExists) return result if item.ParentNodeId.is_null(): # self.logger.info("add_node: while adding node %s, requested parent node is null %s %s", # item.RequestedNewNodeId, item.ParentNodeId, item.ParentNodeId.is_null()) if check: result.StatusCode = ua.StatusCode( ua.StatusCodes.BadParentNodeIdInvalid) return result parentdata = self._aspace.get(item.ParentNodeId) if parentdata is None and not item.ParentNodeId.is_null(): self.logger.info( "add_node: while adding node %s, requested parent node %s does not exists", item.RequestedNewNodeId, item.ParentNodeId) result.StatusCode = ua.StatusCode( ua.StatusCodes.BadParentNodeIdInvalid) return result nodedata = NodeData(item.RequestedNewNodeId) self._add_node_attributes(nodedata, item, add_timestamps=check) # now add our node to db self._aspace[nodedata.nodeid] = nodedata if parentdata is not None: self._add_ref_from_parent(nodedata, item, parentdata) self._add_ref_to_parent(nodedata, item, parentdata) # add type definition if item.TypeDefinition != ua.NodeId(): self._add_type_definition(nodedata, item) result.StatusCode = ua.StatusCode() result.AddedNodeId = nodedata.nodeid return result
async def test_xml_statuscode(opc, tmpdir): o = await opc.opc.nodes.objects.add_variable( 2, "xmllstatuscode", ua.StatusCode(value=ua.StatusCodes.BadNodeIdInvalid)) await _test_xml_var_type(opc, tmpdir, o, "statuscode") await opc.opc.delete_nodes([o])
def _parse_value(self, val_el, obj): """ Parse the node val_el as a constant. """ if val_el is not None and val_el.text is not None: ntag = self._retag.match(val_el.tag).groups()[1] else: ntag = "Null" obj.valuetype = ntag if ntag == "Null": obj.value = None elif hasattr(ua.ua_binary.Primitives1, ntag): # Elementary types have their parsing directly relying on ua_type_to_python. obj.value = ua_type_to_python(val_el.text, ntag) elif ntag == "DateTime": obj.value = ua_type_to_python(val_el.text, ntag) # According to specs, DateTime should be either UTC or with a timezone. if obj.value.tzinfo is None or obj.value.tzinfo.utcoffset(obj.value) is None: utc.localize(obj.value) # FIXME Forcing to UTC if unaware, maybe should raise? elif ntag == "ByteString": if val_el.text is None: mytext = b"" else: mytext = val_el.text.encode() mytext = base64.b64decode(mytext) obj.value = mytext elif ntag == "String" or ntag == "XmlElement": # String and XMLElement are identical only type is different mytext = val_el.text if mytext is None: # Support importing null strings. mytext = "" if ntag == "XmlElement": obj.value = ua.XmlElement(mytext) else: obj.value = mytext elif ntag == "Guid": self._parse_contained_value(val_el, obj) # Override parsed string type to guid. obj.valuetype = ntag elif ntag == "NodeId": id_el = val_el.find("uax:Identifier", self.ns) if id_el is not None: obj.value = id_el.text elif ntag == "ExpandedNodeId": id_el = val_el.find("uax:Identifier", self.ns) if id_el is not None: obj.value = ua.NodeId.from_string(id_el.text) if not isinstance(obj.value, ua.ExpandedNodeId): obj.value = ua.ExpandedNodeId(obj.value.Identifier, obj.value.NamespaceIndex) elif ntag == "ExtensionObject": obj.value = self._parse_ext_obj(val_el) elif ntag == "LocalizedText": obj.value = self._parse_body(val_el) elif ntag == "ListOfLocalizedText": obj.value = self._parse_list_of_localized_text(val_el) elif ntag == "ListOfExtensionObject": obj.value = self._parse_list_of_extension_object(val_el) elif ntag == "StatusCode": code_el = val_el.find("uax:Code", self.ns) val = code_el.text if code_el is not None else "0" obj.value = ua.StatusCode(string_to_val(val, ua.VariantType.UInt32)) elif ntag == "QualifiedName": obj.value = self._parse_qualifed_name(val_el) elif ntag.startswith("ListOf"): # Default case for "ListOf" types. # Should stay after particular cases (e.g.: "ListOfLocalizedText"). obj.value = [] for val_el in val_el: tmp = NodeData() self._parse_value(val_el, tmp) obj.value.append(tmp.value) else: # Missing according to ua.VariantType (also missing in string_to_val): # DataValue, Variant, DiagnosticInfo. self.logger.warning("Parsing value of type '%s' not implemented", ntag)