Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refractored add references functions to greatly improve xml import speed. #1111

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions asyncua/server/address_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that help?
I have been considering doing that in main library but i thought someone concluded it does not help much

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, from my tests this helps.



class AttributeValue(object):
"""
The class holds the value(s) of an attribute and callbacks.
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand Down Expand Up @@ -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}"
Expand All @@ -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()
Expand All @@ -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):
Expand Down
16 changes: 10 additions & 6 deletions asyncua/ua/uaprotocol_auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -5906,6 +5908,8 @@ def NodeClass(self):
def NodeClass(self, val):
self.NodeClass_ = val

def __hash__(self):
return hash((self.ReferenceTypeId.__hash__(), self.NodeId.__hash__(), self.IsForward))

@dataclass(frozen=FROZEN)
class BrowseResult:
Expand Down
12 changes: 1 addition & 11 deletions asyncua/ua/uatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember we considered using slot instead of our custon frozen thing. We had some reasons for not using it...not remembering what...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slots help, but it was not the biggest improvement. Will revert this

class NodeId:
"""
NodeId Object
Expand All @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you remove documentation?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc was redundant with the Args: above, also it does not match the dataclass argument list

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a special format to generate documentation automatically. better to update it than remove it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.
Thought the format above was supported (i think that is google style, also the one I use)

"""

Identifier: Union[Int32, String, Guid, ByteString] = 0
Expand Down