Kaynağa Gözat

Alternative for sorting in namespace compiler

* introduce `--high-level-api` option for namespace compiler
* make nodeset_example.xml more complex
Markus Graube 8 yıl önce
ebeveyn
işleme
d0efc73622

+ 16 - 16
CMakeLists.txt

@@ -477,24 +477,24 @@ add_dependencies(open62541-amalgamation-source open62541-generator-types
 
 # generated namespace 0
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
-        PRE_BUILD
-        COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-        -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_AssumeExternal.txt
-        -s description -b ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist.txt
-        ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated
-        DEPENDS ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
-        ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py)
+                          ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h
+                   PRE_BUILD
+                   COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
+                           -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_AssumeExternal.txt
+                           -s description -b ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist.txt
+                           ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
+                           ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated
+                   DEPENDS ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/${GENERATE_NAMESPACE0_FILE}
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
+                           ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py)
 # we need a custom target to avoid that the generator is called concurrently and thus overwriting files while the other thread is compiling
 add_custom_target(open62541-generator-namespace DEPENDS
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
-        ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h)
+                  ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.c
+                  ${PROJECT_BINARY_DIR}/src_generated/ua_namespaceinit_generated.h)
 
 #####################
 # Build the Library #

+ 29 - 22
examples/CMakeLists.txt

@@ -79,29 +79,35 @@ if(UA_ENABLE_NODEMANAGEMENT)
 endif()
 
 if(UA_BUILD_EXAMPLES_NODESET_COMPILER)
-  if(BUILD_SHARED_LIBS)
-    message(FATAL_ERROR "The nodeset compiler currently requires static linking to access internal API")
-  endif()
+    if(BUILD_SHARED_LIBS)
+        message(FATAL_ERROR "The nodeset compiler currently requires static linking to access internal API")
+    endif()
 
-  # example information model from nodeset xml
-  add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
-                    PRE_BUILD
-                    COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-                                                 -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
-                                                 ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
-                                                 ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml
-                                                 ${PROJECT_BINARY_DIR}/src_generated/nodeset
-                    DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
-                            ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
-                            ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
-
-  # needs internal methods which are not exported in the dynamic lib
-  add_example(server_nodeset ${PROJECT_BINARY_DIR}/src_generated/nodeset.c server_nodeset.c)
+    # example information model from nodeset xml
+    add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/nodeset.h ${PROJECT_BINARY_DIR}/src_generated/nodeset.c
+                      PRE_BUILD
+                      COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
+                                                    -i ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
+                                                    --high-level-api
+                                                    ${PROJECT_SOURCE_DIR}/tools/schema/namespace0/Opc.Ua.NodeSet2.xml
+                                                    ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml
+                                                    #${PROJECT_SOURCE_DIR}/examples/Opc.ISA95.NodeSet2.xml
+                                                    #${PROJECT_SOURCE_DIR}/examples/Opc.Ua.AMLBaseTypes.NodeSet2.xml
+                                                    #${PROJECT_SOURCE_DIR}/examples/Opc.Ua.AMLLibraries.NodeSet2.xml
+                                                    #${PROJECT_SOURCE_DIR}/examples/Opc.Ua.Di.NodeSet2.xml
+                                                    #${PROJECT_SOURCE_DIR}/examples/Opc.Ua.Adi.NodeSet2.xml
+                                                    ${PROJECT_BINARY_DIR}/src_generated/nodeset
+                      DEPENDS ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/generate_open62541CCode.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/open62541_MacroHelper.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_builtin_types.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_constants.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_namespace.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/ua_node_types.py
+                              ${PROJECT_SOURCE_DIR}/tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt
+                              ${PROJECT_SOURCE_DIR}/examples/server_nodeset.xml)
+
+  add_executable(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/nodeset.c $<TARGET_OBJECTS:open62541-object>)
+  target_link_libraries(server_nodeset ${LIBS})
   target_include_directories(server_nodeset PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/deps) # needs an internal header
   set_target_properties(server_nodeset PROPERTIES COMPILE_FLAGS "-Wno-pedantic -Wno-sign-conversion")
 endif()
@@ -112,6 +118,7 @@ if(UA_BUILD_SELFSIGNED_CERTIFICATE)
                      COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${CMAKE_CURRENT_BINARY_DIR}
                      DEPENDS ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py
                              ${PROJECT_SOURCE_DIR}/tools/certs/localhost.cnf)
+                             
   add_custom_target(selfsigned ALL DEPENDS server_cert.der ca.crt)
   add_executable(server_certificate server_certificate.c ${STATIC_OBJECTS} server_cert.der ca.crt)
   target_link_libraries(server_certificate open62541 ${open62541_LIBRARIES})

+ 16 - 8
examples/server_nodeset.xml

@@ -10,6 +10,22 @@
         <Alias Alias="HasSubtype">i=45</Alias>
         <Alias Alias="HasComponent">i=47</Alias>
     </Aliases>
+    <!-- Following object has only references to nodes defined after itself -->
+    <UAObject NodeId="ns=1;i=5001" BrowseName="1:testInstance">
+        <DisplayName>testInstance</DisplayName>
+        <References>
+            <Reference ReferenceType="Organizes" IsForward="false">ns=1;i=5002</Reference>
+            <Reference ReferenceType="HasTypeDefinition">ns=1;i=1001</Reference>
+            <Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
+        </References>
+    </UAObject>
+    <UAObject NodeId="ns=1;i=5002" BrowseName="1:testFolder">
+        <DisplayName>testFolder</DisplayName>
+        <References>
+            <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
+            <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
+        </References>
+    </UAObject>
     <UAObjectType NodeId="ns=1;i=1001" BrowseName="1:testType">
         <DisplayName>testType</DisplayName>
         <References>
@@ -25,14 +41,6 @@
             <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
         </References>
     </UAVariable>
-    <UAObject NodeId="ns=1;i=5001" BrowseName="1:testInstance">
-        <DisplayName>testInstance</DisplayName>
-        <References>
-            <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
-            <Reference ReferenceType="HasTypeDefinition">ns=1;i=1001</Reference>
-            <Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
-        </References>
-    </UAObject>
     <UAVariable DataType="Double" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="1:Var1" UserAccessLevel="3" AccessLevel="3">
         <DisplayName>Var1</DisplayName>
         <References>

+ 5 - 2
tools/pyUANamespace/generate_open62541CCode.py

@@ -63,7 +63,10 @@ parser.add_argument('-s','--suppress',
                     choices=['description', 'browseName', 'displayName', 'writeMask', 'userWriteMask','nodeid'],
                     default=[],
                     help="Suppresses the generation of some node attributes. Currently supported options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask' and 'nodeid'.")
-
+parser.add_argument('--high-level-api',
+                    action='store_true', default=False,
+                    dest='high_level_api',
+                    help="Use only high level API which makes it possible to add nodes in userspace")
 parser.add_argument('-v','--verbose', action='count', help='Make the script more verbose. Can be applied up to 4 times')
 
 args = parser.parse_args()
@@ -171,7 +174,7 @@ for ignore in args.ignoreFiles:
 logger.info("Generating Header")
 # Returns a tuple of (["Header","lines"],["Code","lines","generated"])
 from os.path import basename
-generatedCode = ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile))
+generatedCode = ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile), high_level_api=args.high_level_api)
 for line in generatedCode[0]:
   outfileh.write(line+"\n")
 for line in generatedCode[1]:

+ 19 - 24
tools/pyUANamespace/open62541_MacroHelper.py

@@ -37,9 +37,9 @@ class open62541_MacroHelper():
 
   def getCreateExpandedNodeIDMacro(self, node):
     if node.id().i != None:
-      return "UA_EXPANDEDNODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
+      return "UA_EXPANDEDNODEID_NUMERIC(%s, %s)" % (node.id().ns, node.id().i)
     elif node.id().s != None:
-      return "UA_EXPANDEDNODEID_STRING("  + str(node.id().ns) + ", " + node.id().s + ")"
+      return "UA_EXPANDEDNODEID_STRING(%s, %s)" % (node.id().ns, node.id().s)
     elif node.id().b != None:
       logger.debug("NodeID Generation macro for bytestrings has not been implemented.")
       return ""
@@ -65,7 +65,6 @@ class open62541_MacroHelper():
     return input
 
   def getNodeIdDefineString(self, node):
-    code = []
     extrNs = node.browseName().split(":")
     symbolic_name = ""
     # strip all characters that would be illegal in C-Code
@@ -76,7 +75,7 @@ class open62541_MacroHelper():
 
     symbolic_name = self.substitutePunctuationCharacters(nodename)
     if symbolic_name != nodename :
-        logger.warn("Subsituted characters in browsename for nodeid " + str(node.id().i) + " while generating C-Code ")
+        logger.warn("Substituted characters in browsename for nodeid " + str(node.id().i) + " while generating C-Code ")
 
     if symbolic_name in defined_typealiases:
       logger.warn(self, "Typealias definition of " + str(node.id().i) + " is non unique!")
@@ -87,15 +86,13 @@ class open62541_MacroHelper():
       symbolic_name = symbolic_name+"_"+str(extendedN)
 
     defined_typealiases.append(symbolic_name)
-
-    code.append("#define UA_NS"  + str(node.id().ns) + "ID_" + symbolic_name.upper() + " " + str(node.id().i))
-    return code
+    return "#define UA_NS%sID_%s %s" % (node.id().ns, symbolic_name.upper(), node.id().i)
 
   def getCreateNodeIDMacro(self, node):
     if node.id().i != None:
-      return "UA_NODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
+      return "UA_NODEID_NUMERIC(%s, %s)" % (node.id().ns, node.id().i)
     elif node.id().s != None:
-      return "UA_NODEID_STRING("  + str(node.id().ns) + ", " + node.id().s + ")"
+      return "UA_NODEID_STRING(%s, %s)" % (node.id().ns, node.id().s)
     elif node.id().b != None:
       logger.debug("NodeID Generation macro for bytestrings has not been implemented.")
       return ""
@@ -109,14 +106,12 @@ class open62541_MacroHelper():
     code = []
 
     if reference.isForward():
-      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", true);")
-    else:
-      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", false);")
+      code.append("UA_Server_addReference(server, %s, %s, %s, true);" % (self.getCreateNodeIDMacro(sourcenode), self.getCreateNodeIDMacro(reference.referenceType()), self.getCreateExpandedNodeIDMacro(reference.target())))
     return code
 
-  def getCreateNodeNoBootstrap(self, node, parentNode, parentReference, unprintedNodes):
+  def getCreateNodeNoBootstrap(self, node, parentNode, parentReference, unprintedNodes=[]):
     code = []
-    code.append("// Node: " + str(node) + ", " + str(node.browseName()))
+    code.append("// Node: %s, %s" % (str(node), str(node.browseName())))
 
     if node.nodeClass() == NODE_CLASS_OBJECT:
       nodetype = "Object"
@@ -208,16 +203,16 @@ class open62541_MacroHelper():
       code.append("attr.userAccessLevel = %s;" % str(node.userAccessLevel()))
     if nodetype in ["Variable", "VariableType"]:
       code.append("attr.valueRank = %s;"       % str(node.valueRank()))
-      
+
     if nodetype in ["Variable", "VariableType"]:
-      code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
+      code.extend(node.printOpen62541CCode_SubtypeEarly(bootstrapping = False))
     elif nodetype == "Method":
       if node.executable():
         code.append("attr.executable = true;")
       if node.userExecutable():
         code.append("attr.userExecutable = true;")
 
-    code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
+    code.append("UA_NodeId nodeId = %s;" % str(self.getCreateNodeIDMacro(node)))
     if nodetype in ["Object", "Variable", "VariableType"]:
       typeDefinition = None
       for r in node.getReferences():
@@ -228,13 +223,13 @@ class open62541_MacroHelper():
         code.append("UA_NodeId typeDefinition = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE);")
       else:
         code.append("UA_NodeId typeDefinition = " + str(self.getCreateNodeIDMacro(typeDefinition)) + ";")
-    code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
-    code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
+    code.append("UA_NodeId parentNodeId = %s;" % str(self.getCreateNodeIDMacro(parentNode)))
+    code.append("UA_NodeId parentReferenceNodeId = %s;" % str(self.getCreateNodeIDMacro(parentReference.referenceType())))
     extrNs = node.browseName().split(":")
     if len(extrNs) > 1:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(" +  str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
+      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(%s, \"%s\");" % (str(extrNs[0]), extrNs[1]))
     else:
-      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"" + str(node.browseName()) + "\");")
+      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"%s\");" % str(node.browseName()))
 
     # In case of a MethodNode: Add in|outArg struct generation here. Mandates that namespace reordering was done using
     # Djikstra (check that arguments have not been printed). (@ichrispa)
@@ -245,9 +240,9 @@ class open62541_MacroHelper():
     if nodetype != "Method":
       code.append("       , attr, NULL, NULL);")
     else:
-      code.append("       , attr, (UA_MethodCallback) NULL, " + str(len(inArgVal)) + ", inputArguments,  " + str(len(outArgVal)) + ", outputArguments, NULL, NULL);")
-      
-    #Adding a Node with typeDefinition = UA_NODEID_NULL will create a HasTypeDefinition reference to BaseDataType - remove it since 
+      code.append("       , attr, (UA_MethodCallback) NULL, NULL, %s, inputArguments,  %s, outputArguments, NULL);" % (str(len(inArgVal)), str(len(outArgVal))))
+
+    #Adding a Node with typeDefinition = UA_NODEID_NULL will create a HasTypeDefinition reference to BaseDataType - remove it since
     #a real Reference will be add in a later step (a single HasTypeDefinition reference is assumed here)
     #The current API does not let us specify IDs of Object's subelements.
     if nodetype is "Object":

+ 2 - 5
tools/pyUANamespace/open62541_XMLPreprocessor.py

@@ -265,7 +265,7 @@ class open62541_XMLPreprocessor:
   def testModelCongruencyAgainstReferences(self, doc, refs):
     """ testModelCongruencyAgainstReferences
 
-        Counts how many of the nodes referencef in refs can be found in the model
+        Counts how many of the nodes referenced in refs can be found in the model
         doc.
 
         returns: double corresponding to the percentage of hits
@@ -277,7 +277,6 @@ class open62541_XMLPreprocessor:
     for ref in refs:
       for n in doc.containedNodes:
         if str(ref) == str(n[0]):
-          print(ref, n[0])
           found = found + 1
           break
     return float(found)/float(sspace)
@@ -361,16 +360,14 @@ class open62541_XMLPreprocessor:
           r.toString()
         # ... how many of them would be found!?
         c = self.testModelCongruencyAgainstReferences(tDoc, refs)
-        print(c)
         if c>0:
           matches.append((c, tDoc))
       best = (0, None)
       for m in matches:
-        print(m[0])
         if m[0] > best[0]:
           best = m
       if best[1] != None:
-        logger.warn("Best match (" + str(best[1]*100) + "%) for what " + os.path.basename(doc.originXML) + " refers to as ns="+str(d)+" was " + os.path.basename(best[1].originXML))
+        logger.warn("Best match (%d) for what %s refers to as ns=%s was %s", best[1], os.path.basename(doc.originXML), d, os.path.basename(best[1].originXML))
         doc.reassignReferencedNamespaceId(d, best[1].getNamespaceId())
       else:
         logger.error("Failed to find a match for what " +  os.path.basename(doc.originXML) + " refers to as ns=" + str(d))

+ 27 - 27
tools/pyUANamespace/ua_builtin_types.py

@@ -54,7 +54,7 @@ class opcua_value_t():
     self.value = None
     self.parent = parent
     self.stringRepresentation = ""
-    self.setStringReprentation()
+    self.setStringRepresentation()
     self.__binTypeId__ = 0
     self.setNumericRepresentation()
     self.__alias__ = None
@@ -313,7 +313,7 @@ class opcua_value_t():
         ebodypart = getNextElementNode(ebodypart)
       return extobj
 
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     pass
 
   def setNumericRepresentation(self):
@@ -368,7 +368,7 @@ class opcua_value_t():
         if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
           for v in self.value:
             logger.debug("Building extObj array index " + str(self.value.index(v)))
-            code = code + v.printOpen62541CCode_SubType_build(arrayIndex=self.value.index(v))
+            code.extend(v.printOpen62541CCode_SubType_build(arrayIndex=self.value.index(v)))
         #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
         code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + "[" + str(len(self.value)) + "];")
         if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
@@ -394,7 +394,7 @@ class opcua_value_t():
       else:
         # The following strategy applies to all other types, in particular strings and numerics.
         if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
-          code = code + self.value[0].printOpen62541CCode_SubType_build()
+          code.extend(self.value[0].printOpen62541CCode_SubType_build())
         #code.append("attr.value.type = &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "];")
         if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
           code.append("UA_" + self.value[0].stringRepresentation + " *" + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
@@ -420,7 +420,7 @@ class opcua_value_t():
 ###
 
 class opcua_BuiltinType_extensionObject_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "ExtensionObject"
     self.__typeId__ = None
 
@@ -539,7 +539,7 @@ class opcua_BuiltinType_extensionObject_t(opcua_value_t):
     return "'" + self.alias() + "':" + self.stringRepresentation + "(" + str(self.value) + ")"
 
 class opcua_BuiltinType_localizedtext_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "LocalizedText"
 
   def setNumericRepresentation(self):
@@ -606,7 +606,7 @@ class opcua_BuiltinType_localizedtext_t(opcua_value_t):
       return code
 
 class opcua_BuiltinType_expandednodeid_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "ExpandedNodeId"
 
   def setNumericRepresentation(self):
@@ -625,7 +625,7 @@ class opcua_BuiltinType_expandednodeid_t(opcua_value_t):
     return code
 
 class opcua_BuiltinType_nodeid_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "NodeId"
 
   def setNumericRepresentation(self):
@@ -677,7 +677,7 @@ class opcua_BuiltinType_nodeid_t(opcua_value_t):
     return "UA_NODEID_NUMERIC(0,0)"
 
 class opcua_BuiltinType_datetime_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "DateTime"
 
   def setNumericRepresentation(self):
@@ -718,7 +718,7 @@ class opcua_BuiltinType_datetime_t(opcua_value_t):
         self.value = strptime(strftime("%Y-%m-%dT%H:%M%S"), "%Y-%m-%dT%H:%M%S")
 
 class opcua_BuiltinType_qualifiedname_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "QualifiedName"
 
   def setNumericRepresentation(self):
@@ -764,7 +764,7 @@ class opcua_BuiltinType_qualifiedname_t(opcua_value_t):
       return code
 
 class opcua_BuiltinType_statuscode_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "StatusCode"
 
   def setNumericRepresentation(self):
@@ -777,7 +777,7 @@ class opcua_BuiltinType_statuscode_t(opcua_value_t):
     logger.warn("Not implemented")
 
 class opcua_BuiltinType_diagnosticinfo_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "StatusCode"
 
   def setNumericRepresentation(self):
@@ -790,7 +790,7 @@ class opcua_BuiltinType_diagnosticinfo_t(opcua_value_t):
     logger.warn("Not implemented")
 
 class opcua_BuiltinType_guid_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Guid"
 
   def setNumericRepresentation(self):
@@ -833,7 +833,7 @@ class opcua_BuiltinType_guid_t(opcua_value_t):
       self.value = tmp
 
 class opcua_BuiltinType_boolean_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Boolean"
 
   def setNumericRepresentation(self):
@@ -867,7 +867,7 @@ class opcua_BuiltinType_boolean_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_byte_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Byte"
 
   def setNumericRepresentation(self):
@@ -901,7 +901,7 @@ class opcua_BuiltinType_byte_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_sbyte_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "SByte"
 
   def setNumericRepresentation(self):
@@ -935,7 +935,7 @@ class opcua_BuiltinType_sbyte_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int16_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Int16"
 
   def setNumericRepresentation(self):
@@ -969,7 +969,7 @@ class opcua_BuiltinType_int16_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint16_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "UInt16"
 
   def setNumericRepresentation(self):
@@ -1003,7 +1003,7 @@ class opcua_BuiltinType_uint16_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int32_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Int32"
 
   def setNumericRepresentation(self):
@@ -1037,7 +1037,7 @@ class opcua_BuiltinType_int32_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint32_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "UInt32"
 
   def setNumericRepresentation(self):
@@ -1071,7 +1071,7 @@ class opcua_BuiltinType_uint32_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_int64_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Int64"
 
   def setNumericRepresentation(self):
@@ -1101,7 +1101,7 @@ class opcua_BuiltinType_int64_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_uint64_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "UInt64"
 
   def setNumericRepresentation(self):
@@ -1135,7 +1135,7 @@ class opcua_BuiltinType_uint64_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_float_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Float"
 
   def setNumericRepresentation(self):
@@ -1169,7 +1169,7 @@ class opcua_BuiltinType_float_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_double_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "Double"
 
   def setNumericRepresentation(self):
@@ -1203,7 +1203,7 @@ class opcua_BuiltinType_double_t(opcua_value_t):
     return "(UA_" + self.stringRepresentation + ") " + str(self.value)
 
 class opcua_BuiltinType_string_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "String"
 
   def setNumericRepresentation(self):
@@ -1240,7 +1240,7 @@ class opcua_BuiltinType_string_t(opcua_value_t):
       return code
 
 class opcua_BuiltinType_xmlelement_t(opcua_BuiltinType_string_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "XmlElement"
 
   def setNumericRepresentation(self):
@@ -1251,7 +1251,7 @@ class opcua_BuiltinType_xmlelement_t(opcua_BuiltinType_string_t):
       return code
 
 class opcua_BuiltinType_bytestring_t(opcua_value_t):
-  def setStringReprentation(self):
+  def setStringRepresentation(self):
     self.stringRepresentation = "ByteString"
 
   def setNumericRepresentation(self):

+ 66 - 221
tools/pyUANamespace/ua_namespace.py

@@ -139,31 +139,13 @@ class opcua_namespace():
   def getNodeByBrowseName(self, idstring):
     """ Returns the first node in the nodelist whose browseName matches idstring.
     """
-    matches = []
-    for n in self.nodes:
-      if idstring==str(n.browseName()):
-        matches.append(n)
-    if len(matches) > 1:
-      logger.error("Found multiple nodes with same ID!?")
-    if len(matches) == 0:
-      return None
-    else:
-      return matches[0]
+    return next((n for n in self.nodes if idstring==str(n.browseName())), None)
 
   def getNodeByIDString(self, idstring):
     """ Returns the first node in the nodelist whose id string representation
         matches idstring.
     """
-    matches = []
-    for n in self.nodes:
-      if idstring==str(n.id()):
-        matches.append(n)
-    if len(matches) > 1:
-      logger.error("Found multiple nodes with same ID!?")
-    if len(matches) == 0:
-      return None
-    else:
-      return matches[0]
+    return next((n for n in self.nodes if idstring==str(n.id())), None)
 
   def createNode(self, ndtype, xmlelement):
     """ createNode is instantiates a node described by xmlelement, its type being
@@ -437,22 +419,6 @@ class opcua_namespace():
       if isinstance(n, opcua_node_variable_t):
         n.allocateValue()
 
-  def printDot(self, filename="namespace.dot"):
-    """ Outputs a graphiz/dot description of all nodes in the namespace.
-
-        Output will written into filename to be parsed by dot/neato...
-
-        Note that for namespaces with more then 20 nodes the reference structure
-        will lead to a mostly illegible and huge graph. Use printDotGraphWalk()
-        for plotting specific portions of a large namespace.
-    """
-    file=open(filename, 'w+')
-
-    file.write("digraph ns {\n")
-    for n in self.nodes:
-      file.write(n.printDot())
-    file.write("}\n")
-    file.close()
 
   def getSubTypesOf(self, tdNodes = None, currentNode = None, hasSubtypeRefNode = None):
     # If this is a toplevel call, collect the following information as defaults
@@ -476,153 +442,12 @@ class opcua_namespace():
 
     return tdNodes
 
-
-  def printDotGraphWalk(self, depth=1, filename="out.dot", rootNode=None, followInverse = False, excludeNodeIds=[]):
-    """ Outputs a graphiz/dot description the nodes centered around rootNode.
-
-        References beginning from rootNode will be followed for depth steps. If
-        "followInverse = True" is passed, then inverse (not Forward) references
-        will also be followed.
-
-        Nodes can be excluded from the graph by passing a list of NodeIds as
-        string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
-
-        Output is written into filename to be parsed by dot/neato/srfp...
-    """
-    iter = depth
-    processed = []
-    if rootNode == None or \
-       not isinstance(rootNode, opcua_node_t) or \
-       not rootNode in self.nodes:
-      root = self.getRoot()
-    else:
-      root = rootNode
-
-    file=open(filename, 'w+')
-
-    if root == None:
-      return
-
-    file.write("digraph ns {\n")
-    file.write(root.printDot())
-    refs=[]
-    if followInverse == True:
-      refs = root.getReferences(); # + root.getInverseReferences()
-    else:
-      for ref in root.getReferences():
-        if ref.isForward():
-          refs.append(ref)
-    while iter > 0:
-      tmp = []
-      for ref in refs:
-        if isinstance(ref.target(), opcua_node_t):
-          tgt = ref.target()
-          if not str(tgt.id()) in excludeNodeIds:
-            if not tgt in processed:
-              file.write(tgt.printDot())
-              processed.append(tgt)
-              if ref.isForward() == False and followInverse == True:
-                tmp = tmp + tgt.getReferences(); # + tgt.getInverseReferences()
-              elif ref.isForward() == True :
-                tmp = tmp + tgt.getReferences();
-      refs = tmp
-      iter = iter - 1
-
-    file.write("}\n")
-    file.close()
-
-  def __reorder_getMinWeightNode__(self, nmatrix):
-    rcind = -1
-    rind = -1
-    minweight = -1
-    minweightnd = None
-    for row in nmatrix:
-      rcind += 1
-      if row[0] == None:
-        continue
-      w = sum(row[1:])
-      if minweight < 0:
-        rind = rcind
-        minweight = w
-        minweightnd = row[0]
-      elif w < minweight:
-        rind = rcind
-        minweight = w
-        minweightnd = row[0]
-    return (rind, minweightnd, minweight)
-
-  def reorderNodesMinDependencies(self):
-    # create a matrix represtantion of all node
-    #
-    nmatrix = []
-    for n in range(0,len(self.nodes)):
-      nmatrix.append([None] + [0]*len(self.nodes))
-
-    typeRefs = []
-    tn = self.getNodeByBrowseName("HasTypeDefinition")
-    if tn != None:
-      typeRefs.append(tn)
-      typeRefs = typeRefs + self.getSubTypesOf(currentNode=tn)
-    subTypeRefs = []
-    tn = self.getNodeByBrowseName("HasSubtype")
-    if tn  != None:
-      subTypeRefs.append(tn)
-      subTypeRefs = subTypeRefs + self.getSubTypesOf(currentNode=tn)
-
-    logger.debug("Building connectivity matrix for node order optimization.")
-    # Set column 0 to contain the node
-    for node in self.nodes:
-      nind = self.nodes.index(node)
-      nmatrix[nind][0] = node
-
-    # Determine the dependencies of all nodes
-    logger.debug("Determining node interdependencies.")
-    for node in self.nodes:
-      nind = self.nodes.index(node)
-      #print "Examining node " + str(nind) + " " + str(node)
-      for ref in node.getReferences():
-        if isinstance(ref.target(), opcua_node_t):
-          tind = self.nodes.index(ref.target())
-          # Typedefinition of this node has precedence over this node
-          if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] += 200 # Very big weight for typedefs
-          # isSubTypeOf/typeDefinition of this node has precedence over this node
-          elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] += 100 # Big weight for subtypes
-          # Else the target depends on us
-          elif ref.isForward():
-            nmatrix[tind][nind+1] += 1 # regular weight for dependencies
-
-    logger.debug("Using Djikstra topological sorting to determine printing order.")
-    reorder = []
-    while len(reorder) < len(self.nodes):
-      (nind, node, w) = self.__reorder_getMinWeightNode__(nmatrix)
-      #print  str(100*float(len(reorder))/len(self.nodes)) + "% " + str(w) + " " + str(node) + " " + str(node.browseName())
-      reorder.append(node)
-      for ref in node.getReferences():
-        if isinstance(ref.target(), opcua_node_t):
-          tind = self.nodes.index(ref.target())
-          if ref.referenceType() in typeRefs and ref.isForward():
-            nmatrix[nind][tind+1] -= 200
-          elif ref.referenceType() in subTypeRefs and not ref.isForward():
-            nmatrix[nind][tind+1] -= 100
-          elif ref.isForward():
-            nmatrix[tind][nind+1] -= 1
-      nmatrix[nind][0] = None
-    self.nodes = reorder
-    logger.debug("Nodes reordered.")
-    return
-
-  def printOpen62541Header(self, printedExternally=[], supressGenerationOfAttribute=[], outfilename=""):
+  def printOpen62541Header(self, printedExternally=[], supressGenerationOfAttribute=[], outfilename="", high_level_api=False):
     unPrintedNodes = []
     unPrintedRefs  = []
     code = []
     header = []
 
-    # Reorder our nodes to produce a bare minimum of bootstrapping dependencies
-    logger.debug("Reordering nodes for minimal dependencies during printing.")
-    self.reorderNodesMinDependencies()
-
     # Some macros (UA_EXPANDEDNODEID_MACRO()...) are easily created, but
     # bulky. This class will help to offload some code.
     codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
@@ -640,7 +465,7 @@ class opcua_namespace():
         if (r.target() != None) and (r.target().id() != None) and (r.parent() != None):
           unPrintedRefs.append(r)
 
-    logger.debug(str(len(unPrintedNodes)) + " Nodes, " + str(len(unPrintedRefs)) +  "References need to get printed.")
+    logger.debug("%d nodes and %d references need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
     header.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
     code.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
 
@@ -686,56 +511,79 @@ class opcua_namespace():
       if n.id().ns != 0:
         nc = n.nodeClass()
         if nc != NODE_CLASS_OBJECT and nc != NODE_CLASS_VARIABLE and nc != NODE_CLASS_VIEW:
-          header = header + codegen.getNodeIdDefineString(n)
+          header.append(codegen.getNodeIdDefineString(n))
 
       # Now for the actual references...
       for r in n.getReferences():
         # Only print valid references in namespace 0 (users will not want their refs bootstrapped)
         if not r.referenceType() in refsUsed and r.referenceType() != None and r.referenceType().id().ns == 0:
           refsUsed.append(r.referenceType())
-    logger.debug(str(len(refsUsed)) + " reference types are used in the namespace, which will now get bootstrapped.")
+    logger.debug("%d reference types are used in the namespace, which will now get bootstrapped.", len(refsUsed))
     for r in refsUsed:
-      code = code + r.printOpen62541CCode(unPrintedNodes, unPrintedRefs);
-
-    header.append("extern UA_StatusCode "+outfilename+"(UA_Server *server);\n")
-    header.append("#endif /* "+outfilename.upper()+"_H_ */")
+      code.extend(r.printOpen62541CCode(unPrintedNodes, unPrintedRefs))
 
-    # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
-    #               Nodes remove themselves from this list when printed.
-    logger.debug("Printing all other nodes.")
-    for n in self.nodes:
-      code = code + n.printOpen62541CCode(unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute=supressGenerationOfAttribute)
+    logger.debug("%d Nodes, %d References need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
 
-    if len(unPrintedNodes) != 0:
-      logger.warn("" + str(len(unPrintedNodes)) + " nodes could not be translated to code.")
-    else:
-      logger.debug("Printing suceeded for all nodes")
-
-    if len(unPrintedRefs) != 0:
-      logger.debug("Attempting to print " + str(len(unPrintedRefs)) + " unprinted references.")
-      tmprefs = []
-      for r in unPrintedRefs:
-        if  not (r.target() not in unPrintedNodes) and not (r.parent() in unPrintedNodes):
-          if not isinstance(r.parent(), opcua_node_t):
-            logger.debug("Reference has no parent!")
-          elif not isinstance(r.parent().id(), opcua_node_id_t):
-            logger.debug("Parents nodeid is not a nodeID!")
-          else:
-            if (len(tmprefs) == 0):
-              code.append("//  Creating leftover references:")
-            code = code + codegen.getCreateStandaloneReference(r.parent(), r)
-            code.append("")
-            tmprefs.append(r)
-      # Remove printed refs from list
-      for r in tmprefs:
-        unPrintedRefs.remove(r)
-      if len(unPrintedRefs) != 0:
-        logger.warn("" + str(len(unPrintedRefs)) + " references could not be translated to code.")
-    else:
-      logger.debug("Printing succeeded for all references")
+    if not high_level_api:
+        # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
+        #               Nodes remove themselves from this list when printed.
+        logger.debug("Printing all other nodes.")
+        for n in self.nodes:
+          code.extend(n.printOpen62541CCode(unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute=supressGenerationOfAttribute))
 
+        if len(unPrintedNodes) != 0:
+          logger.warn("%d nodes could not be translated to code.", len(unPrintedNodes))
+        else:
+          logger.debug("Printing suceeded for all nodes")
+
+        if len(unPrintedRefs) != 0:
+          logger.debug("Attempting to print " + str(len(unPrintedRefs)) + " unprinted references.")
+          tmprefs = []
+          for r in unPrintedRefs:
+            if  not (r.target() not in unPrintedNodes) and not (r.parent() in unPrintedNodes):
+              if not isinstance(r.parent(), opcua_node_t):
+                logger.debug("Reference has no parent!")
+              elif not isinstance(r.parent().id(), opcua_node_id_t):
+                logger.debug("Parents nodeid is not a nodeID!")
+              else:
+                if (len(tmprefs) == 0):
+                  code.append("//  Creating leftover references:")
+                code.extend(codegen.getCreateStandaloneReference(r.parent(), r))
+                code.append("")
+                tmprefs.append(r)
+          # Remove printed refs from list
+          for r in tmprefs:
+            unPrintedRefs.remove(r)
+          if len(unPrintedRefs) != 0:
+            logger.warn("" + str(len(unPrintedRefs)) + " references could not be translated to code.")
+        else:
+          logger.debug("Printing succeeded for all references")
+    else:  # Using only High Level API
+        already_printed = list(printedExternally)
+        while unPrintedNodes:
+            node_found = False
+            for node in unPrintedNodes:
+                for ref in node.getReferences():
+                    if ref.referenceType() in already_printed and ref.target() in already_printed:
+                        node_found = True
+                        code.extend(node.printOpen62541CCode_HL_API(ref, supressGenerationOfAttribute))
+                        unPrintedRefs.remove(ref)
+                        unPrintedNodes.remove(node)
+                        already_printed.append(node)
+                        break
+            if not node_found:
+                logger.critical("no complete code generation with high level API possible; not all nodes will be created")
+                code.append("CRITICAL: no complete code generation with high level API possible; not all nodes will be created")
+                break
+        code.append("// creating references")
+        for r in unPrintedRefs:
+            code.extend(codegen.getCreateStandaloneReference(r.parent(), r))
+
+    # finalizing source and header
+    header.append("extern void "+outfilename+"(UA_Server *server);\n")
+    header.append("#endif /* "+outfilename.upper()+"_H_ */")
     code.append("return UA_STATUSCODE_GOOD;")
-    code.append("}")
+    code.append("} // closing nodeset()")
     return (header,code)
 
 ###
@@ -787,9 +635,6 @@ class testing:
         ns.append(n)
       print("...done, " + str(len(ns)) + " nodes discovered")
 
-    logger.debug("Phase 5: Printing pretty graph")
-    self.namespace.printDotGraphWalk(depth=1, rootNode=self.namespace.getNodeByIDString("i=84"), followInverse=False, excludeNodeIds=["i=29", "i=22", "i=25"])
-    #self.namespace.printDot()
 
 class testing_open62541_header:
   def __init__(self):

+ 30 - 5
tools/pyUANamespace/ua_node_types.py

@@ -655,6 +655,31 @@ class opcua_node_t:
     """
     return []
 
+  def printOpen62541CCode_HL_API(self, reference, supressGenerationOfAttribute=[]):
+    """ printOpen62541CCode_HL_API
+
+        Returns a list of strings containing the C-code necessary to intialize
+        this node for the open62541 OPC-UA Stack using only the high level API
+
+        Note that this function will fail if the nodeid is non-numeric, as
+        there is no UA_EXPANDEDNNODEID_[STRING|GUID|BYTESTRING] macro.
+    """
+    codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
+    code = []
+    code.append("")
+    code.append("do {")
+    # If we are being passed a parent node by the namespace, use that for registering ourselves in the namespace
+    # Note: getFirstParentNode will return [parentNode, referenceToChild]
+    parentNode = reference.target()
+    parentRefType = reference.referenceType()
+    code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" +
+                  str(parentNode .__node_browseName__) + " using " + str(parentRefType.id()) +
+                  "/" + str(parentRefType.__node_browseName__))
+    code.extend(codegen.getCreateNodeNoBootstrap(self, parentNode, reference))
+    code.append("} while(0);")
+    return code
+
+
   def printOpen62541CCode(self, unPrintedNodes=[], unPrintedReferences=[], supressGenerationOfAttribute=[]):
     """ printOpen62541CCode
 
@@ -681,7 +706,7 @@ class opcua_node_t:
       code.append("// Referencing node found and declared as parent: " + str(parentNode .id()) + "/" +
                   str(parentNode .__node_browseName__) + " using " + str(parentRef.referenceType().id()) +
                   "/" + str(parentRef.referenceType().__node_browseName__))
-      code = code + codegen.getCreateNodeNoBootstrap(self, parentNode, parentRef, unPrintedNodes)
+      code.extend(codegen.getCreateNodeNoBootstrap(self, parentNode, parentRef, unPrintedNodes))
       # Parent to child reference is added by the server, do not reprint that reference
       if parentRef in unPrintedReferences:
         unPrintedReferences.remove(parentRef)
@@ -705,7 +730,7 @@ class opcua_node_t:
         if r in unPrintedReferences:
           if (len(tmprefs) == 0):
             code.append("// This node has the following references that can be created:")
-          code = code + codegen.getCreateStandaloneReference(self, r)
+          code.extend(codegen.getCreateStandaloneReference(self, r))
           tmprefs.append(r)
     # Remove printed refs from list
     for r in tmprefs:
@@ -724,7 +749,7 @@ class opcua_node_t:
         else:
           if (len(tmprefs) == 0):
             code.append("//  Creating this node has resolved the following open references:")
-          code = code + codegen.getCreateStandaloneReference(r.parent(), r)
+          code.extend(codegen.getCreateStandaloneReference(r.parent(), r))
           tmprefs.append(r)
     # Remove printed refs from list
     for r in tmprefs:
@@ -1075,7 +1100,7 @@ class opcua_node_variable_t(opcua_node_t):
       # determined a valid encoding
       if self.dataType().target().isEncodable():
         if self.value() != None:
-          code = code + self.value().printOpen62541CCode(bootstrapping)
+          code.extend(self.value().printOpen62541CCode(bootstrapping))
           return code
     if(bootstrapping):
       code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = (UA_Variant*)UA_alloca(sizeof(UA_Variant));")
@@ -1329,7 +1354,7 @@ class opcua_node_variableType_t(opcua_node_t):
       # determined a valid encoding
       if self.dataType().target().isEncodable():
         if self.value() != None:
-          code = code + self.value().printOpen62541CCode(bootstrapping)
+          code.extend(self.value().printOpen62541CCode(bootstrapping))
           return code
     if(bootstrapping):
       code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = (UA_Variant*)UA_alloca(sizeof(UA_Variant));")