Explorar o código

NodeSetCompiler: Pull NamespacMapping out of datatypes.py

Julius Pfrommer %!s(int64=5) %!d(string=hai) anos
pai
achega
51ed5301ef

+ 23 - 29
tools/nodeset_compiler/datatypes.py

@@ -132,7 +132,7 @@ class Value(object):
             logger.error("Expected XML Element, but got junk...")
             return
 
-    def parseXMLEncoding(self, xmlvalue, parentDataTypeNode, parent, namespaceMapping):
+    def parseXMLEncoding(self, xmlvalue, parentDataTypeNode, parent):
         self.checkXML(xmlvalue)
         if not "value" in xmlvalue.localName.lower():
             logger.error("Expected <Value> , but found " + xmlvalue.localName + \
@@ -153,15 +153,15 @@ class Value(object):
             for el in xmlvalue.childNodes:
                 if not el.nodeType == el.ELEMENT_NODE:
                     continue
-                val = self.__parseXMLSingleValue(el, parentDataTypeNode, parent, namespaceMapping=namespaceMapping)
+                val = self.__parseXMLSingleValue(el, parentDataTypeNode, parent)
                 if val is None:
                     self.value = []
                     return
                 self.value.append(val)
         else:
-            self.value = [self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent, namespaceMapping=namespaceMapping)]
+            self.value = [self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent)]
 
-    def __parseXMLSingleValue(self, xmlvalue, parentDataTypeNode, parent, namespaceMapping, alias=None, encodingPart=None, valueRank=None):
+    def __parseXMLSingleValue(self, xmlvalue, parentDataTypeNode, parent, alias=None, encodingPart=None, valueRank=None):
         # Parse an encoding list such as enc = [[Int32], ['Duration', ['DateTime']]],
         # returning a possibly aliased variable or list of variables.
         # Keep track of aliases, as ['Duration', ['Hawaii', ['UtcTime', ['DateTime']]]]
@@ -198,26 +198,25 @@ class Value(object):
                                 if not el.nodeType == el.ELEMENT_NODE:
                                     continue
                                 val = self.getTypeByString(enc[0], enc)
-                                val.parseXML(el, namespaceMapping=namespaceMapping)
+                                val.parseXML(el)
                                 values.append(val)
                             return values
                         else:
                             if xmlvalue is not None:
-                                t.parseXML(xmlvalue, namespaceMapping=namespaceMapping)
+                                t.parseXML(xmlvalue)
                             return t
                 else:
                     if not valueIsInternalType(xmlvalue.localName):
                         logger.error(str(parent.id) + ": Expected XML describing builtin type " + enc[0] + " but found " + xmlvalue.localName + " instead")
                     else:
                         t = self.getTypeByString(enc[0], enc)
-                        t.parseXML(xmlvalue, namespaceMapping=namespaceMapping)
+                        t.parseXML(xmlvalue)
                         t.isInternal = True
                         return t
             else:
                 # 1: ['Alias', [...], n]
                 # Let the next elif handle this
                 return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent,
-                                                  namespaceMapping=namespaceMapping,
                                                   alias=alias, encodingPart=enc[0], valueRank=enc[2] if len(enc)>2 else None)
         elif len(enc) == 3 and isinstance(enc[0], string_types):
             # [ 'Alias', [...], 0 ]          aliased multipart
@@ -228,7 +227,6 @@ class Value(object):
                 alias = enc[0]
             # otherwise drop the alias
             return self.__parseXMLSingleValue(xmlvalue, parentDataTypeNode, parent,
-                                              namespaceMapping=namespaceMapping,
                                               alias=alias, encodingPart=enc[1], valueRank=enc[2] if len(enc)>2 else None)
         else:
             # [ [...], [...], [...]] multifield of unknowns (analyse separately)
@@ -286,7 +284,8 @@ class Value(object):
 
             extobj.value = []
             for e in enc:
-                extobj.value.append(extobj.__parseXMLSingleValue(ebodypart, parentDataTypeNode, parent, namespaceMapping=namespaceMapping, alias=None, encodingPart=e))
+                extobj.value.append(extobj.__parseXMLSingleValue(ebodypart, parentDataTypeNode, parent,
+                                                                 alias=None, encodingPart=e))
                 ebodypart = getNextElementNode(ebodypart)
             return extobj
 
@@ -320,7 +319,7 @@ class Boolean(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <Boolean>value</Boolean> or
         #        <Aliasname>value</Aliasname>
         self.checkXML(xmlvalue)
@@ -339,7 +338,7 @@ class Number(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <Int16>value</Int16> or any other valid number type, or
         #        <Aliasname>value</Aliasname>
         self.checkXML(xmlvalue)
@@ -412,7 +411,7 @@ class Float(Number):
         if xmlelement:
             Float.parseXML(self, xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <Float>value</Float> or
         #        <Aliasname>value</Aliasname>
         self.checkXML(xmlvalue)
@@ -436,7 +435,7 @@ class String(Value):
         bin = bin + str(self.value)
         return bin
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <String>value</String> or
         #        <Aliasname>value</Aliasname>
         if not isinstance(xmlvalue, dom.Element):
@@ -455,7 +454,7 @@ class ByteString(Value):
     def __init__(self, xmlelement=None):
         Value.__init__(self)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <ByteString>value</ByteString>
         if not isinstance(xmlvalue, dom.Element):
             self.value = xmlvalue
@@ -472,7 +471,7 @@ class ExtensionObject(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlelement, namespaceMapping=None):
+    def parseXML(self, xmlelement):
         pass
 
     def __str__(self):
@@ -486,7 +485,7 @@ class LocalizedText(Value):
         if xmlvalue:
             self.parseXML(xmlvalue)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <LocalizedText> or <AliasName>
         #          <Locale>xx_XX</Locale>
         #          <Text>TextText</Text>
@@ -553,7 +552,8 @@ class NodeId(Value):
             else:
                 raise Exception("no valid nodeid: " + idstring)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    # The parsing can be called with an optional namespace mapping dict.
+    def parseXML(self, xmlvalue):
         # Expect <NodeId> or <Alias>
         #           <Identifier> # It is unclear whether or not this is manditory. Identifier tags are used in Namespace 0.
         #                ns=x;i=y or similar string representation of id()
@@ -573,8 +573,6 @@ class NodeId(Value):
             if len(xmlvalue.getElementsByTagName("Identifier")) != 0:
                 xmlvalue = xmlvalue.getElementsByTagName("Identifier")[0]
             self.setFromIdString(unicode(xmlvalue.firstChild.data))
-            if namespaceMapping is not None:
-                self.ns = namespaceMapping[self.ns]
 
     def __str__(self):
         s = "ns=" + str(self.ns) + ";"
@@ -615,7 +613,7 @@ class ExpandedNodeId(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         self.checkXML(xmlvalue)
         logger.debug("Not implemented", LOG_LEVEL_ERR)
 
@@ -625,7 +623,7 @@ class DateTime(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <DateTime> or <AliasName>
         #        2013-08-13T21:00:05.0000L
         #        </DateTime> or </AliasName>
@@ -661,7 +659,7 @@ class QualifiedName(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         # Expect <QualifiedName> or <AliasName>
         #           <NamespaceIndex>Int16<NamespaceIndex>
         #           <Name>SomeString<Name>
@@ -673,16 +671,12 @@ class QualifiedName(Value):
             else:
                 self.name = xmlvalue[colonindex + 1:]
                 self.ns = int(xmlvalue[:colonindex])
-                if namespaceMapping is not None:
-                    self.ns = namespaceMapping[self.ns]
             return
 
         self.checkXML(xmlvalue)
         # Is a namespace index passed?
         if len(xmlvalue.getElementsByTagName("NamespaceIndex")) != 0:
             self.ns = int(xmlvalue.getElementsByTagName("NamespaceIndex")[0].firstChild.data)
-            if namespaceMapping is not None:
-                self.ns = namespaceMapping[self.ns]
         if len(xmlvalue.getElementsByTagName("Name")) != 0:
             self.name = xmlvalue.getElementsByTagName("Name")[0].firstChild.data
 
@@ -703,7 +697,7 @@ class DiagnosticInfo(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         self.checkXML(xmlvalue)
         logger.warn("Not implemented")
 
@@ -713,7 +707,7 @@ class Guid(Value):
         if xmlelement:
             self.parseXML(xmlelement)
 
-    def parseXML(self, xmlvalue, namespaceMapping=None):
+    def parseXML(self, xmlvalue):
         self.checkXML(xmlvalue)
 
         val = getXmlTextTrimmed(xmlvalue.firstChild)

+ 9 - 6
tools/nodeset_compiler/nodes.py

@@ -288,7 +288,7 @@ class VariableNode(Node):
             return False
 
         self.value = Value()
-        self.value.parseXMLEncoding(self.xmlValueDef, dataTypeNode, self, nodeset.namespaceMapping[self.modelUri])
+        self.value.parseXMLEncoding(self.xmlValueDef, dataTypeNode, self)
         return True
 
 
@@ -420,7 +420,7 @@ class DataTypeNode(Node):
             return self.__baseTypeEncoding__
 
 
-    def buildEncoding(self, nodeset, indent=0, force=False):
+    def buildEncoding(self, nodeset, indent=0, force=False, namespaceMapping=None):
         """ buildEncoding() determines the structure and aliases used for variables
             of this DataType.
 
@@ -498,7 +498,8 @@ class DataTypeNode(Node):
         if self.__xmlDefinition__ is None:
             if parentType is not None:
                 logger.debug( prefix + "Attempting definition using supertype " + str(targetNode.browseName) + " for DataType " + " " + str(self.browseName))
-                subenc = targetNode.buildEncoding(nodeset=nodeset, indent=indent+1)
+                subenc = targetNode.buildEncoding(nodeset=nodeset, indent=indent+1,
+                                                  namespaceMapping=namespaceMapping)
                 if not targetNode.isEncodable():
                     self.__encodable__ = False
                 else:
@@ -570,7 +571,8 @@ class DataTypeNode(Node):
                     # This might be a subtype... follow the node defined as datatype to find out
                     # what encoding to use
                     fdTypeNodeId = NodeId(fdtype)
-                    fdTypeNodeId.ns = nodeset.namespaceMapping[self.modelUri][fdTypeNodeId.ns]
+                    if namespaceMapping != None:
+                        fdTypeNodeId.ns = namespaceMapping[fdTypeNodeId.ns]
                     if not fdTypeNodeId in nodeset.nodes:
                         raise Exception("Node {} not found in nodeset".format(fdTypeNodeId))
                     dtnode = nodeset.nodes[fdTypeNodeId]
@@ -579,7 +581,8 @@ class DataTypeNode(Node):
                     typeDict.append([fname, dtnode])
                     fdtype = str(dtnode.browseName.name)
                     logger.debug( prefix + fname + " : " + fdtype + " -> " + str(dtnode.id))
-                    subenc = dtnode.buildEncoding(nodeset=nodeset, indent=indent+1)
+                    subenc = dtnode.buildEncoding(nodeset=nodeset, indent=indent+1,
+                                                  namespaceMapping=namespaceMapping)
                     self.__baseTypeEncoding__ = self.__baseTypeEncoding__ + [[fname, subenc, valueRank]]
                     if not dtnode.isEncodable():
                         # If we inherit an encoding from an unencodable not, this node is
@@ -596,7 +599,7 @@ class DataTypeNode(Node):
 
         if isOptionSet == True:
             self.__isOptionSet__ = True
-            subenc = parentType.buildEncoding(nodeset=nodeset)
+            subenc = parentType.buildEncoding(nodeset=nodeset, namespaceMapping=namespaceMapping)
             if not parentType.isEncodable():
                 self.__encodable__ = False
             else:

+ 14 - 17
tools/nodeset_compiler/nodeset.py

@@ -113,7 +113,6 @@ class NodeSet(object):
         self.nodes = {}
         self.aliases = {}
         self.namespaces = ["http://opcfoundation.org/UA/"]
-        self.namespaceMapping = {};
 
     def sanitize(self):
         for n in self.nodes.values():
@@ -128,6 +127,7 @@ class NodeSet(object):
                 if not ref.referenceType in self.nodes:
                     raise Exception("Reference " + str(ref) + " has an unknown reference type")
                 if not ref.target in self.nodes:
+                    print(self.namespaces)
                     raise Exception("Reference " + str(ref) + " has an unknown target")
 
     def addNamespace(self, nsURL):
@@ -233,7 +233,7 @@ class NodeSet(object):
 
         for ns in orig_namespaces:
             self.addNamespace(ns)
-        self.namespaceMapping[modelUri] = self.createNamespaceMapping(orig_namespaces)
+        namespaceMapping = self.createNamespaceMapping(orig_namespaces) # mapping for this file
 
         # Extract the aliases
         for nd in nodeset.childNodes:
@@ -244,7 +244,7 @@ class NodeSet(object):
                 self.aliases = self.merge_dicts(self.aliases, buildAliasList(nd))
 
         # Instantiate nodes
-        newnodes = []
+        newnodes = {}
         for nd in nodeset.childNodes:
             if nd.nodeType != nd.ELEMENT_NODE:
                 continue
@@ -252,14 +252,23 @@ class NodeSet(object):
             if not node:
                 continue
             node.replaceAliases(self.aliases)
-            node.replaceNamespaces(self.namespaceMapping[modelUri])
+            node.replaceNamespaces(namespaceMapping)
             node.typesArray = typesArray
 
             # Add the node the the global dict
             if node.id in self.nodes:
                 raise Exception("XMLElement with duplicate ID " + str(node.id))
             self.nodes[node.id] = node
-            newnodes.append(node)
+            newnodes[node.id] = node
+
+        # Parse Datatypes in order to find out what the XML keyed values actually
+        # represent.
+        # Ex. <rpm>123</rpm> is not encodable
+        #     only after parsing the datatypes, it is known that
+        #     rpm is encoded as a double
+        for n in newnodes.values():
+            if isinstance(n, DataTypeNode):
+                n.buildEncoding(self, namespaceMapping=namespaceMapping)
 
     def getBinaryEncodingIdForNode(self, nodeId):
         """
@@ -275,18 +284,6 @@ class NodeSet(object):
                     return ref.target
         raise Exception("No DefaultBinary encoding defined for node " + str(nodeId))
 
-    def buildEncodingRules(self):
-        """ Calls buildEncoding() for all DataType nodes (opcua_node_dataType_t).
-
-            No return value
-        """
-        stat = {True: 0, False: 0}
-        for n in self.nodes.values():
-            if isinstance(n, DataTypeNode):
-                n.buildEncoding(self)
-                stat[n.isEncodable()] = stat[n.isEncodable()] + 1
-        logger.debug("Type definitions built/passed: " +  str(stat))
-
     def allocateVariables(self):
         for n in self.nodes.values():
             if isinstance(n, VariableNode):

+ 0 - 8
tools/nodeset_compiler/nodeset_compiler.py

@@ -160,14 +160,6 @@ for ignoreFile in args.ignoreFiles:
 # unresolvable or no references or invalid NodeIDs
 ns.sanitize()
 
-
-# Parse Datatypes in order to find out what the XML keyed values actually
-# represent.
-# Ex. <rpm>123</rpm> is not encodable
-#     only after parsing the datatypes, it is known that
-#     rpm is encoded as a double
-ns.buildEncodingRules()
-
 # Allocate/Parse the data values. In order to do this, we must have run
 # buidEncodingRules.
 ns.allocateVariables()

+ 1 - 4
tools/nodeset_compiler/nodeset_testing.py

@@ -25,10 +25,7 @@ class testing:
         self.ns.linkOpenPointers()
         self.ns.sanitize()
 
-        logger.debug("Phase 3: Comprehending DataType encoding rules")
-        self.ns.buildEncodingRules()
-
-        logger.debug("Phase 4: Allocating variable value data")
+        logger.debug("Phase 3: Allocating variable value data")
         self.ns.allocateVariables()
 
         bin = self.ns.buildBinary()