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

Nodeset exporter should include the full namespace array, not just the namespaces of the nodes that are exported #1758

Open
CraigBuilds opened this issue Dec 5, 2024 · 1 comment

Comments

@CraigBuilds
Copy link

CraigBuilds commented Dec 5, 2024

Describe the bug

The namespace index of exported nodes will not match up with what is observed in UaExpert. This is because the nodeset only contains the namespaces of nodes in the NodeSet.

To Reproduce

Connect to a server and export the XML using a subset of all nodes, e.g

    client = Client(url)
    print("Connecting to server...")
    async with client as client:
        print("Connected to server")
        objects_node = client.get_objects_node()
        all_nodes: List[Node] = []
        await recursively_populate_children(objects_node, all_nodes)
        filtered_nodes: List[Node] = []
        for node in all_nodes:
            if await is_in_namespace(node, ns=3):
                filtered_nodes.append(node)
        await client.export_xml(filtered_nodes, output_file_path)

There will only be one element in the NamespaceUris array, e.g.

  <NamespaceUris>
    <Uri>http://www.helloworld.com/OPCUA/MyNamespace</Uri>
  </NamespaceUris>

And the namespace index of nodes will all be 1, and not 3 like they should be. (I think they are not 0, because the NamespaceUris implicitly contains http://opcfoundation.org/UA/ as index 0, even if it looks like the 0 index is http://www.helloworld.com/OPCUA/MyNamespace.

I think the issue is related to this function

    async def _add_namespaces(self, nodes):
        ns_array = await self.server.get_namespace_array()
        idxs = await self._get_ns_idxs_of_nodes(nodes)

        # now create a dict of idx_in_address_space to idx_in_exported_file
        self._addr_idx_to_xml_idx = self._make_idx_dict(idxs, ns_array)
        ns_to_export = [ns_array[i] for i in sorted(list(self._addr_idx_to_xml_idx.keys())) if i != 0]
        # write namespaces to xml
        self._add_namespace_uri_els(ns_to_export)

Version

Python-Version: 3
opcua-asyncio Version: 1.1.5

@CraigBuilds
Copy link
Author

I have used this as a workaround:

This calls add_namespace_uri_els with all namespaces before calling build_etree. To get around the issue of the use of the _addr_idx_to_xml_idx map while building the etree for each node, it overrides the _make_idx_dict function so the address space idxs are the same as the etree idxs.

async def export_xml_with_all_namespaces(client: Client, nodes: List[Node], output_file_path: str, export_values: bool):
        print("Warning! Exporting all namespaces is an experimental feature and may not work as expected.")
        exp = XmlExporter(client, export_values=export_values)
        ns_array = await exp.server.get_namespace_array()
        exp._add_namespace_uri_els(ns_array)
        def _make_idx_dict(idxs: List[int], ns_array: List[str])-> Dict[int, int]:
            return {idx: idx for idx in idxs}
        exp._make_idx_dict = _make_idx_dict
        await exp.build_etree(nodes)
        await exp.write_xml(output_file_path)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant