python-opcua icon indicating copy to clipboard operation
python-opcua copied to clipboard

`BadParentNodeIdInvalid` when importing xml with user-defined UAVariableType.

Open wjjfromSCN opened this issue 5 years ago • 13 comments

Problem

When importing Opc.Ua.AMLBaseTypes.NodeSet2.xml, it raised BadParentNodeIdInvalid with information:

failure adding node NodeData(nodeid:NumericNodeId(ns=1;i=1010))

Analysis

The node 1010 is defined at Line 175

<UAVariable NodeId="ns=1;i=1010" BrowseName="1:ID" ParentNodeId="ns=1;i=3002" DataType="String">

Its parent node is a user-defined UAVariableType, defined at Line 282

  <UAVariableType NodeId="ns=1;i=3002" BrowseName="1:AMLOpcUaConnectionType">

Detailed Steps & Version Information:

# Name: opcua
# Version: 0.98.11
import sys
from opcua import ua, Server, instantiate
from opcua.common.xmlexporter import XmlExporter

server = Server()
node = server.import_xml("Opc.Ua.AMLBaseTypes.NodeSet2.xml")

Same problem happend when import xml from GUI opcua-modeler Ver. 0.5.9

wjjfromSCN avatar Jun 02 '20 13:06 wjjfromSCN

same with Opc.Ua.Robotics.NodeSet2.xml !!!

AndreasHeine avatar Jun 02 '20 20:06 AndreasHeine

Call Stack Analysis

I found the problem is caused by xmlimporter.py#L174

# in method add_variable(self, obj):
node = self._get_node(obj)

Input:

  • obj
    • parent: NumericNodeId(ns=1;i=3002)
    • parentlink: None,

Output

  • node
    • ParentNodeId: TwoByteNodeId(i=0).

The method _get_node skipped the assignment of node.ParentNodeId when obj.parentlink is None, see xmlimporter.py#L148

# in method _get_node(self, obj):
if obj.parent and obj.parentlink:
    node.ParentNodeId = self._migrate_ns(obj.parent)
    node.ReferenceTypeId = self._migrate_ns(obj.parentlink)

BigeYoung avatar Jun 03 '20 03:06 BigeYoung

obj.parentlink is None is caused by method _parse_refs. If the ParentNodeId is not in Refereces, the parentlink will remain None.

def _parse_refs(self, el, obj):
    parent, parentlink = obj.parent, None

    for ref in el:
        struct = RefStruct()
        struct.forward = "IsForward" not in ref.attrib or ref.attrib["IsForward"] not in ("false", "False")
        struct.target = ref.text
        struct.reftype = ref.attrib["ReferenceType"]
        obj.refs.append(struct)

        if ref.attrib["ReferenceType"] == "HasTypeDefinition":
            obj.typedef = ref.text
        elif not struct.forward:
            parent, parentlink = struct.target, struct.reftype
            if obj.parent == parent:
                obj.parentlink = parentlink

    if not obj.parent or not obj.parentlink:
        obj.parent, obj.parentlink = parent, parentlink
        self.logger.info("Could not detect backward reference to parent for node '%s'", obj.nodeid)

This case can be found in Opc.Ua.AMLBaseTypes.NodeSet2.xml

<UAVariable NodeId="ns=1;i=1010" BrowseName="1:ID" ParentNodeId="ns=1;i=3002" DataType="String">
  <DisplayName>ID</DisplayName>
  <References>
    <Reference ReferenceType="HasTypeDefinition">i=68</Reference>
    <Reference ReferenceType="HasModellingRule">i=80</Reference>
  </References>
</UAVariable>

BigeYoung avatar Jun 03 '20 03:06 BigeYoung

It seems like its a OPCFoundation issue

BigeYoung avatar Jun 03 '20 06:06 BigeYoung

@BigeYoung is it solved or not? i am interested!

AndreasHeine avatar Jun 03 '20 19:06 AndreasHeine

@BigeYoung is it solved or not? i am interested!

Yes, I solved it. See the links below for more details. https://github.com/OPCFoundation/UA-Nodeset/issues/60 https://github.com/OPCFoundation/UA-Nodeset/pull/61

BigeYoung avatar Jun 04 '20 02:06 BigeYoung

@BigeYoung is it solved or not? i am interested!

Oh, I've also checked your file. It seems your issue is not the same as mine.

# -*- coding: UTF-8 -*-
from lxml import etree as ET
file_path = "Opc.Ua.Robotics.NodeSet2.xml"
nodelist = {}
tree = ET.parse(file_path)
xml = tree.getroot()
for child in xml:
    ParentNodeId = child.attrib.get("ParentNodeId", None)
    References = child.find("{http://opcfoundation.org/UA/2011/03/UANodeSet.xsd}References")
    if ParentNodeId and References:
        unfound = True
        for Ref in References:
            if Ref.text == ParentNodeId:
                unfound = False
                break
        if unfound:
            nodelist[child.attrib.get("NodeId")] = [ParentNodeId]

The nodelist is still empty, it means that every nodes in this file have references to thier parent.

BigeYoung avatar Jun 04 '20 02:06 BigeYoung

@BigeYoung is it solved or not? i am interested!

The good news is, your problem is much more easier to be solved.

The file Opc.Ua.Robotics.NodeSet2.xml required another model named Opc.Ua.Di.NodeSet2.xml, see RequiredModel at Opc.Ua.Robotics.NodeSet2.xml#L34

<RequiredModel ModelUri="http://opcfoundation.org/UA/DI/" Version="1.02" PublicationDate="2019-05-01T00:00:00Z" />

So you need to import Opc.Ua.Di.NodeSet2.xml before you import Opc.Ua.Robotics.NodeSet2.xml.

from opcua import ua, Server
from opcua.common.xmlimporter import XmlImporter
server = Server()
server.import_xml("Opc.Ua.Di.NodeSet2.xml")
server.import_xml("Opc.Ua.Robotics.NodeSet2.xml")

I've tested it and hope it works for you!

BigeYoung avatar Jun 04 '20 02:06 BigeYoung

@BigeYoung : Yes for sure it works! Thanks :) i have rarely used theose special-nodeset

AndreasHeine avatar Jun 05 '20 10:06 AndreasHeine

According to opcfoundation-org, it is still a FreeOpcUa issue. See https://github.com/OPCFoundation/UA-Nodeset/issues/60#issuecomment-640907962

BigeYoung avatar Jun 09 '20 01:06 BigeYoung

Their answer seems trivial. Don't reference both direction to save XML file size? All this does is make XML parsing even more complicated and probably slower as well.

zerox1212 avatar Jun 09 '20 04:06 zerox1212

when i am importing OPC UA devices, OPCUA machinery , and OPCUA pump , gertting this error : Parsing value of type 'QualifiedName' not implemented

here is the code i am using :

Create server

server = Server()

Set server endpoint

url = "opc.tcp://10.220.1.163:4140" server.set_endpoint(url)

Set server name

server.set_server_name("OPC UA Server")

server.import_xml("Opc.Ua.Di.NodeSet2.xml") server.import_xml("Opc.Ua.Machinery.Nodeset2.xml") server.import_xml("Opc.Ua.Pumps.NodeSet2.xml") server.import_xml("NetschPump.xml")

server.start()

can somebody tell me what is the error ? i have been trying to solve since 2 days, but did not find any clue. your advices and solution would be appreciated.

anshi43 avatar Jun 05 '23 10:06 anshi43

You need to switch to opcua-asyncio which also has a sync-wrapper, with very few changes in API. Because this library is not supported anymore. In asyncua we have fixed a lot of bug with the xml importer.

schroeder- avatar Jun 05 '23 11:06 schroeder-