Przeglądaj źródła

Namespace compiler was rewritten to use userland insertion methods whenever possible. Some adaptations to several add<Type>Node calls where made to facilitate the compiler.

FIXME's left:
  * Node reordering
  * Missing node attributes not part of add<Type>Node calls
  * Variable nodes need to have there variant determination sequence reexamined
ichrispa 9 lat temu
rodzic
commit
17ec7526dd

+ 4 - 2
examples/server.c

@@ -344,8 +344,10 @@ int main(int argc, char** argv) {
   outputArguments.valueRank = -1;
 
   UA_NodeId methodId; // Retrieve the actual ID if this node if a random id as in UA_NODEID_NUMERIC(1,0) is used
-  UA_Server_addMethodNode(server, UA_QUALIFIEDNAME(1,"ping"), UA_NODEID_NUMERIC(1,62541),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+  UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541), UA_QUALIFIEDNAME(1,"ping"), UA_LOCALIZEDTEXT("en_US", "ping"),
+                          UA_LOCALIZEDTEXT("en_US", "Return a single argument as passed by the caller"),
+                          UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                          0,0,
                           &getMonitoredItems, 1, &inputArguments, 1, &outputArguments, &methodId);
 #endif
    

+ 4 - 3
examples/server_method.c

@@ -65,9 +65,10 @@ int main(int argc, char** argv) {
     outputArguments.name = UA_STRING("MyOutput");
     outputArguments.valueRank = -1;
         
-    UA_Server_addMethodNode(server, UA_QUALIFIEDNAME(1, "hello world"), UA_NODEID_NUMERIC(1,62541),
-                            UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                            &helloWorldMethod, 1, &inputArguments, 1, &outputArguments, NULL);
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541), UA_QUALIFIEDNAME(1, "hello world"), 
+                            UA_LOCALIZEDTEXT("en_US","Hello World"), UA_LOCALIZEDTEXT("en_US","Say `Hello World`"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            0, 0, &helloWorldMethod, 1, &inputArguments, 1, &outputArguments, NULL);
 
     /* start server */
     UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false

+ 7 - 5
include/ua_server.h

@@ -200,12 +200,14 @@ UA_Server_addVariableNode(UA_Server *server, UA_NodeId nodeId, const UA_Qualifie
                           const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
                           UA_Variant *value, UA_NodeId *createdNodeId);
 
+// Missing: eventNotifier
 UA_StatusCode UA_EXPORT
 UA_Server_addObjectNode(UA_Server *server, UA_NodeId nodeId, const UA_QualifiedName browseName, 
                         UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
                         const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
                         const UA_ExpandedNodeId typeDefinition, UA_NodeId *createdNodeId);
 
+// Missing: isAbstract, symmetric
 UA_StatusCode UA_EXPORT 
 UA_Server_addReferenceTypeNode(UA_Server *server, UA_NodeId nodeId, UA_QualifiedName browseName, 
                                UA_LocalizedText displayName, UA_LocalizedText description, UA_NodeId parentNodeId, 
@@ -278,11 +280,11 @@ typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Va
  * 
  */
 UA_StatusCode UA_EXPORT
-UA_Server_addMethodNode(UA_Server *server, const UA_QualifiedName browseName, UA_NodeId nodeId,
-                        const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId,
-                        UA_MethodCallback method, UA_Int32 inputArgumentsSize,
-                        const UA_Argument *inputArguments, UA_Int32 outputArgumentsSize,
-                        const UA_Argument *outputArguments,
+UA_Server_addMethodNode(UA_Server *server, UA_NodeId nodeId, const UA_QualifiedName browseName,
+                        UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
+                        const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
+                        UA_MethodCallback method, UA_Int32 inputArgumentsSize, const UA_Argument *inputArguments, 
+                        UA_Int32 outputArgumentsSize, const UA_Argument *outputArguments,
                         UA_NodeId *createdNodeId);
 #endif
 

+ 26 - 11
src/server/ua_server_addressspace.c

@@ -762,24 +762,32 @@ UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *no
 
 #ifdef ENABLE_METHODCALLS
 UA_StatusCode
-UA_Server_addMethodNode(UA_Server *server, const UA_QualifiedName browseName, UA_NodeId nodeId,
-                        const UA_ExpandedNodeId parentNodeId, const UA_NodeId referenceTypeId,
-                        UA_MethodCallback method, UA_Int32 inputArgumentsSize,
-                        const UA_Argument *inputArguments, UA_Int32 outputArgumentsSize,
-                        const UA_Argument *outputArguments,
-                        UA_NodeId *createdNodeId) {
+UA_Server_addMethodNode(UA_Server* server, UA_NodeId nodeId, const UA_QualifiedName browseName, 
+                        UA_LocalizedText displayName, UA_LocalizedText description, const UA_NodeId parentNodeId, 
+                        const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
+                        UA_MethodCallback method, UA_Int32 inputArgumentsSize, const UA_Argument* inputArguments, 
+                        UA_Int32 outputArgumentsSize, const UA_Argument* outputArguments, 
+                        UA_NodeId* createdNodeId) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_MethodNode *newMethod = UA_MethodNode_new();
     UA_NodeId_copy(&nodeId, &newMethod->nodeId);
     UA_QualifiedName_copy(&browseName, &newMethod->browseName);
-    UA_String_copy(&browseName.name, &newMethod->displayName.text);
+    UA_LocalizedText_copy(&displayName, &newMethod->displayName);
+    UA_LocalizedText_copy(&description, &newMethod->description);
+    newMethod->writeMask = writeMask;
+    newMethod->userWriteMask = userWriteMask;
     newMethod->attachedMethod = method;
     newMethod->executable = UA_TRUE;
     newMethod->userExecutable = UA_TRUE;
     
-    UA_AddNodesResult addRes = UA_Server_addNode(server, (UA_Node*)newMethod, parentNodeId, referenceTypeId);
-    if(addRes.statusCode != UA_STATUSCODE_GOOD) {
+    UA_ExpandedNodeId parentExpandedNodeId;
+    UA_ExpandedNodeId_init(&parentExpandedNodeId);
+    UA_NodeId_copy(&parentNodeId, &parentExpandedNodeId.nodeId);
+    UA_AddNodesResult addRes = UA_Server_addNode(server, (UA_Node*)newMethod, parentExpandedNodeId, referenceTypeId);
+    retval |= addRes.statusCode;
+    if(retval!= UA_STATUSCODE_GOOD) {
         UA_MethodNode_delete(newMethod);
-        return addRes.statusCode;
+        return retval;
     }
     
     UA_ExpandedNodeId methodExpandedNodeId;
@@ -789,10 +797,17 @@ UA_Server_addMethodNode(UA_Server *server, const UA_QualifiedName browseName, UA
     if (createdNodeId != UA_NULL)
       UA_NodeId_copy(&addRes.addedNodeId, createdNodeId);
     
+    /* Only proceed with creating in/outputArguments if the method and both arguments are not
+     * UA_NULL; otherwise this is a pretty strong indicator that this node was generated,
+     * in which case these arguments will be created later and individually.
+     */
+    if (method == UA_NULL && inputArguments == UA_NULL && outputArguments == UA_NULL && inputArgumentsSize == 0 && outputArgumentsSize == 0)
+      return retval;
+    
     /* create InputArguments */
     UA_NodeId argId = UA_NODEID_NUMERIC(nodeId.namespaceIndex, 0); 
     UA_VariableNode *inputArgumentsVariableNode  = UA_VariableNode_new();
-    UA_StatusCode retval = UA_NodeId_copy(&argId, &inputArgumentsVariableNode->nodeId);
+    retval |= UA_NodeId_copy(&argId, &inputArgumentsVariableNode->nodeId);
     inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"InputArguments");
     inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
     inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");

+ 1 - 1
tools/pyUANamespace/logger.py

@@ -29,7 +29,7 @@ LOG_LEVEL_ERROR  = 0
 LOG_LEVEL_SILENT = -1
 
 # Change the following to filter logging output
-GLOBAL_LOG_LEVEL = LOG_LEVEL_SILENT
+GLOBAL_LOG_LEVEL = LOG_LEVEL_DEBUG
 
 def log(callee, logstr, level=LOG_LEVEL_DEBUG):
   prefixes = { LOG_LEVEL_DEBUG : "DBG: ",

+ 40 - 2
tools/pyUANamespace/open62541_MacroHelper.py

@@ -76,8 +76,46 @@ class open62541_MacroHelper():
       code.append("UA_Server_AddMonodirectionalReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", UA_FALSE);")
 
     return code
-
-  def getCreateNode(self, node):
+                               
+  def getCreateNodeNoBootstrap(self, node, parentNode, parentReference):
+    code = []
+    code.append("// Node: " + str(node) + ", " + str(node.browseName()))
+    if node.nodeClass() == NODE_CLASS_OBJECT:
+      code.append("UA_Server_addObjectNode(server, ")
+    elif node.nodeClass() == NODE_CLASS_VARIABLE:
+      code.append("UA_Server_addVariableNode(server,")
+    elif node.nodeClass() == NODE_CLASS_METHOD:
+      code.append("UA_Server_addMethodNode(server,")
+    elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
+      code.append("UA_Server_addObjectTypeNode(server,")
+    elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
+      code.append("UA_Server_addReferenceTypeNode(server,")
+    elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
+      code.append("UA_Server_addVariableTypeNode(server,")
+    elif node.nodeClass() == NODE_CLASS_DATATYPE:
+      code.append("UA_Server_addDataTypeNode(server,")
+    elif node.nodeClass() == NODE_CLASS_VIEW:
+      code.append("UA_Server_addViewNode(server,")
+    elif node.nodeClass() == NODE_CLASS_METHODTYPE:
+      code.append("UA_Server_addMethodTypeNode(server,")
+    else:
+      return []
+    
+    code.append("       " + str(self.getCreateNodeIDMacro(node)) + ",") # NodeId
+    extrNs = node.browseName().split(":")
+    if len(extrNs) > 1:
+      code.append("       UA_QUALIFIEDNAME(\"" +  str(extrNs[0]) + "\", \"" + extrNs[1] + "\"),")  # browseName
+    else:
+      code.append("       UA_QUALIFIEDNAME(0, \"" + str(node.browseName()) + "\"),")  # browseName
+    code.append("       UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\"),")  # displayName
+    code.append("       UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\"),")  # description
+    code.append("       " + str(self.getCreateNodeIDMacro(parentNode)) + ",") # ParentNode
+    code.append("       " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ",") # ReferenceTypeId
+    code.append("       " + str(node.writeMask()) + ", " + str(node.userWriteMask()) + ",") # write/userWriteMask
+      
+    return code
+    
+  def getCreateNodeBootstrap(self, node):
     nodetype = ""
     code = []
 

+ 7 - 4
tools/pyUANamespace/ua_builtin_types.py

@@ -312,7 +312,7 @@ class opcua_value_t():
   def printOpen62541CCode_SubType(self, asIndirect=True):
     return ""
 
-  def printOpen62541CCode(self):
+  def printOpen62541CCode(self, bootstrapping = True):
     codegen = open62541_MacroHelper()
     code = []
     valueName = self.parent.getCodePrintableID() + "_variant_DataContents"
@@ -360,7 +360,8 @@ class opcua_value_t():
           for v in self.value:
             code.append(valueName + "[" + str(self.value.index(v)) + "] = " + v.printOpen62541CCode_SubType() + ";")
         code.append("UA_Variant_setArrayCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", (UA_Int32) " + str(len(self.value)) + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
-        code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+        if (bootstrapping == True):
+          code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
     else:
       # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
       if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
@@ -382,12 +383,14 @@ class opcua_value_t():
           code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, " + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
           #FIXME: There is no membership definition for extensionObjects generated in this function.
           code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
-          code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+          if (bootstrapping == True):
+            code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
         else:
           code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
           code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
           code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(&" + valueName + ");")
-          code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+          if (bootstrapping == True):
+            code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
     return code
 
 

+ 24 - 1
tools/pyUANamespace/ua_namespace.py

@@ -438,7 +438,30 @@ class opcua_namespace():
       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
+    if tdNodes == None: 
+      tdNodes = []
+    if currentNode == None:
+      currentNode = self.getNodeByBrowseName("HasTypeDefinition")
+      tdNodes.append(currentNode)
+      if len(tdNodes) < 1:
+        return []
+    if hasSubtypeRefNode == None:
+      hasSubtypeRefNode = self.getNodeByBrowseName("HasSubtype")
+      if hasSubtypeRefNode == None:
+        return tdNodes
+    
+    # collect all subtypes of this node
+    for ref in currentNode.getReferences():
+      if ref.isForward() and ref.referenceType().id() == hasSubtypeRefNode.id():
+        tdNodes.append(ref.target())
+        self.getTypeDefinitionNodes(tdNodes=tdNodes, currentNode = ref.target(), hasSubtypeRefNode=hasSubtypeRefNode)
+    
+    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.
 

+ 234 - 51
tools/pyUANamespace/ua_node_types.py

@@ -622,11 +622,20 @@ class opcua_node_t:
 
   def printXML(self):
     pass
+  
+  def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
+    """ printOpen62541CCode_SubtypeEarly
 
-  def printOpen62541CCode_Subtype(self):
+        Initiate code segments for the nodes instantiotion that preceed
+        the actual UA_Server_addNode or UA_NodeStore_insert calls.
+    """
+    return []
+  
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     """ printOpen62541CCode_Subtype
 
-        Specific node subtype does it's own initialization
+        Appends node type specific information to the nodes  UA_Server_addNode 
+        or UA_NodeStore_insert calls.
     """
     return []
 
@@ -648,31 +657,27 @@ class opcua_node_t:
     if not (self in unPrintedNodes):
       log(self, str(self) + " attempted to reprint already printed node " + str(self)+ ".", LOG_LEVEL_WARN)
       return []
-    code = code + codegen.getCreateNode(self)
-    code = code + self.printOpen62541CCode_Subtype()
-
-    # If we are being passed a parent node by the namespace, use that for
-    # Registering ourselves in the namespace
-    parent = self.getFirstParentNode()
-    if not (parent[0] in unPrintedNodes) and (parent[0] != None):
-      if parent[1].referenceType() != None:
-        code.append("// Referencing node found and declared as parent: " + str(parent[0].id()) + "/" + str(parent[0].__node_browseName__) + " using " + str(parent[1].referenceType().id()) + "/" + str(parent[1].referenceType().__node_browseName__))
-        code.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parent[0]) + ", " + codegen.getCreateNodeIDMacro(parent[1].referenceType()) + ");")
-    
-    # Otherwise use the "Bootstrapping" method and we will get registered
-    # with other nodes later.
+
+    # 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, parentRef) = self.getFirstParentNode()
+    if not (parentNode in unPrintedNodes) and (parentNode != None) and (parentRef.referenceType() != None):
+      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 + self.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
+      code = code + codegen.getCreateNodeNoBootstrap(self, parentNode, parentRef)
+      code = code + self.printOpen62541CCode_Subtype(bootstrapping = False)
+      code.append("       UA_NULL);") # createdNodeId, wraps up the UA_Server_add<XYType>Node() call
+      # Parent to child reference is added by the server, do not reprint that reference
+      if parentRef in unPrintedReferences:
+        unPrintedReferences.remove(parentRef)
+    # Otherwise use the "Bootstrapping" method and we will get registered with other nodes later.
     else:
+      code = code + self.printOpen62541CCode_SubtypeEarly(bootstrapping = True)
+      code = code + codegen.getCreateNodeBootstrap(self)
+      code = code + self.printOpen62541CCode_Subtype(bootstrapping = True)
       code.append("// Parent node does not exist yet. This node will be bootstrapped and linked later.")
       code.append("UA_NodeStore_insert(server->nodestore, (UA_Node*) " + self.getCodePrintableID() + ", UA_NULL);")
-    # Note: getFirstParentNode will return [parentNode, referenceToChild]
-    (parentNode, parentRef) = self.getFirstParentNode()
-    if not (parentNode in unPrintedNodes) and (parentNode != None):
-      if parentRef.referenceType() != None:
-        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.append("UA_Server_addNode(server, (UA_Node*) " + self.getCodePrintableID() + ", " + codegen.getCreateExpandedNodeIDMacro(parentNode ) + ", " + codegen.getCreateNodeIDMacro(parentRef.referenceType()) + ");")
-        # Parent to child reference is added by the server, do not reprint that reference
-        if parentRef in unPrintedReferences:
-          unPrintedReferences.remove(parentRef)
+      
     # Try to print all references to nodes that already exist
     # Note: we know the reference types exist, because the namespace class made sure they were
     #       the first ones being printed
@@ -783,15 +788,38 @@ class opcua_node_referenceType_t(opcua_node_t):
         else:
           log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-    # Note that UA_FALSE is default here
+    codegen = open62541_MacroHelper()
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
+      myType = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myType = ref.target()
+          break
+      if myType==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myType = ref.target()
+            break
+      if myType==None:
+        log(self, str(self) + " failed to locate a type definition, assuming BaseDataType.", LOG_LEVEL_WARN)
+        code.append("       // No valid typeDefinition found; assuming BaseDataType")
+        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
+      else:
+        code.append("       " + codegen.getCreateExpandedNodeIDMacro(myType) + ",")
+      code.append("       UA_LOCALIZEDTEXT(\"\",\"" + str(self.inverseName()) + "\"),");
+      code.append("       // FIXME: Missing, isAbstract")
+      code.append("       // FIXME: Missing, symmetric")
+      return code
+    
     if self.isAbstract():
       code.append(self.getCodePrintableID() + "->isAbstract = UA_TRUE;")
-
     if self.symmetric():
       code.append(self.getCodePrintableID() + "->symmetric  = UA_TRUE;")
-
     if self.__reference_inverseName__ != "":
       code.append(self.getCodePrintableID() + "->inverseName  = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + self.__reference_inverseName__ + "\");")
     return code;
@@ -824,9 +852,34 @@ class opcua_node_object_t(opcua_node_t):
       if x.nodeType == x.ELEMENT_NODE:
         log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-
+    codegen = open62541_MacroHelper()
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
+      myType = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myType = ref.target()
+          break
+      if myType==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myType = ref.target()
+            break
+      if myType==None:
+        log(self, str(self) + " failed to locate a type definition, assuming BaseObjectType.", LOG_LEVEL_WARN)
+        code.append("       // No valid typeDefinition found; assuming BaseObjectType")
+        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
+      else:
+        code.append("       " + codegen.getCreateExpandedNodeIDMacro(myType) + ",")
+      
+      #FIXME: No event notifier in UA_Server_addNode call!
+      return code
+    
+    # We are being bootstrapped! Add the raw attributes to the node.
     code.append(self.getCodePrintableID() + "->eventNotifier = (UA_Byte) " + str(self.eventNotifier()) + ";")
     return code
 
@@ -987,10 +1040,34 @@ class opcua_node_variable_t(opcua_node_t):
         else:
           log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
+    code = []
+    # If we have an encodable value, try to encode that
+    if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
+      # Delegate the encoding of the datavalue to the helper if we have
+      # determined a valid encoding
+      if self.dataType().target().isEncodable():
+        if self.value() != None:
+          code = code + self.value().printOpen62541CCode(bootstrapping)
+          return code
+    if bootstrapping == False:
+      # Otherwise create a dummy variant to statisfy UA_Server_addVariableNode()
+      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_Variant_new();")
+    return code
+  
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
     codegen = open62541_MacroHelper()
-
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      code.append("       " + self.getCodePrintableID() + "_variant, ")
+      code.append("       // FIXME: missing minimumSamplingInterval")
+      code.append("       // FIXME: missing accessLevel")
+      code.append("       // FIXME: missing userAccessLevel")
+      code.append("       // FIXME: missing valueRank")
+      return code
+    
     if self.historizing():
       code.append(self.getCodePrintableID() + "->historizing = UA_TRUE;")
 
@@ -998,13 +1075,7 @@ class opcua_node_variable_t(opcua_node_t):
     code.append(self.getCodePrintableID() + "->userAccessLevel = (UA_Int32) " + str(self.userAccessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
-
-    if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
-      # Delegate the encoding of the datavalue to the helper if we have
-      # determined a valid encoding
-      if self.dataType().target().isEncodable():
-        if self.value() != None:
-          code = code + self.value().printOpen62541CCode()
+    
     return code
 
 class opcua_node_method_t(opcua_node_t):
@@ -1054,9 +1125,20 @@ class opcua_node_method_t(opcua_node_t):
       if x.nodeType == x.ELEMENT_NODE:
         log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      code.append("       // Note: in/outputArguments are added by attaching the variable nodes,")
+      code.append("       //       not by including the in the addMethodNode() call.")
+      code.append("       UA_NULL,")
+      code.append("       0, UA_NULL,")
+      code.append("       0, UA_NULL,")
+      code.append("       // FIXME: Missing executable")
+      code.append("       // FIXME: Missing userExecutable")
+      return code
+    
     # UA_False is default for booleans on _init()
     if self.executable():
       code.append(self.getCodePrintableID() + "->executable = UA_TRUE;")
@@ -1090,13 +1172,38 @@ class opcua_node_objectType_t(opcua_node_t):
       if x.nodeType == x.ELEMENT_NODE:
         log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-
+    codegen = open62541_MacroHelper();
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
+      myType = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myType = ref.target()
+          break
+      if myType==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myType = ref.target()
+            break
+      if myType==None:
+        log(self, str(self) + " failed to locate a type definition, assuming BaseObjectType.", LOG_LEVEL_WARN)
+        code.append("       // No valid typeDefinition found; assuming BaseObjectType")
+        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),")
+      else:
+        code.append("       " + codegen.getCreateExpandedNodeIDMacro(myType) + ",")
+      
+      if (self.isAbstract()):
+        code.append("       UA_TRUE,")
+      else:
+        code.append("       UA_FALSE,")
+    
+    # Fallback mode for bootstrapping
     if (self.isAbstract()):
       code.append(self.getCodePrintableID() + "->isAbstract = UA_TRUE;")
-    #else:
-    #  code.append(self.getCodePrintableID() + "->isAbstract = UA_FALSE;")
 
     return code
 
@@ -1184,9 +1291,34 @@ class opcua_node_variableType_t(opcua_node_t):
         else:
           log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_SubtypeEarly(self, bootstrapping = True):
     code = []
-
+    # If we have an encodable value, try to encode that
+    if self.dataType() != None and isinstance(self.dataType().target(), opcua_node_dataType_t):
+      # Delegate the encoding of the datavalue to the helper if we have
+      # determined a valid encoding
+      if self.dataType().target().isEncodable():
+        if self.value() != None:
+          code = code + self.value().printOpen62541CCode(bootstrapping)
+          return code
+    if bootstrapping == False:
+      # Otherwise create a dummy variant to statisfy UA_Server_addVariableNode()
+      code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_Variant_new();")
+    return code
+  
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
+    code = []
+    codegen = open62541_MacroHelper()
+    
+    if bootstrapping == False:
+      code.append("       " + self.getCodePrintableID() + "_variant, ")
+      code.append("       " + str(self.valueRank()) + ",")
+      if self.isAbstract():
+        code.append("       UA_TRUE,")
+      else:
+        code.append("       UA_FALSE,")
+      return code
+    
     if (self.isAbstract()):
       code.append(self.getCodePrintableID() + "->isAbstract = UA_TRUE;")
     else:
@@ -1500,9 +1632,36 @@ class opcua_node_dataType_t(opcua_node_t):
           else:
             return opcua_value_t(None).getTypeByString(enc[0]).getNumericRepresentation()
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-
+    codegen = open62541_MacroHelper()
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
+      myType = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myType = ref.target()
+          break
+      if myType==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myType = ref.target()
+            break
+      if myType==None:
+        log(self, str(self) + " failed to locate a type definition, assuming BaseDataType.", LOG_LEVEL_WARN)
+        code.append("       // No valid typeDefinition found; assuming BaseDataType")
+        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE),")
+      else:
+        code.append("       " + codegen.getCreateExpandedNodeIDMacro(myType) + ",")
+      
+      if (self.isAbstract()):
+        code.append("       UA_TRUE,")
+      else:
+        code.append("       UA_FALSE,")
+      return code
+    
     if (self.isAbstract()):
       code.append(self.getCodePrintableID() + "->isAbstract = UA_TRUE;")
     else:
@@ -1536,9 +1695,33 @@ class opcua_node_view_t(opcua_node_t):
       if x.nodeType == x.ELEMENT_NODE:
         log(self,  "Unprocessable XML Element: " + x.tagName, LOG_LEVEL_INFO)
 
-  def printOpen62541CCode_Subtype(self):
+  def printOpen62541CCode_Subtype(self, bootstrapping = True):
     code = []
-
+    codegen = open62541_MacroHelper()
+    
+    # Detect if this is bootstrapping or if we are attempting to use userspace...
+    if bootstrapping == False:
+      typeDefs = self.getNamespace().getSubTypesOf() # defaults to TypeDefinition
+      myType = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myType = ref.target()
+          break
+      if myType==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myType = ref.target()
+            break
+      if myType==None:
+        log(self, str(self) + " failed to locate a type definition, assuming BaseViewType.", LOG_LEVEL_WARN)
+        code.append("       // No valid typeDefinition found; assuming BaseViewType")
+        code.append("       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEViewTYPE),")
+      else:
+        code.append("       " + codegen.getCreateExpandedNodeIDMacro(myType) + ",")
+      code.append("       // FIXME: Missing eventNotifier")
+      code.append("       // FIXME: Missing containsNoLoops")
+      return code
+    
     if self.containsNoLoops():
       code.append(self.getCodePrintableID() + "->containsNoLoops = UA_TRUE;")
     else: