From e43cbe9b7e0ba274e986cc6ac04ffa16bdad0f86 Mon Sep 17 00:00:00 2001 From: JPortela Date: Thu, 3 Nov 2022 01:20:37 +0100 Subject: [PATCH 1/3] refratored add references functions to greatly improve xml import speed. --- asyncua/server/address_space.py | 27 ++++++++++++++------------- asyncua/ua/uaprotocol_auto.py | 2 ++ asyncua/ua/uatypes.py | 12 +----------- 3 files changed, 17 insertions(+), 24 deletions(-) diff --git a/asyncua/server/address_space.py b/asyncua/server/address_space.py index 41bbb65e6..d4fad667c 100644 --- a/asyncua/server/address_space.py +++ b/asyncua/server/address_space.py @@ -33,6 +33,13 @@ _logger = logging.getLogger(__name__) +# usefull const NodeIds: avoid constructing multiple times for comparsion purpose +NODE_ID_HAS_PROPERTY = ua.NodeId(ua.ObjectIds.HasProperty) +NODE_ID_NULL = ua.NodeId(ua.ObjectIds.Null) +NODE_ID_HASSUBTYPE = ua.NodeId(ua.ObjectIds.HasSubtype) +NODE_ID_DEFAULT = ua.NodeId() + + class AttributeValue(object): """ The class holds the value(s) of an attribute and callbacks. @@ -55,7 +62,7 @@ class NodeData: def __init__(self, nodeid: ua.NodeId): self.nodeid = nodeid self.attributes: Dict[ua.AttributeIds, AttributeValue] = {} - self.references: List[ua.ReferenceDescription] = [] + self.references: set[ua.ReferenceDescription] = set() # use a set because it guaranties uniqueness self.call = None def __str__(self) -> str: @@ -148,7 +155,7 @@ def _is_suitable_ref(self, desc: ua.BrowseDescription, ref: ua.ReferenceDescript return True def _suitable_reftype(self, ref1: ua.NodeId, ref2: ua.NodeId, subtypes: bool) -> bool: - if ref1 == ua.NodeId(ua.ObjectIds.Null): + if ref1 == NODE_ID_NULL: # If ReferenceTypeId is not specified in the BrowseDescription, # all References are returned and includeSubtypes is ignored. return True @@ -162,7 +169,8 @@ def _get_sub_ref(self, ref: ua.NodeId) -> Generator[ua.NodeId, None, None]: nodedata = self._aspace.get(ref) if nodedata is not None: for ref_desc in nodedata.references: - if ref_desc.ReferenceTypeId == ua.NodeId(ua.ObjectIds.HasSubtype) and ref_desc.IsForward: + # first compare the bool, as it is faster, if false we save time + if ref_desc.IsForward and ref_desc.ReferenceTypeId == NODE_ID_HASSUBTYPE: yield ref_desc.NodeId yield from self._get_sub_ref(ref_desc.NodeId) @@ -298,7 +306,7 @@ def _add_node(self, item: ua.AddNodesItem, user: User, check: bool = True) -> ua # check properties for ref in self._aspace[item.ParentNodeId].references: - if ref.ReferenceTypeId == ua.NodeId(ua.ObjectIds.HasProperty): + if ref.ReferenceTypeId == NODE_ID_HAS_PROPERTY: if item.BrowseName.Name == ref.BrowseName.Name: self.logger.warning( f"AddNodesItem: Requested Browsename {item.BrowseName.Name}" @@ -320,7 +328,7 @@ def _add_node(self, item: ua.AddNodesItem, user: User, check: bool = True) -> ua self._add_ref_to_parent(nodedata, item, parentdata) # add type definition - if item.TypeDefinition != ua.NodeId(): + if item.TypeDefinition != NODE_ID_DEFAULT: self._add_type_definition(nodedata, item) result.StatusCode = ua.StatusCode() @@ -343,14 +351,7 @@ def _add_node_attributes(self, nodedata: NodeData, item: ua.AddNodesItem, add_ti self._add_nodeattributes(item.NodeAttributes, nodedata, add_timestamps) def _add_unique_reference(self, nodedata: NodeData, desc: ua.ReferenceDescription): - for r in nodedata.references: - if r.ReferenceTypeId == desc.ReferenceTypeId and r.NodeId == desc.NodeId: - if r.IsForward != desc.IsForward: - self.logger.error("Cannot add conflicting reference %s ", str(desc)) - return ua.StatusCode(ua.StatusCodes.BadReferenceNotAllowed) - break # ref already exists - else: - nodedata.references.append(desc) + nodedata.references.add(desc) return ua.StatusCode() def _add_ref_from_parent(self, nodedata, item, parentdata): diff --git a/asyncua/ua/uaprotocol_auto.py b/asyncua/ua/uaprotocol_auto.py index d96c77cf6..5bbdd4a9c 100644 --- a/asyncua/ua/uaprotocol_auto.py +++ b/asyncua/ua/uaprotocol_auto.py @@ -5906,6 +5906,8 @@ def NodeClass(self): def NodeClass(self, val): self.NodeClass_ = val + def __hash__(self): + return hash((self.ReferenceTypeId, self.NodeId, self.IsForward)) @dataclass(frozen=FROZEN) class BrowseResult: diff --git a/asyncua/ua/uatypes.py b/asyncua/ua/uatypes.py index ee96af5c4..7f42cccf3 100644 --- a/asyncua/ua/uatypes.py +++ b/asyncua/ua/uatypes.py @@ -380,7 +380,7 @@ class NodeIdType(IntEnum): _NodeIdType = NodeIdType # ugly hack -@dataclass(frozen=True, eq=False, order=False) +@dataclass(slots=True, frozen=True, eq=False, order=False) class NodeId: """ NodeId Object @@ -390,16 +390,6 @@ class NodeId: namespaceidx(int): The index of the namespace nodeidtype(NodeIdType): The type of the nodeid if it cannot be guess or you want something special like twobyte nodeid or fourbytenodeid - - - :ivar Identifier: - :vartype Identifier: NodeId - :ivar NamespaceIndex: - :vartype NamespaceIndex: Int - :ivar NamespaceUri: - :vartype NamespaceUri: String - :ivar ServerIndex: - :vartype ServerIndex: Int """ Identifier: Union[Int32, String, Guid, ByteString] = 0 From 374e8e86d426c626dfccf98b19d23dee4c69764b Mon Sep 17 00:00:00 2001 From: JPortela Date: Thu, 3 Nov 2022 14:16:57 +0100 Subject: [PATCH 2/3] fix hash --- asyncua/ua/uaprotocol_auto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asyncua/ua/uaprotocol_auto.py b/asyncua/ua/uaprotocol_auto.py index 5bbdd4a9c..df51ef39e 100644 --- a/asyncua/ua/uaprotocol_auto.py +++ b/asyncua/ua/uaprotocol_auto.py @@ -5907,7 +5907,7 @@ def NodeClass(self, val): self.NodeClass_ = val def __hash__(self): - return hash((self.ReferenceTypeId, self.NodeId, self.IsForward)) + return hash((self.ReferenceTypeId.__hash__(), self.NodeId.__hash__(), self.IsForward)) @dataclass(frozen=FROZEN) class BrowseResult: From 01d0ee0bbc20f4e2e89bcc643b99820b1277d5cf Mon Sep 17 00:00:00 2001 From: JPortela Date: Thu, 3 Nov 2022 15:22:23 +0100 Subject: [PATCH 3/3] fix ReferenceDescription. This changes need to be done via generator! --- asyncua/ua/uaprotocol_auto.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/asyncua/ua/uaprotocol_auto.py b/asyncua/ua/uaprotocol_auto.py index df51ef39e..bdb2e207e 100644 --- a/asyncua/ua/uaprotocol_auto.py +++ b/asyncua/ua/uaprotocol_auto.py @@ -5888,15 +5888,17 @@ class ReferenceDescription: :vartype TypeDefinition: ExpandedNodeId """ - data_type = NodeId(ObjectIds.ReferenceDescription) + data_type : NodeId = field(default=NodeId(ObjectIds.ReferenceDescription), compare=False) ReferenceTypeId: NodeId = field(default_factory=NodeId) - IsForward: Boolean = True + IsForward: Boolean = field(default=True) NodeId: ExpandedNodeId = field(default_factory=ExpandedNodeId) - BrowseName: QualifiedName = field(default_factory=QualifiedName) - DisplayName: LocalizedText = field(default_factory=LocalizedText) - NodeClass_: NodeClass = NodeClass.Unspecified - TypeDefinition: ExpandedNodeId = field(default_factory=ExpandedNodeId) + + BrowseName: QualifiedName = field(default_factory=QualifiedName, compare=False) + DisplayName: LocalizedText = field(default_factory=LocalizedText, compare=False) + NodeClass_: NodeClass = field(default=NodeClass.Unspecified, compare=False) + TypeDefinition: ExpandedNodeId = field(default_factory=ExpandedNodeId, compare=False) + @property def NodeClass(self):