diff --git a/opcua/client/client.py b/opcua/client/client.py index 04d1e0bc3..a46de5470 100644 --- a/opcua/client/client.py +++ b/opcua/client/client.py @@ -586,8 +586,8 @@ def get_namespace_index(self, uri): uries = self.get_namespace_array() return uries.index(uri) - def delete_nodes(self, nodes, recursive=False): - return delete_nodes(self.uaclient, nodes, recursive) + def delete_nodes(self, nodes, recursive=False, batch=False): + return delete_nodes(self.uaclient, nodes, recursive, batch=batch) def import_xml(self, path=None, xmlstring=None): """ diff --git a/opcua/common/manage_nodes.py b/opcua/common/manage_nodes.py index 25619a3fc..b32f14c43 100644 --- a/opcua/common/manage_nodes.py +++ b/opcua/common/manage_nodes.py @@ -372,10 +372,12 @@ def _guess_datatype(variant): return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name)) -def delete_nodes(server, nodes, recursive=False, delete_target_references=True): +def delete_nodes(server, nodes, recursive=False, delete_target_references=True, batch=False): """ - Delete specified nodes. Optionally delete recursively all nodes with a - downward hierachic references to the node + Delete specified nodes. Optionally recursively delete all nodes with a + downward hierachic references to the node. + `batch` can be False or a integer. If a integer is given, batch the nodes + discovered into multiple DeleteNodesRequests. return the list of deleted node and the result """ nodestodelete = [] @@ -386,9 +388,23 @@ def delete_nodes(server, nodes, recursive=False, delete_target_references=True): it.NodeId = mynode.nodeid it.DeleteTargetReferences = delete_target_references nodestodelete.append(it) - params = ua.DeleteNodesParameters() - params.NodesToDelete = nodestodelete - return nodes, server.delete_nodes(params) + + if batch and isinstance(batch, int): + chunks = [ + nodestodelete[i : i + batch] + for i in range(0, len(nodestodelete), batch) + ] + + results = [] + for chunk in chunks: + params = ua.DeleteNodesParameters() + params.NodesToDelete = chunk + results.extend(server.delete_nodes(params)) + return nodes, results + else: + params = ua.DeleteNodesParameters() + params.NodesToDelete = nodestodelete + return nodes, server.delete_nodes(params) def _add_childs(nodes): diff --git a/opcua/server/server.py b/opcua/server/server.py index af69bfd24..18d808fc9 100644 --- a/opcua/server/server.py +++ b/opcua/server/server.py @@ -541,8 +541,8 @@ def export_xml_by_ns(self, path, namespaces=None): nodes = get_nodes_of_namespace(self, namespaces) self.export_xml(nodes, path) - def delete_nodes(self, nodes, recursive=False): - return delete_nodes(self.iserver.isession, nodes, recursive) + def delete_nodes(self, nodes, recursive=False, batch=False): + return delete_nodes(self.iserver.isession, nodes, recursive, batch=batch) def historize_node_data_change(self, node, period=timedelta(days=7), count=0): """ diff --git a/tests/tests_common.py b/tests/tests_common.py index 5def9436e..edab1aa01 100644 --- a/tests/tests_common.py +++ b/tests/tests_common.py @@ -173,6 +173,30 @@ def test_delete_nodes_recursive2(self): with self.assertRaises(ua.UaStatusCodeError): node.get_browse_name() + def test_delete_nodes_recursive_batching(self): + obj = self.opc.get_objects_node() + fold = obj.add_folder(2, "FolderToDeleteRoot") + nfold = fold + mynodes = [] + for i in range(7): + nfold = fold.add_folder(2, "FolderToDeleteRoot") + var = fold.add_variable(2, "VarToDeleteR", 9.1) + var = fold.add_property(2, "ProToDeleteR", 9.1) + prop = fold.add_property(2, "ProToDeleteR", 9.1) + o = fold.add_object(3, "ObjToDeleteR") + o_var = o.add_variable(3, "VarToDeleteRR", 9.2) + o_prop = o.add_property(3, "PropToDeleteRR", 9.2) + mynodes.append(nfold) + mynodes.append(var) + mynodes.append(prop) + mynodes.append(o) + mynodes.append(o_var) + mynodes.append(o_prop) + self.opc.delete_nodes([fold], recursive=True, batch=2) + for node in mynodes: + with self.assertRaises(ua.UaStatusCodeError): + node.get_browse_name() + def test_delete_references(self): newtype = self.opc.get_node(ua.ObjectIds.HierarchicalReferences).add_reference_type(0, "HasSuperSecretVariable")