FreeOpcUa / opcua-asyncio

OPC UA library for python >= 3.7
GNU Lesser General Public License v3.0
1.1k stars 357 forks source link

XML-Import: Opc.Ua.Di.NodeSet2.xml + Opc.Ua.Machinery.NodeSet2.xml #307

Closed AndreasHeine closed 3 years ago

AndreasHeine commented 3 years ago

Hey,

i am kind of confused, i looked for quite some time to find the issue but i did not found it yet... maybe someone sees what i dont!

  1. Import DI nodeset (Opc.Ua.Di.NodeSet2.xml)
  2. Import Machinery nodeset (Opc.Ua.Machinery.NodeSet2.xml)
  3. await server.load_type_definitions()
  4. IMachineryItemVendorNameplateType ??? and some more are not there...
<UAObjectType NodeId="ns=1;i=1003" BrowseName="1:IMachineryItemVendorNameplateType" IsAbstract="true">
    <DisplayName>IMachineryItemVendorNameplateType</DisplayName>
    <Description Locale="en">Interface containing identification and nameplate information for a MachineryItem provided by the vendor</Description>
    <Documentation>https://reference.opcfoundation.org/v104/Machinery/v100/docs/8.2</Documentation>
    <References>
      <Reference ReferenceType="HasSubtype" IsForward="false">ns=2;i=15035</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=6027</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=6022</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=6026</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=6024</Reference>
      <Reference ReferenceType="HasProperty">ns=1;i=6025</Reference>
      <Reference ReferenceType="HasInterface" IsForward="false">ns=1;i=1004</Reference>
    </References>
  </UAObjectType>

INFO:asyncua.common.xmlparser:Parsing node: ns=1;i=1003 1:IMachineryItemVendorNameplateType

???

pbertoni89 commented 3 years ago

Beside of that, you may have other Problems with your connection, which would be better in another Issue. :)

Dunno, because everything's on localhost and server-example.py works well on the same default port. Thank you anyway

swamper123 commented 3 years ago

*ping* Perhaps that issue may have evolve a bit, since some commits?

@AndreasHeine Can you make a check if your issue-Topics are still happening?

AndreasHeine commented 3 years ago

@swamper123 yes still existing!

swamper123 commented 3 years ago

I am on that problem at the moment and want to share my adventures insights. Perhaps somebody knows, where the root of all evil lays:

So I debugged step by step throw the actual import process. The Node was created, it exists on the server with an NodeID, but is not browsable in the addresspace. What is still possible is, to follow the Node threw the References:

1) Browse: ObjectTypes->BaseObjectType->FolderType->FunctionalGroupType->MachineryItemIdentificationType 2) In the References, you can see, that the Node owns a Reference to IMachineTagNamePlateType (as example) grafik 3) You can follow that Reference and got this: grafik

I don't know what is causing these problems. Perhaps HasInterface type is not correctly build in? Still a specific order I miss while import? Thankfull for every hint you may know in that case. 😕

swamper123 commented 3 years ago

🕵️ The Case of the missing Nodes is a step ahead. The problem realy seems to be the "HasInterface" reference, which causes the Node to be....invisible. The Node exists on the server. The invisible Node contains all References needed (even SubtypeOf exists). But the reference from the Parent to the "invisible" Node seems to be deleted.

I took the Machinery Companion Spec and commented out the "HasInterface" reference, and everything was fine for that single Node. Was Browsable and everything (just the Reference was missing).

I still don't know why and where this bloody deletion happens, but I am on track.

oroulet commented 3 years ago
swamper123 commented 3 years ago

It was added in an Amendment 7 in March 2019 (if I can trust the data on the doc):

https://reference.opcfoundation.org/v104/Core/docs/Amendment7/#11.20

The namespace of the nodes is correct as well (see the images above, they are ns=3). Even the References (from child Node, in this Case IMachineTagNameplateType) are correct, just not from the parent Node (MachineIdentificationType) as it seems.

And why the reference is "destroyed" is still unknown for me, since the other references should still work as normal.

swamper123 commented 3 years ago

I am hanging on that thing now since a couple of days and can't find a working solution yet, even if it sounds so easy... :disappointed:

What is happening:

What shall happen:

What happened while trying:

What I have now atm is this structure:

   async def import_xml(self, xmlpath=None, xmlstring=None):
        """
        import xml and return added nodes
        """
        if (xmlpath is None and xmlstring is None) or (xmlpath and xmlstring):
            raise ValueError("Expected either xmlpath or xmlstring, not both or neither.")
        _logger.info("Importing XML file %s", xmlpath)
        self.parser = XMLParser()
        await self._check_required_models(xmlpath, xmlstring)
        await self.parser.parse(xmlpath, xmlstring)
        self.namespaces = await self._map_namespaces()
        _logger.info("namespace map: %s", self.namespaces)
        self.aliases = self._map_aliases(self.parser.get_aliases())
        self.refs = []
        dnodes = self.parser.get_node_datas()
        dnodes = self.make_objects(dnodes)
        self._add_missing_parents(dnodes)
        nodes_parsed = self._sort_nodes_by_parentid(dnodes)
        nodes = []
        for nodedata in nodes_parsed:  # self.parser:
            try:
                node = await self._add_node_data(nodedata)
            except Exception:
                _logger.warning("failure adding node %s", nodedata)
                raise
            nodes.append(node)
        self.refs, remaining_refs = [], self.refs
        await self._add_references(remaining_refs)
        if len(self.refs):
            _logger.warning("The following references could not be imported and are probably broken: %s", self.refs)
        print("Stopo")
        await self._check_and_add_inverse_references(nodes_parsed)
        return nodes

    async def _check_and_add_inverse_references(self, nodedatas):
        # References which shall be uni-directional only:
        # GuardvariableType NodeId(15113)
        # Has Guard Reference Type NodeId(15112)
        # TransitionVariableType NodeId(2762)
        # StatemachineType NodeId(2771)
        # StateVariableType  NodeId(2755)
        # Two-stateVariableType NodeId(8995)
        # StateType NodeId(2307)
        # TransitionType NodeId(2310)
        # FiniteTransitionVariableType NodeId(2767)
        __unidirectional_nodes = {ua.NodeId(15113, 0),ua.NodeId(15112, 0),ua.NodeId(2762, 0),ua.NodeId(2771, 0),
                                  ua.NodeId(2755, 0),ua.NodeId(8995, 0),ua.NodeId(2307, 0),
                                  ua.NodeId(2310, 0),ua.NodeId(2767, 0),ua.NodeId(17603, 0)}
        for new_node in nodedatas:
            for ref in new_node.refs:
                if ref.reftype not in __unidirectional_nodes:
                    try:
                        # check if target Node even exist
                        n = self.server.iserver.isession.aspace[ref.target]
                    except KeyError:
                        logger.warning(f"Node {ref.target} does not exist in Server, but reference exist!")
                        continue
                    for t_ref in n.references:
                        if ref.target == t_ref.NodeId:
                            # perhaps more checks needed, like equal reference type?
                            break
                    else:
                        # Create a new reference and add it to the target_Node
                        #
                        print("hello")

Perhaps somebody have a hint/solution for me?

swamper123 commented 3 years ago

With #445 this and similar import problems should be solved. @AndreasHeine Could you verify, if all Topics of your post are fullfiled? In case it solves everything this case could be closed :detective:

AndreasHeine commented 3 years ago

@swamper123 sorry for the late reply... i confirme case closed good job!