Quellcode durchsuchen

Merge pull request #359 from acplt/api_hl_abstractions

Api hl abstractions
Sten Grüner vor 9 Jahren
Ursprung
Commit
3f766ff769

+ 1 - 1
doc/building.rst

@@ -13,7 +13,7 @@ Using the GCC compiler, the following calls build the library on Linux.
    
 
 Building with CMake on Ubuntu or Debian
------------------------------
+---------------------------------------
 
 .. code-block:: bash
    

Datei-Diff unterdrückt, da er zu groß ist
+ 268 - 0
doc/tutorial_firstSteps.rst


Datei-Diff unterdrückt, da er zu groß ist
+ 550 - 0
doc/tutorial_noderelations.rst


Datei-Diff unterdrückt, da er zu groß ist
+ 313 - 0
doc/tutorial_nodes.rst


Datei-Diff unterdrückt, da er zu groß ist
+ 266 - 0
doc/tutorial_nodescontents.rst


+ 17 - 0
examples/CMakeLists.txt

@@ -1,4 +1,5 @@
 include_directories(${PROJECT_SOURCE_DIR}/include)
+include_directories(${PROJECT_BINARY_DIR}/src_generated)
 
 set(LIBS open62541)
 if(NOT WIN32)
@@ -19,6 +20,22 @@ target_link_libraries(server_variable ${LIBS})
 add_executable(server_repeated_job server_repeated_job.c)
 target_link_libraries(server_repeated_job ${LIBS})
 
+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/logger.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_link_libraries(server_nodeset ${LIBS})
+
 if(ENABLE_METHODCALLS)
   add_executable(server_method server_method.c)
   target_link_libraries(server_method ${LIBS})

+ 62 - 34
examples/client.c

@@ -5,12 +5,12 @@
 # include "networklayer_tcp.h"
 # include "logger_stdout.h"
 # include "ua_types_encoding_binary.h"
+#include "server/ua_nodes.h"
 #else
 # include "open62541.h"
 # include <string.h>
 # include <stdlib.h>
 #endif
-
 #include <stdio.h>
 
 void handler_TheAnswerChanged(UA_UInt32 handle, UA_DataValue *value);
@@ -19,6 +19,17 @@ void handler_TheAnswerChanged(UA_UInt32 handle, UA_DataValue *value) {
     return;
 }
 
+UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
+UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {  
+  printf("References ns=%d;i=%d using i=%d ", childId.namespaceIndex, childId.identifier.numeric, referenceTypeId.identifier.numeric);
+  if (isInverse == UA_TRUE) {
+    printf(" (inverse)");
+  }
+  printf("\n");
+  
+  return UA_STATUSCODE_GOOD;
+}
+
 int main(int argc, char *argv[]) {
     UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
     UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect,
@@ -145,7 +156,7 @@ int main(int argc, char *argv[]) {
     UA_Int32 outputSize;
     UA_Variant *output;
     
-    retval = UA_Client_CallServerMethod(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+    retval = UA_Client_callServerMethod(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(1, 62541), 1, &input, &outputSize, &output);
     if(retval == UA_STATUSCODE_GOOD) {
         printf("Method call was successfull, and %i returned values available.\n", outputSize);
@@ -160,8 +171,10 @@ int main(int argc, char *argv[]) {
 #ifdef ENABLE_ADDNODES 
     /* Create a new object type node */
     // New ReferenceType
-    UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client,
-        UA_EXPANDEDNODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times)
+    UA_StatusCode addRes;
+    UA_NodeId retNodeId;
+    addRes = UA_Client_addReferenceTypeNode(client,
+        UA_NODEID_NUMERIC(1, 12133), // Assign this NodeId (will fail if client is called multiple times)
         UA_QUALIFIEDNAME(0, "NewReference"),
         UA_LOCALIZEDTEXT("en_US", "TheNewReference"),
         UA_LOCALIZEDTEXT("en_US", "References something that might or might not exist."),
@@ -169,44 +182,43 @@ int main(int argc, char *argv[]) {
         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
         (UA_UInt32) 0, (UA_UInt32) 0, 
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy"));
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewReference' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+        UA_LOCALIZEDTEXT("en_US", "IsNewlyReferencedBy"),
+        &retNodeId);
+    if (addRes == UA_STATUSCODE_GOOD ) {
+      printf("Created 'NewReference' with numeric NodeID %u\n", retNodeId.identifier.numeric );
     }
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
     
     // New ObjectType
-    adResp = UA_Client_createObjectTypeNode(client,    
-        UA_EXPANDEDNODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times)
+    addRes = UA_Client_addObjectTypeNode(client,    
+        UA_NODEID_NUMERIC(1, 12134), // Assign this NodeId (will fail if client is called multiple times)
         UA_QUALIFIEDNAME(0, "NewObjectType"),
         UA_LOCALIZEDTEXT("en_US", "TheNewObjectType"),
         UA_LOCALIZEDTEXT("en_US", "Put innovative description here."),
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
         (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
-        if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewObjectType' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), 
+        UA_FALSE,
+        &retNodeId);
+    if (addRes == UA_STATUSCODE_GOOD ) {
+      printf("Created 'NewObjectType' with numeric NodeID %u\n", retNodeId.identifier.numeric );
     }
     
     // New Object
-    adResp = UA_Client_createObjectNode(client,    
-        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
+    addRes = UA_Client_addObjectNode(client,    
+        UA_NODEID_NUMERIC(1, 0), // Assign new/random NodeID  
         UA_QUALIFIEDNAME(0, "TheNewGreatNodeBrowseName"),
         UA_LOCALIZEDTEXT("en_US", "TheNewGreatNode"),
         UA_LOCALIZEDTEXT("de_DE", "Hier koennte Ihre Webung stehen!"),
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
         (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewObject' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), 
+        &retNodeId);
+    if (addRes == UA_STATUSCODE_GOOD ) {
+      printf("Created 'NewObject' with numeric NodeID %u\n", retNodeId.identifier.numeric );
     }
     
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
-    
     // New Integer Variable
     UA_Variant *theValue = UA_Variant_new();
     UA_Int32 *theValueDate = UA_Int32_new();
@@ -214,27 +226,43 @@ int main(int argc, char *argv[]) {
     theValue->type = &UA_TYPES[UA_TYPES_INT32];
     theValue->data = theValueDate;
     
-    adResp = UA_Client_createVariableNode(client,
-        UA_EXPANDEDNODEID_NUMERIC(1, 0), // Assign new/random NodeID  
+    addRes = UA_Client_addVariableNode(client,
+        UA_NODEID_NUMERIC(1, 0), // Assign new/random NodeID  
         UA_QUALIFIEDNAME(0, "VariableNode"),
         UA_LOCALIZEDTEXT("en_US", "TheNewVariableNode"),
         UA_LOCALIZEDTEXT("en_US", "This integer is just amazing - it has digits and everything."),
         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
         (UA_UInt32) 0, (UA_UInt32) 0, 
-        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-        UA_NODEID_NUMERIC(0, UA_NS0ID_INT32),
-        theValue);
-    if (adResp->resultsSize > 0 && adResp->results[0].statusCode == UA_STATUSCODE_GOOD ) {
-        printf("Created 'NewVariable' with numeric NodeID %u\n", adResp->results[0].addedNodeId.identifier.numeric );
+        theValue, 
+        &retNodeId);
+    if (addRes == UA_STATUSCODE_GOOD ) {
+      printf("Created 'NewVariable' with numeric NodeID %u\n", retNodeId.identifier.numeric );
     }
-    UA_AddNodesResponse_deleteMembers(adResp);
-    free(adResp);
+
     free(theValue);
     /* Done creating a new node*/
+#else
+  // retNodeId is needed for the next test
+  UA_NodeId retNodeId = UA_NODEID_STRING(1, "the.answer");
 #endif
-    UA_Client_disconnect(client);
-    UA_Client_delete(client);
-    return UA_STATUSCODE_GOOD;
+  // Iterate over all nodes in 'Objects'
+  UA_Client_forEachChildNodeCall(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, NULL);
+    
+  // Get a copy of the node 'TheNewVariableNode' and delete it
+  void *theCopy;
+  UA_Client_getNodeCopy(client, retNodeId, (void*) &theCopy);
+  UA_Client_deleteNodeCopy(client, &theCopy);
+  
+  // Delete a serverside node
+  UA_Client_deleteMethodNode(client, UA_NODEID_NUMERIC(1,62541));
+  
+  // Set a localized string version of "Objects"
+  UA_LocalizedText objectsLocale = UA_LOCALIZEDTEXT("de_DE", "Die Objekte");
+  UA_Client_setAttributeValue(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_ATTRIBUTEID_WRITEMASK, (void *) &objectsLocale);
+  
+  UA_Client_disconnect(client);
+  UA_Client_delete(client);
+  return UA_STATUSCODE_GOOD;
 }
 

+ 184 - 158
examples/server.c

@@ -35,6 +35,7 @@
 # endif
 #include <pthread.h>
 #endif
+
 /****************************/
 /* Server-related variables */
 /****************************/
@@ -115,17 +116,18 @@ FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
 static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
-    if(range)
-        return UA_STATUSCODE_BADINDEXRANGEINVALID;
-
-    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus, &UA_TYPES[UA_TYPES_BOOLEAN]);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-	if(sourceTimeStamp) {
-		value->sourceTimestamp = UA_DateTime_now();
-		value->hasSourceTimestamp = UA_TRUE;
-	}
-	return UA_STATUSCODE_GOOD;
+  if(range)
+    return UA_STATUSCODE_BADINDEXRANGEINVALID;
+
+  UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus, &UA_TYPES[UA_TYPES_BOOLEAN]);
+  if(retval != UA_STATUSCODE_GOOD)
+    return retval;
+  
+  if(sourceTimeStamp) {
+          value->sourceTimestamp = UA_DateTime_now();
+          value->hasSourceTimestamp = UA_TRUE;
+  }
+  return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const UA_NumericRange *range) {
@@ -155,8 +157,9 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const
 }
 
 #ifdef ENABLE_METHODCALLS
-static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output) {
+static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
     UA_String tmp = UA_STRING("Hello World");
+    //UA_Server *theServer = (UA_Server *) handle; // Commented, would result in "unused variable" error
     UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
     printf("getMonitoredItems was called\n");
     return UA_STATUSCODE_GOOD;
@@ -192,175 +195,198 @@ static UA_ByteString loadCertificate(void) {
     return certificate;
 }
 
+UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
+UA_StatusCode nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {  
+  printf("References ns=%d;i=%d using i=%d ", childId.namespaceIndex, childId.identifier.numeric, referenceTypeId.identifier.numeric);
+  if (isInverse == UA_TRUE) {
+    printf(" (inverse)");
+  }
+  printf("\n");
+  
+  return UA_STATUSCODE_GOOD;
+}
+
 int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
+  signal(SIGINT, stopHandler); /* catches ctrl-c */
 #ifdef UA_MULTITHREADING
-	pthread_rwlock_init(&writeLock, 0);
+  pthread_rwlock_init(&writeLock, 0);
 #endif
 
-    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-    logger = Logger_Stdout_new();
-    UA_Server_setLogger(server, logger);
-    UA_ByteString certificate = loadCertificate();
-    UA_Server_setServerCertificate(server, certificate);
-    UA_ByteString_deleteMembers(&certificate);
-	UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+  UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+  logger = Logger_Stdout_new();
+  UA_Server_setLogger(server, logger);
+  UA_ByteString certificate = loadCertificate();
+  UA_Server_setServerCertificate(server, certificate);
+  UA_ByteString_deleteMembers(&certificate);
+  UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
-	// add node with the datetime data source
-	UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
-	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
-	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
+  // add node with the datetime data source
+  UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
+  const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
+  UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
 
 #ifndef _WIN32
-	//cpu temperature monitoring for linux machines
-	if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))) {
-		// add node with the data source
-		UA_DataSource temperatureDataSource = (UA_DataSource) {.handle = NULL, .read = readTemperature, .write = NULL};
-		const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
-		UA_Server_addDataSourceVariableNode(server, temperatureDataSource, tempName, UA_NODEID_NULL,
-                                                    UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
-	}
-
-	//LED control for rpi
-	if(access("/sys/class/leds/led0/trigger", F_OK ) != -1 || access("/sys/class/leds/led0/brightness", F_OK ) != -1) {
-        if((triggerFile = fopen("/sys/class/leds/led0/trigger", "w")) && (ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
-            //setting led mode to manual
-            fprintf(triggerFile, "%s", "none");
-            fflush(triggerFile);
-
-            //turning off led initially
-            fprintf(ledFile, "%s", "1");
-            fflush(ledFile);
-
-            // add node with the LED status data source
-            UA_DataSource ledStatusDataSource = (UA_DataSource) {.handle = NULL, .read = readLedStatus, .write = writeLedStatus};
-            const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
-            UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
-                                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
-        } else {
-            UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "[Raspberry Pi] LED file exist, but I have no access (try to run server with sudo)");
-        }
+  //cpu temperature monitoring for linux machines
+  if((temperatureFile = fopen("/sys/class/thermal/thermal_zone0/temp", "r"))) {
+          // add node with the data source
+          UA_DataSource temperatureDataSource = (UA_DataSource) {.handle = NULL, .read = readTemperature, .write = NULL};
+          const UA_QualifiedName tempName = UA_QUALIFIEDNAME(1, "cpu temperature");
+          UA_Server_addDataSourceVariableNode(server, temperatureDataSource, tempName, UA_NODEID_NULL,
+                                              UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
+  }
+
+  //LED control for rpi
+  if(access("/sys/class/leds/led0/trigger", F_OK ) != -1 || access("/sys/class/leds/led0/brightness", F_OK ) != -1) {
+    if((triggerFile = fopen("/sys/class/leds/led0/trigger", "w")) && (ledFile = fopen("/sys/class/leds/led0/brightness", "w"))) {
+      //setting led mode to manual
+      fprintf(triggerFile, "%s", "none");
+      fflush(triggerFile);
+
+      //turning off led initially
+      fprintf(ledFile, "%s", "1");
+      fflush(ledFile);
+
+      // add node with the LED status data source
+      UA_DataSource ledStatusDataSource = (UA_DataSource) {.handle = NULL, .read = readLedStatus, .write = writeLedStatus};
+      const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
+      UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
+                                          UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                          UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
+    } else {
+      UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "[Raspberry Pi] LED file exist, but I have no access (try to run server with sudo)");
     }
+  }
 #endif
 
-	// add a static variable node to the adresspace
-    UA_Variant *myIntegerVariant = UA_Variant_new();
-    UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
-    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
-                              myIntegerNodeId, parentNodeId, parentReferenceNodeId, NULL);
-
-   /**************/
-   /* Demo Nodes */
-   /**************/
+  // add a static variable node to the adresspace
+  UA_Variant *myIntegerVariant = UA_Variant_new();
+  UA_Int32 myInteger = 42;
+  UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+  const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+  const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+  UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+  UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+  UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US", "the answer"), UA_LOCALIZEDTEXT("en_US", "the answer"),
+                            parentNodeId, parentReferenceNodeId, 0, 0, myIntegerVariant, NULL);
+
+  /**************/
+  /* Demo Nodes */
+  /**************/
 
 #define DEMOID 50000
-   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), 
+                          UA_LOCALIZEDTEXT("en_US","Demo"), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
+                          0, 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
 
 #define SCALARID 50001
-   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Scalar"), UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(1, DEMOID),
-                           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID), UA_QUALIFIEDNAME(1, "Scalar"), UA_LOCALIZEDTEXT("en_US","Demo"), 
+                          UA_LOCALIZEDTEXT("en_US","Demo"), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
+                          0, 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
 
 #define ARRAYID 50002
-   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Array"), UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(1, DEMOID),
-                           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID), UA_QUALIFIEDNAME(1, "Array"), UA_LOCALIZEDTEXT("en_US","Demo"), 
+                          UA_LOCALIZEDTEXT("en_US","Demo"), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
+                          0, 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
 
 #define MATRIXID 50003
-   UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Matrix"), UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(1, DEMOID),
-                           UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
-
-   UA_UInt32 id = 51000; //running id in namespace 0
-   for(UA_UInt32 type = 0; UA_IS_BUILTIN(type); type++) {
-       if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
-           continue;
-       //add a scalar node for every built-in type
-        void *value = UA_new(&UA_TYPES[type]);
-        UA_Variant *variant = UA_Variant_new();
-        UA_Variant_setScalar(variant, value, &UA_TYPES[type]);
-        char name[15];
-        sprintf(name, "%02d", type);
-        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
-        UA_Server_addVariableNode(server, variant, qualifiedName, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
-
-        //add an array node for every built-in type
-        UA_Variant *arrayvar = UA_Variant_new();
-        UA_Variant_setArray(arrayvar, UA_Array_new(&UA_TYPES[type], 10), 10, &UA_TYPES[type]);
-        UA_Server_addVariableNode(server, arrayvar, qualifiedName, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
-
-        //add an matrix node for every built-in type
-        arrayvar = UA_Variant_new();
-        void* myMultiArray = UA_Array_new(&UA_TYPES[type],9);
-        arrayvar->arrayDimensions = UA_Array_new(&UA_TYPES[UA_TYPES_INT32],2);
-        arrayvar->arrayDimensions[0] = 3;
-        arrayvar->arrayDimensions[1] = 3;
-        arrayvar->arrayDimensionsSize = 2;
-        arrayvar->arrayLength = 9;
-        arrayvar->data = myMultiArray;
-        arrayvar->type = &UA_TYPES[type];
-        UA_Server_addVariableNode(server, arrayvar, qualifiedName, UA_NODEID_NUMERIC(1, ++id),
-                                  UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), NULL);
-   }
-
+  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_QUALIFIEDNAME(1, "Matrix"), UA_LOCALIZEDTEXT("en_US","Demo"), 
+                          UA_LOCALIZEDTEXT("en_US","Demo"), UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 
+                          0, 0,  UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+
+  UA_UInt32 id = 51000; //running id in namespace 0
+  for(UA_UInt32 type = 0; UA_IS_BUILTIN(type); type++) {
+    if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
+        continue;
+    //add a scalar node for every built-in type
+    void *value = UA_new(&UA_TYPES[type]);
+    UA_Variant *variant = UA_Variant_new();
+    UA_Variant_setScalar(variant, value, &UA_TYPES[type]);
+    char name[15];
+    sprintf(name, "%02d", type);
+    UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",""), UA_LOCALIZEDTEXT("en_US",""),
+                              UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 0, 0, variant, NULL);
+
+    //add an array node for every built-in type
+    UA_Variant *arrayvar = UA_Variant_new();
+    UA_Variant_setArray(arrayvar, UA_Array_new(&UA_TYPES[type], 10), 10, &UA_TYPES[type]);
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",""), UA_LOCALIZEDTEXT("en_US",""),
+                              UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 0, 0, arrayvar, NULL);
+
+    //add an matrix node for every built-in type
+    arrayvar = UA_Variant_new();
+    void* myMultiArray = UA_Array_new(&UA_TYPES[type],9);
+    arrayvar->arrayDimensions = UA_Array_new(&UA_TYPES[UA_TYPES_INT32],2);
+    arrayvar->arrayDimensions[0] = 3;
+    arrayvar->arrayDimensions[1] = 3;
+    arrayvar->arrayDimensionsSize = 2;
+    arrayvar->arrayLength = 9;
+    arrayvar->data = myMultiArray;
+    arrayvar->type = &UA_TYPES[type];
+    UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), qualifiedName, UA_LOCALIZEDTEXT("en_US",""), UA_LOCALIZEDTEXT("en_US",""),
+                              UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 0, 0, arrayvar, NULL);
+  }
 
 #ifdef ENABLE_METHODCALLS
-   UA_Argument inputArguments;
-   UA_Argument_init(&inputArguments);
-   inputArguments.arrayDimensionsSize = -1;
-   inputArguments.arrayDimensions = NULL;
-   inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-   inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-   inputArguments.name = UA_STRING("Input an integer");
-   inputArguments.valueRank = -1;
-
-   UA_Argument outputArguments;
-   UA_Argument_init(&outputArguments);
-   outputArguments.arrayDimensionsSize = -1;
-   outputArguments.arrayDimensions = NULL;
-   outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-   outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-   outputArguments.name = UA_STRING("Input an integer");
-   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_StatusCode retvalM = 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),
-                                                  &getMonitoredItems, 1, &inputArguments, 1, &outputArguments, &methodId);
-                                                  
-   if (retvalM == UA_STATUSCODE_GOOD)
-      UA_NodeId_deleteMembers(&methodId);
-                                                  
+  UA_Argument inputArguments;
+  UA_Argument_init(&inputArguments);
+  inputArguments.arrayDimensionsSize = -1;
+  inputArguments.arrayDimensions = NULL;
+  inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+  inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+  inputArguments.name = UA_STRING("Input an integer");
+  inputArguments.valueRank = -1;
+
+  UA_Argument outputArguments;
+  UA_Argument_init(&outputArguments);
+  outputArguments.arrayDimensionsSize = -1;
+  outputArguments.arrayDimensions = NULL;
+  outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+  outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+  outputArguments.name = UA_STRING("Input an integer");
+  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_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, // Call this method
+                          (void *) server,    // Pass our server pointer as a handle to the method
+                          1, &inputArguments, 1, &outputArguments, &methodId);
 #endif
-	//start server
-	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
-
-	//ctrl-c received -> clean up
-	UA_Server_delete(server);
-
-	if(temperatureFile)
-		fclose(temperatureFile);
-
-	if(triggerFile) {
-		fseek(triggerFile, 0, SEEK_SET);
-		//setting led mode to default
-		fprintf(triggerFile, "%s", "mmc0");
-		fclose(triggerFile);
-	}
-
-	if(ledFile)
-		fclose(ledFile);
+   
+  // Example for iterating over all nodes referenced by "Objects":
+  printf("Nodes connected to 'Objects':\n=============================\n");
+  UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), nodeIter, NULL);
+  
+  // Some easy localization
+  UA_LocalizedText objectsName = UA_LOCALIZEDTEXT("de_DE", "Objekte");
+  UA_Server_setAttributeValue(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_ATTRIBUTEID_DISPLAYNAME, (void *) &objectsName);
+  
+  //start server
+  UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+
+  //ctrl-c received -> clean up
+  UA_Server_delete(server);
+
+  if(temperatureFile)
+          fclose(temperatureFile);
+
+  if(triggerFile) {
+          fseek(triggerFile, 0, SEEK_SET);
+          //setting led mode to default
+          fprintf(triggerFile, "%s", "mmc0");
+          fclose(triggerFile);
+  }
+  
+  if(ledFile)
+    fclose(ledFile);
 
 #ifdef UA_MULTITHREADING
-	pthread_rwlock_destroy(&writeLock);
+  pthread_rwlock_destroy(&writeLock);
 #endif
 
-	return retval;
+  return retval;
 }

+ 82 - 92
examples/server_method.c

@@ -18,23 +18,22 @@
 UA_Boolean running = UA_TRUE;
 UA_Logger logger;
 
-static UA_StatusCode helloWorldMethod(const UA_NodeId objectId,
-		const UA_Variant *input, UA_Variant *output) {
-	UA_String *inputStr = (UA_String*) input->data;
-	UA_String tmp = UA_STRING_ALLOC("Hello ");
-	if (inputStr->length > 0) {
-		tmp.data = realloc(tmp.data, tmp.length + inputStr->length);
-		memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
-		tmp.length += inputStr->length;
-	}
-	UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
-	UA_String_deleteMembers(&tmp);
-	UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
-	return UA_STATUSCODE_GOOD;
-}
+static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
+        UA_String *inputStr = (UA_String*)input->data;
+        UA_String tmp = UA_STRING_ALLOC("Hello ");
+        if(inputStr->length > 0) {
+            tmp.data = realloc(tmp.data, tmp.length + inputStr->length);
+            memcpy(&tmp.data[tmp.length], inputStr->data, inputStr->length);
+            tmp.length += inputStr->length;
+        }
+        UA_Variant_setScalarCopy(output, &tmp, &UA_TYPES[UA_TYPES_STRING]);
+        UA_String_deleteMembers(&tmp);
+        UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Hello World was called");
+        return UA_STATUSCODE_GOOD;
+} 
 
 static UA_StatusCode IncInt32ArrayValues(const UA_NodeId objectId,
-		const UA_Variant *input, UA_Variant *output) {
+                                         const UA_Variant *input, UA_Variant *output, void *handle) {
 
 
 	UA_Variant_setArrayCopy(output,input->data,5,&UA_TYPES[UA_TYPES_INT32]);
@@ -45,92 +44,83 @@ static UA_StatusCode IncInt32ArrayValues(const UA_NodeId objectId,
 	return UA_STATUSCODE_GOOD;
 }
 
-
 static void stopHandler(int sign) {
     UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
     running = 0;
 }
 int main(int argc, char** argv) {
-	signal(SIGINT, stopHandler); /* catches ctrl-c */
-
-	/* initialize the server */
-	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-	logger = Logger_Stdout_new();
-	UA_Server_setLogger(server, logger);
-	UA_Server_addNetworkLayer(server,
-			ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-
-
-	 /* add the method node with the callback */
-	 UA_Argument inputArguments;
-	 UA_Argument_init(&inputArguments);
-	 inputArguments.arrayDimensionsSize = -1;
-	 inputArguments.arrayDimensions = NULL;
-	 inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-	 inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-	 inputArguments.name = UA_STRING("MyInput");
-	 inputArguments.valueRank = -1;
-
-	 UA_Argument outputArguments;
-	 UA_Argument_init(&outputArguments);
-	 outputArguments.arrayDimensionsSize = -1;
-	 outputArguments.arrayDimensions = NULL;
-	 outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
-	 outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
-	 outputArguments.name = UA_STRING("MyOutput");
-	 outputArguments.valueRank = -1;
-	 UA_NodeId createdNodeId;
-	 UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "hello world object node"),UA_NODEID_NUMERIC(1,31415),
-	 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-	 UA_NODEID_NUMERIC(0,UA_NS0ID_OBJECTNODE),&createdNodeId);
-
-	 UA_Server_addMethodNode(server, UA_QUALIFIEDNAME(1, "hello world"), UA_NODEID_NUMERIC(1,62541),
-	 UA_EXPANDEDNODEID_NUMERIC(1, 31415), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-	 &helloWorldMethod, 1, &inputArguments, 1, &outputArguments, NULL);
-
-	 /* add another method node: output argument as 1d Int32 array*/
-	// define input arguments
-	UA_Argument_init(&inputArguments);
-	inputArguments.arrayDimensionsSize = 1;
-	UA_UInt32 * pInputDimensions = UA_UInt32_new();
-	pInputDimensions[0] = 5;
-	inputArguments.arrayDimensions = pInputDimensions;
-	inputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-	inputArguments.description = UA_LOCALIZEDTEXT("en_US",
-			"input an array with 5 elements, type int32");
-	inputArguments.name = UA_STRING("int32 value");
-	inputArguments.valueRank = 1;
-
-	// define output arguments
-	UA_Argument_init(&outputArguments);
-	outputArguments.arrayDimensionsSize = 1;
-	UA_UInt32 * pOutputDimensions = UA_UInt32_new();
-	pOutputDimensions[0] = 5;
-	outputArguments.arrayDimensions = pOutputDimensions;
-	outputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
-	outputArguments.description = UA_LOCALIZEDTEXT("en_US",
-			"increment each array index");
-	outputArguments.name = UA_STRING(
-			"output is the array, each index is incremented");
-	outputArguments.valueRank = 1;
-
-	UA_Server_addMethodNode(server, UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
-			UA_NODEID_STRING(1, "IncInt32ArrayValues"),
-			UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-			UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), &IncInt32ArrayValues,
-			1, &inputArguments, 1, &outputArguments, NULL);
-
-	/* start server */
-	UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
-
-	/* ctrl-c received -> clean up */
-
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    /* initialize the server */
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    logger = Logger_Stdout_new();
+    UA_Server_setLogger(server, logger);
+    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    /* add the method node with the callback */
+    UA_Argument inputArguments;
+    UA_Argument_init(&inputArguments);
+    inputArguments.arrayDimensionsSize = -1;
+    inputArguments.arrayDimensions = NULL;
+    inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    inputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    inputArguments.name = UA_STRING("MyInput");
+    inputArguments.valueRank = -1;
+
+    UA_Argument outputArguments;
+    UA_Argument_init(&outputArguments);
+    outputArguments.arrayDimensionsSize = -1;
+    outputArguments.arrayDimensions = NULL;
+    outputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "A String");
+    outputArguments.name = UA_STRING("MyOutput");
+    outputArguments.valueRank = -1;
         
+    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, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+
+    /* add another method node: output argument as 1d Int32 array*/
+    // define input arguments
+    UA_Argument_init(&inputArguments);
+    inputArguments.arrayDimensionsSize = 1;
+    UA_UInt32 * pInputDimensions = UA_UInt32_new();
+    pInputDimensions[0] = 5;
+    inputArguments.arrayDimensions = pInputDimensions;
+    inputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    inputArguments.description = UA_LOCALIZEDTEXT("en_US",
+                    "input an array with 5 elements, type int32");
+    inputArguments.name = UA_STRING("int32 value");
+    inputArguments.valueRank = 1;
+
+    // define output arguments
+    UA_Argument_init(&outputArguments);
+    outputArguments.arrayDimensionsSize = 1;
+    UA_UInt32 * pOutputDimensions = UA_UInt32_new();
+    pOutputDimensions[0] = 5;
+    outputArguments.arrayDimensions = pOutputDimensions;
+    outputArguments.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US",
+                    "increment each array index");
+    outputArguments.name = UA_STRING(
+                    "output is the array, each index is incremented");
+    outputArguments.valueRank = 1;
+
+    UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"), UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
+                            UA_LOCALIZEDTEXT("en_US","1dArrayExample"), UA_LOCALIZEDTEXT("en_US","1dArrayExample"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), 
+                            0, 0, &IncInt32ArrayValues, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+
+    /* start server */
+    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+
+        /* ctrl-c received -> clean up */
     UA_UInt32_delete(pInputDimensions);
     UA_UInt32_delete(pOutputDimensions);
-	UA_Server_delete(server);
+    UA_Server_delete(server);
 
-	return retval;
+    return retval;
 }
 
 

+ 47 - 0
examples/server_nodeset.c

@@ -0,0 +1,47 @@
+/*
+ * This work is licensed under a Creative Commons CCZero 1.0 Universal License.
+ * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
+ */
+
+#include <signal.h>
+#include <stdlib.h>
+
+#ifdef UA_NO_AMALGAMATION
+# include "ua_types.h"
+# include "ua_server.h"
+# include "logger_stdout.h"
+# include "networklayer_tcp.h"
+#else
+# include "open62541.h"
+#endif
+
+/* files nodeset.h and nodeset.c are created from server_nodeset.xml in the /src_generated directory by CMake */
+#include "nodeset.h"
+
+UA_Boolean running = UA_TRUE;
+UA_Logger logger;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = 0;
+}
+
+int main(int argc, char** argv) {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
+    /* initialize the server */
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    logger = Logger_Stdout_new();
+    UA_Server_setLogger(server, logger);
+    UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
+
+    /* create nodes from nodeset */
+    nodeset(server);
+
+    /* start server */
+    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+
+    /* ctrl-c received -> clean up */
+    UA_Server_delete(server);
+    return retval;
+}

+ 43 - 0
examples/server_nodeset.xml

@@ -0,0 +1,43 @@
+<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:s1="http://yourorganisation.org/test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+    <NamespaceUris>
+        <Uri>http://yourorganisation.org/test/</Uri>
+    </NamespaceUris>
+    <Aliases>
+        <Alias Alias="Double">i=11</Alias>
+        <Alias Alias="Organizes">i=35</Alias>
+        <Alias Alias="HasModellingRule">i=37</Alias>
+        <Alias Alias="HasTypeDefinition">i=40</Alias>
+        <Alias Alias="HasSubtype">i=45</Alias>
+        <Alias Alias="HasComponent">i=47</Alias>
+    </Aliases>
+    <UAObjectType NodeId="ns=1;i=1001" BrowseName="1:testType">
+        <DisplayName>testType</DisplayName>
+        <References>
+            <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
+            <Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
+        </References>
+    </UAObjectType>
+    <UAVariable DataType="Double" ParentNodeId="ns=1;i=1001" NodeId="ns=1;i=6001" BrowseName="1:Var1" UserAccessLevel="3" AccessLevel="3">
+        <DisplayName>Var1</DisplayName>
+        <References>
+            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
+            <Reference ReferenceType="HasModellingRule">i=78</Reference>
+            <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>
+            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
+            <Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5001</Reference>
+        </References>
+    </UAVariable>
+</UANodeSet>

+ 3 - 2
examples/server_variable.c

@@ -38,8 +38,9 @@ int main(int argc, char** argv) {
     UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* UA_NODEID_NULL would assign a random free nodeid */
     UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
-                              myIntegerNodeId, parentNodeId, parentReferenceNodeId, NULL);
+
+    UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US",""), UA_LOCALIZEDTEXT("en_US",""),
+                              parentNodeId, parentReferenceNodeId, 0, 0, myIntegerVariant, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);

+ 74 - 15
include/ua_client.h

@@ -63,30 +63,51 @@ UA_DeleteReferencesResponse UA_EXPORT
 
 
 /* Client-Side Macro/Procy functions */
+
+UA_StatusCode UA_EXPORT
+UA_Client_deleteNode(UA_Client *client, UA_NodeId nodeId);
+
+#define UA_CLIENT_DELETENODETYPEALIAS_DECL(TYPE) \
+UA_StatusCode UA_EXPORT UA_Client_delete##TYPE##Node(UA_Client *client, UA_NodeId nodeId);
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(Object)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(Variable)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(ObjectType)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(VariableType)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(DataType)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(Method)
+
+UA_CLIENT_DELETENODETYPEALIAS_DECL(View)
+
 #ifdef ENABLE_METHODCALLS
 UA_CallResponse UA_EXPORT UA_Client_call(UA_Client *client, UA_CallRequest *request);
-UA_StatusCode UA_EXPORT UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
+UA_StatusCode UA_EXPORT UA_Client_callServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
                                                    UA_Int32 inputSize, const UA_Variant *input,
                                                    UA_Int32 *outputSize, UA_Variant **output);
 #endif
     
-UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectNode(  UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+UA_StatusCode UA_EXPORT UA_Client_addObjectNode( UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                 UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                 UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, UA_NodeId *createdNodeId);
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, 
-                                                            UA_NodeId dataType, UA_Variant *value );
+UA_StatusCode UA_EXPORT UA_Client_addVariableNode( UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                   UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                   UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_Variant *value, UA_NodeId *createdNodeId);
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
-                                                            UA_LocalizedText inverseName );
+UA_StatusCode UA_EXPORT UA_Client_addReferenceTypeNode( UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                        UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                        UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
+                                                        UA_LocalizedText inverseName, UA_NodeId *createdNodeId );
 
-UA_AddNodesResponse UA_EXPORT *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                            UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                            UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition);
+UA_StatusCode UA_EXPORT UA_Client_addObjectTypeNode( UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                                     UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                                     UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, UA_Boolean isAbstract, 
+                                                     UA_NodeId *createdNodeId);
 
 
 #ifdef ENABLE_SUBSCRIPTIONS
@@ -102,6 +123,44 @@ UA_StatusCode UA_EXPORT UA_Client_unMonitorItemChanges(UA_Client *client, UA_UIn
                                                        UA_UInt32 monitoredItemId );
 #endif
 
+#ifndef _HAVE_UA_NODEITERATORCALLBACK_D
+#define _HAVE_UA_NODEITERATORCALLBACK_D
+typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
+#endif
+
+UA_StatusCode UA_EXPORT
+UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle);
+
+UA_StatusCode 
+UA_Client_copyBaseAttributes(UA_Client *client, UA_ReadResponse *readResponseSrc, void *dst);
+
+UA_StatusCode 
+UA_Client_appendObjectNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendObjectTypeNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendVariableNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendVariableTypeNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendReferenceTypeNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendViewNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendDataTypeNodeAttributes(UA_Client *client, void *dst);
+UA_StatusCode 
+UA_Client_appendMethodNodeAttributes(UA_Client *client, void *dst);
+
+UA_StatusCode UA_EXPORT 
+UA_Client_getNodeCopy(UA_Client *client, UA_NodeId nodeId, void **copyInto);
+UA_StatusCode UA_EXPORT 
+UA_Client_deleteNodeCopy(UA_Client *client, void **node);
+
+UA_StatusCode UA_EXPORT 
+UA_Client_setAttributeValue(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId, void *value);
+UA_StatusCode UA_EXPORT 
+UA_Client_getAttributeValue(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId, void **value);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 192 - 17
include/ua_server.h

@@ -26,7 +26,7 @@ extern "C" {
 #include "ua_nodeids.h"
 #include "ua_connection.h"
 #include "ua_log.h"
-
+  
 /**
  * @defgroup server Server
  *
@@ -124,17 +124,120 @@ UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 UA_StatusCode UA_EXPORT UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId,
                                                const UA_NodeId refTypeId, const UA_ExpandedNodeId targetId);
 
+/** Deletes a node from the nodestore.
+ *
+ * @param server The server object
+ * @param nodeId ID of the node to be deleted
+
+ * @return Return UA_STATUSCODE_GOOD if the node was deleted or an appropriate errorcode if the node was not found
+ *         or cannot be deleted.
+ */
 UA_StatusCode UA_EXPORT
-UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_QualifiedName browseName, 
-                          UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-                          UA_NodeId *createdNodeId
-                         );
+UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId);
+
+#define UA_SERVER_DELETENODEALIAS_DECL(TYPE) \
+UA_StatusCode UA_EXPORT UA_Server_delete##TYPE##Node(UA_Server *server, UA_NodeId nodeId);
+
+UA_SERVER_DELETENODEALIAS_DECL(Object)
+UA_SERVER_DELETENODEALIAS_DECL(Variable)
+UA_SERVER_DELETENODEALIAS_DECL(ReferenceType)
+UA_SERVER_DELETENODEALIAS_DECL(View)
+UA_SERVER_DELETENODEALIAS_DECL(VariableType)
+UA_SERVER_DELETENODEALIAS_DECL(DataType)
 
+#ifdef ENABLE_METHODCALLS
+UA_SERVER_DELETENODEALIAS_DECL(Method)
+#endif
+
+/** Deletes a copied instance of a node by deallocating it and all its attributes. This assumes that the node was
+ * priorly copied using getNodeCopy. To delete nodes that are located in the nodestore, use UA_Server_deleteNode()
+ * instead.
+ *
+ * @param server The server object
+ * @param node   A copy of any node-type struct created with getNodeCopy; must *not* be managed by the nodestore.
+ * 
+ * @return Return UA_STATUSCODE_GOOD if the node was deleted or an appropriate errorcode if the node was not found
+ *         or cannot be deleted.
+ */
+UA_StatusCode UA_EXPORT 
+UA_Server_deleteNodeCopy(UA_Server *server, void **node);
+
+/** Creates a deep copy of a node located in the nodestore and returns it to the userspace. Note that any manipulation
+ * of this copied node is not reflected by the server, but otherwise not accessible attributes of the node's struct
+ * can be examined in bulk. node->nodeClass can be used to cast the node to a specific node type. Use 
+ * UA_Server_deleteNodeCopy() to deallocate this node.
+ *
+ * @param server The server object
+ * @param nodeId ID of the node copy to be copied
+ * @param copyInto Pointer to a NULL pointer that will hold the copy of the node on a successfull return.
+ * 
+ * @return Return UA_STATUSCODE_GOOD if the node was copied or an appropriate errorcode if the node was not found
+ *         or cannot be copied.
+ */
+UA_StatusCode UA_EXPORT 
+UA_Server_getNodeCopy(UA_Server *server, UA_NodeId nodeId, void **copyInto);
+
+/** A new variable Node with a value passed in variant.
+ *
+ * @param server The server object
+ * @param nodeId        The requested nodeId of the new node. Use the numeric id with i=0 to get a new ID from the server.
+ * @param browseName    The qualified name of this node
+ * @param displayName   The localized text shown when displaying the node
+ * @param description   The localized human readable description
+ * @param parentNodeId  The node under which this node exists ("parent")
+ * @param referenceTypeId Reference type used by the parent to reference this node
+ * @param userWriteMask Bitmask defining the user write permissions
+ * @param writeMask     Bitmask defining the write permissions
+ * @param value         A variant containing the value to be assigned to this node.
+ * @param createdNodeId Pointer to a NULL pointer that will hold the copy of the nodeId on a successfull return.
+ * 
+ * @return Return UA_STATUSCODE_GOOD if the node was created or an appropriate error code if not.
+ */
 UA_StatusCode UA_EXPORT
-UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName,
-                        UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-                        const UA_NodeId typeDefinition,
-                        UA_NodeId *createdNodeId);
+UA_Server_addVariableNode(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_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, 
+                               const UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
+                               const UA_ExpandedNodeId typeDefinition, UA_LocalizedText inverseName, UA_NodeId *createdNodeId );
+
+UA_StatusCode UA_EXPORT
+UA_Server_addObjectTypeNode(UA_Server *server, UA_NodeId nodeId, UA_QualifiedName browseName, 
+                            UA_LocalizedText displayName, UA_LocalizedText description, UA_NodeId parentNodeId, 
+                            UA_NodeId referenceTypeId, UA_UInt32 userWriteMask, UA_UInt32 writeMask, 
+                            UA_ExpandedNodeId typeDefinition, UA_Boolean isAbstract,  
+                            UA_NodeId *createdNodeId );
+
+UA_StatusCode UA_EXPORT 
+UA_Server_addVariableTypeNode(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_Variant *value,  UA_Int32 valueRank, UA_Boolean isAbstract, UA_NodeId *createdNodeId);
+
+UA_StatusCode UA_EXPORT
+UA_Server_addDataTypeNode(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_ExpandedNodeId typeDefinition, UA_Boolean isAbstract, UA_NodeId *createdNodeId);
+
+
+UA_StatusCode UA_EXPORT
+UA_Server_addViewNode(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_ExpandedNodeId typeDefinition, UA_NodeId *createdNodeId);
 
 UA_StatusCode UA_EXPORT
 UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
@@ -147,20 +250,64 @@ UA_Server_AddMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
                                       UA_ExpandedNodeId targetNodeId, UA_NodeId referenceTypeId,
                                       UA_Boolean isforward);
 
-
 #ifdef ENABLE_METHODCALLS
 typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Variant *input,
-                                           UA_Variant *output);
-    
+                                           UA_Variant *output, void *handle);
+/** Creates a serverside method including input- and output variable descriptions
+ * 
+ * @param server The server object.
+ * 
+ * @param browseName BrowseName to be used for the new method.
+ * 
+ * @param nodeId Requested NodeId for the new method. If a numeric ID with i=0 is used, the server will assign a random unused id.
+ * 
+ * @param parentNodeId Parent node containing this method. Note that an ObjectNode needs to reference the method with hasProperty in order for the method to be callable.
+ * 
+ * @param referenceTypeId Reference type ID to be used by the parent to reference the new method.
+ * 
+ * @param method Userspace Method/Function of type UA_MethodCallback to be called when a client invokes the method using the Call Service Set.
+ * 
+ * @param inputArgumentsSize Number of input arguments expected to be passed by a calling client.
+ * 
+ * @param inputArguments Description of input arguments expected to be passed by a calling client.
+ * 
+ * @param outputArgumentsSize Description of output arguments expected to be passed by a calling client.
+ * 
+ * @param outputArguments Description of output arguments expected to be passed by a calling client.
+ * 
+ * @param createdNodeId Actual nodeId of the new method node if UA_StatusCode indicates success. Can be used to determine the random unique ID assigned by the server if i=0 was passed as a nodeId.
+ * 
+ */
 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, void *handle, UA_Int32 inputArgumentsSize, const UA_Argument *inputArguments, 
+                        UA_Int32 outputArgumentsSize, const UA_Argument *outputArguments,
                         UA_NodeId *createdNodeId);
 #endif
 
+#ifndef _HAVE_UA_NODEITERATORCALLBACK_D
+#define _HAVE_UA_NODEITERATORCALLBACK_D
+typedef UA_StatusCode (*UA_NodeIteratorCallback)(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle);
+#endif
+
+/** Iterate over all nodes referenced by parentNodeId by calling the callback function for each child node
+ * 
+ * @param server The server object.
+ *
+ * @param parentNodeId The NodeId of the parent whose references are to be iterated over
+ *
+ * @param callback The function of type UA_NodeIteratorCallback to be called for each referenced child
+ *
+ * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
+ */
+UA_StatusCode UA_EXPORT UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle);
+
+UA_StatusCode UA_EXPORT UA_Server_setAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void *value);
+
+UA_StatusCode UA_EXPORT UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void **value);
+
 /** Jobs describe work that is executed once or repeatedly. */
 typedef struct {
     enum {
@@ -348,6 +495,34 @@ UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, cons
 
 #endif /* external nodestore */
 
+#ifndef _HAVE_UA_INSTANTIONCALLBACK_D
+#define _HAVE_UA_INSTANTIONCALLBACK_D
+typedef UA_StatusCode (*UA_InstantiationCallback)(UA_NodeId objectId, UA_NodeId definitionId, void *handle);
+#endif
+
+typedef struct arrayOfNodeIds_s {
+  UA_Int32  size;
+  UA_NodeId *ids;
+} arrayOfNodeIds;
+
+UA_StatusCode 
+UA_Server_appendInstanceOfSupertype(UA_Server *server, UA_NodeId nodeId, UA_NodeId appendToNodeId, 
+                                    arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, 
+                                    UA_InstantiationCallback callback, arrayOfNodeIds *instantiatedTypes, 
+                                    void *handle);
+
+void UA_Server_addInstanceOf_instatiateChildNode(UA_Server *server, 
+                                                 arrayOfNodeIds *subtypeRefs, arrayOfNodeIds *componentRefs, arrayOfNodeIds *typedefRefs,
+                                                 UA_NodeId objectRoot, UA_InstantiationCallback callback, void *typeDefNode,
+                                                 UA_Boolean instantiateObjects, arrayOfNodeIds *instantiatedTypes, void *handle) 
+;
+UA_StatusCode UA_EXPORT
+UA_Server_addInstanceOf(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_InstantiationCallback callback, void *handle, 
+                        UA_NodeId *createdNodeId) ;
+
 #ifdef __cplusplus
 } // extern "C"
 #endif

+ 843 - 25
src/client/ua_client.c

@@ -1016,7 +1016,7 @@ UA_CallResponse UA_Client_call(UA_Client *client, UA_CallRequest *request) {
     return response;
 }
 
-UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
+UA_StatusCode UA_Client_callServerMethod(UA_Client *client, UA_NodeId objectNodeId, UA_NodeId methodNodeId,
                                          UA_Int32 inputSize, const UA_Variant *input,
                                          UA_Int32 *outputSize, UA_Variant **output) {
     UA_CallRequest request;
@@ -1039,19 +1039,61 @@ UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNode
     rq->inputArgumentsSize = -1;
     UA_CallRequest_deleteMembers(&request);
     UA_StatusCode retval = response.responseHeader.serviceResult;
-    retval |= response.results[0].statusCode;
-
+    
     if(retval == UA_STATUSCODE_GOOD) {
+        retval |= response.results[0].statusCode;
         *output = response.results[0].outputArguments;
         *outputSize = response.results[0].outputArgumentsSize;
         response.results[0].outputArguments = UA_NULL;
         response.results[0].outputArgumentsSize = -1;
     }
+    
     UA_CallResponse_deleteMembers(&response);
     return retval;
 }
 #endif
 
+/* Delete Node */
+#define UA_CLIENT_DELETENODETYPEALIAS(TYPE) \
+UA_StatusCode UA_Client_delete##TYPE##Node(UA_Client *client, UA_NodeId nodeId) { \
+  return UA_Client_deleteNode(client, nodeId);\
+}
+
+UA_StatusCode UA_Client_deleteNode(UA_Client *client, UA_NodeId nodeId) {
+  UA_DeleteNodesRequest *drq = UA_DeleteNodesRequest_new();
+  UA_DeleteNodesResponse drs;
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  
+  drq->nodesToDeleteSize = 1;
+  drq->nodesToDelete = (UA_DeleteNodesItem *) UA_malloc(sizeof(UA_DeleteNodesItem));
+  drq->nodesToDelete[0].deleteTargetReferences = UA_TRUE;
+  UA_NodeId_copy(&nodeId, &drq->nodesToDelete[0].nodeId);
+  drs = UA_Client_deleteNodes(client, drq);
+  
+  if (drs.responseHeader.serviceResult != UA_STATUSCODE_GOOD || drs.resultsSize < 1)
+    return drs.responseHeader.serviceResult;
+  
+  retval = drs.results[0];
+    
+  UA_DeleteNodesRequest_delete(drq);
+  UA_DeleteNodesResponse_deleteMembers(&drs);
+  return retval;
+}
+
+UA_CLIENT_DELETENODETYPEALIAS(Object)
+
+UA_CLIENT_DELETENODETYPEALIAS(Variable)
+
+UA_CLIENT_DELETENODETYPEALIAS(ObjectType)
+
+UA_CLIENT_DELETENODETYPEALIAS(VariableType)
+
+UA_CLIENT_DELETENODETYPEALIAS(DataType)
+
+UA_CLIENT_DELETENODETYPEALIAS(Method)
+
+UA_CLIENT_DELETENODETYPEALIAS(View)
+
 #define ADDNODES_COPYDEFAULTATTRIBUTES(REQUEST,ATTRIBUTES) do {                           \
     ATTRIBUTES.specifiedAttributes = 0;                                                   \
     if(! UA_LocalizedText_copy(&description, &(ATTRIBUTES.description)))                  \
@@ -1066,7 +1108,10 @@ UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNode
     UA_ExpandedNodeId_copy(&parentNodeId, &(REQUEST.nodesToAdd[0].parentNodeId));         \
     UA_NodeId_copy(&referenceTypeId, &(REQUEST.nodesToAdd[0].referenceTypeId));           \
     UA_ExpandedNodeId_copy(&typeDefinition, &(REQUEST.nodesToAdd[0].typeDefinition));     \
-    UA_ExpandedNodeId_copy(&reqId, &(REQUEST.nodesToAdd[0].requestedNewNodeId ));         \
+    UA_ExpandedNodeId reqExpNodeId;                                                       \
+    UA_ExpandedNodeId_init(&reqExpNodeId);                                                \
+    UA_NodeId_copy(&reqId, &reqExpNodeId.nodeId);                                         \
+    UA_ExpandedNodeId_copy(&reqExpNodeId, &(REQUEST.nodesToAdd[0].requestedNewNodeId ));  \
     REQUEST.nodesToAddSize = 1;                                                           \
     } while(0)
     
@@ -1082,10 +1127,11 @@ UA_StatusCode UA_Client_CallServerMethod(UA_Client *client, UA_NodeId objectNode
 } while(0)
     
 /* NodeManagement */
-UA_AddNodesResponse *UA_Client_createObjectNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition ) {
+UA_StatusCode UA_Client_addObjectNode(UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                      UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                      UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, UA_NodeId *createdNodeId) {
     UA_AddNodesRequest adReq;
+    UA_StatusCode retval;
     UA_AddNodesRequest_init(&adReq);
 
     UA_AddNodesResponse *adRes;
@@ -1096,7 +1142,7 @@ UA_AddNodesResponse *UA_Client_createObjectNode(UA_Client *client, UA_ExpandedNo
     UA_ObjectAttributes_init(&vAtt);
     adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
     UA_AddNodesItem_init(adReq.nodesToAdd);
-
+    
     // Default node properties and attributes
     ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
     
@@ -1107,14 +1153,20 @@ UA_AddNodesResponse *UA_Client_createObjectNode(UA_Client *client, UA_ExpandedNo
 
     ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECT); 
     
-    return adRes;
+    if(adRes->responseHeader.serviceResult != UA_STATUSCODE_GOOD || adRes->resultsSize == 0)
+      retval = adRes->responseHeader.serviceResult;
+    retval = adRes->results[0].statusCode;
+    if(createdNodeId != NULL)
+      UA_NodeId_copy(&adRes->results[0].addedNodeId, createdNodeId);
+    UA_AddNodesResponse_deleteMembers(adRes);
+    return retval;
 }
 
-UA_AddNodesResponse *UA_Client_createVariableNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                    UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                    UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, 
-                                                    UA_NodeId dataType, UA_Variant *value) {
+UA_StatusCode UA_Client_addVariableNode(UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                        UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                        UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_Variant *value, UA_NodeId *createdNodeId) {
     UA_AddNodesRequest adReq;
+    UA_StatusCode retval;
     UA_AddNodesRequest_init(&adReq);
     
     UA_AddNodesResponse *adRes;
@@ -1126,6 +1178,10 @@ UA_AddNodesResponse *UA_Client_createVariableNode(UA_Client *client, UA_Expanded
     adReq.nodesToAdd = (UA_AddNodesItem *) UA_AddNodesItem_new();
     UA_AddNodesItem_init(adReq.nodesToAdd);
     
+    UA_ExpandedNodeId typeDefinition;
+    UA_ExpandedNodeId_init(&typeDefinition);
+    if (value != UA_NULL)
+      UA_NodeId_copy(&value->type->typeId, &typeDefinition.nodeId);
     // Default node properties and attributes
     ADDNODES_COPYDEFAULTATTRIBUTES(adReq, vAtt);
     
@@ -1149,18 +1205,25 @@ UA_AddNodesResponse *UA_Client_createVariableNode(UA_Client *client, UA_Expanded
         //vAtt.arrayDimensionsSize  = value->arrayDimensionsSize;
         //vAtt.arrayDimensions      = NULL;
     }
-    UA_NodeId_copy(&dataType, &(vAtt.dataType));
+    UA_NodeId_copy(&value->type->typeId, &(vAtt.dataType));
     
     ADDNODES_PACK_AND_SEND(adReq,vAtt,VARIABLE);
     
-    return adRes;
+    if(adRes->responseHeader.serviceResult != UA_STATUSCODE_GOOD || adRes->resultsSize == 0)
+      retval = adRes->responseHeader.serviceResult;
+    retval = adRes->results[0].statusCode;
+    if(createdNodeId != NULL)
+      UA_NodeId_copy(&adRes->results[0].addedNodeId, createdNodeId);
+    UA_AddNodesResponse_deleteMembers(adRes);
+    return retval;
 }
 
-UA_AddNodesResponse *UA_Client_createReferenceTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                  UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                  UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
-                                                  UA_LocalizedText inverseName ) {
+UA_StatusCode UA_Client_addReferenceTypeNode( UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                              UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                              UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition,
+                                              UA_LocalizedText inverseName, UA_NodeId *createdNodeId ) {
     UA_AddNodesRequest adReq;
+    UA_StatusCode retval;
     UA_AddNodesRequest_init(&adReq);
     
     UA_AddNodesResponse *adRes;
@@ -1187,13 +1250,21 @@ UA_AddNodesResponse *UA_Client_createReferenceTypeNode(UA_Client *client, UA_Exp
     
     ADDNODES_PACK_AND_SEND(adReq,vAtt,REFERENCETYPE);
     
-    return adRes;
+    if(adRes->responseHeader.serviceResult != UA_STATUSCODE_GOOD || adRes->resultsSize == 0)
+      retval = adRes->responseHeader.serviceResult;
+    retval = adRes->results[0].statusCode;
+    if(createdNodeId != NULL)
+      UA_NodeId_copy(&adRes->results[0].addedNodeId, createdNodeId);
+    UA_AddNodesResponse_deleteMembers(adRes);
+    return retval;
 }
 
-UA_AddNodesResponse *UA_Client_createObjectTypeNode(UA_Client *client, UA_ExpandedNodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
-                                                    UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
-                                                    UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition) {
+UA_StatusCode UA_Client_addObjectTypeNode(UA_Client *client, UA_NodeId reqId, UA_QualifiedName browseName, UA_LocalizedText displayName, 
+                                          UA_LocalizedText description, UA_ExpandedNodeId parentNodeId, UA_NodeId referenceTypeId,
+                                          UA_UInt32 userWriteMask, UA_UInt32 writeMask, UA_ExpandedNodeId typeDefinition, UA_Boolean isAbstract,
+                                          UA_NodeId *createdNodeId) {
     UA_AddNodesRequest adReq;
+    UA_StatusCode retval;
     UA_AddNodesRequest_init(&adReq);
     
     UA_AddNodesResponse *adRes;
@@ -1210,11 +1281,758 @@ UA_AddNodesResponse *UA_Client_createObjectTypeNode(UA_Client *client, UA_Expand
     
     // Specific to referencetypes
     adReq.nodesToAdd[0].nodeClass = UA_NODECLASS_OBJECTTYPE;
-    vAtt.isAbstract = UA_FALSE;
+    vAtt.isAbstract = isAbstract;
     vAtt.specifiedAttributes |= UA_NODEATTRIBUTESMASK_ISABSTRACT;
     
     
     ADDNODES_PACK_AND_SEND(adReq,vAtt,OBJECTTYPE);
     
-    return adRes;
+    if(adRes->responseHeader.serviceResult != UA_STATUSCODE_GOOD || adRes->resultsSize == 0)
+      retval = adRes->responseHeader.serviceResult;
+    retval = adRes->results[0].statusCode;
+    if(createdNodeId != NULL)
+      UA_NodeId_copy(&adRes->results[0].addedNodeId, createdNodeId);
+    UA_AddNodesResponse_deleteMembers(adRes);
+    return retval;
+}
+
+UA_StatusCode 
+UA_Client_forEachChildNodeCall(UA_Client *client, UA_NodeId parentNodeId, UA_NodeIteratorCallback callback, void *handle) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  
+  UA_BrowseRequest  brq;
+  UA_BrowseRequest_init(&brq);
+  UA_BrowseResponse brs;
+  UA_BrowseResponse_init(&brs);
+  brq.nodesToBrowseSize = 1;
+  brq.requestedMaxReferencesPerNode = 0;
+  brq.nodesToBrowse = UA_BrowseDescription_new();
+  brq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_BOTH;
+  brq.nodesToBrowse[0].includeSubtypes = UA_TRUE;
+  UA_NodeId_copy(&parentNodeId, &brq.nodesToBrowse[0].nodeId);
+  brq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
+  brs = UA_Client_browse(client, &brq);
+  
+  UA_BrowseRequest_deleteMembers(&brq);
+  if (brs.responseHeader.serviceResult != UA_STATUSCODE_GOOD) {
+    UA_BrowseResponse_deleteMembers(&brq);
+    return brs.responseHeader.serviceResult;
+  }
+  if (brs.resultsSize < 1) {
+    UA_BrowseResponse_deleteMembers(&brq);
+    return UA_STATUSCODE_GOOD;
+  }
+  if (brs.results[0].statusCode != UA_STATUSCODE_GOOD) {
+    UA_BrowseResponse_deleteMembers(&brq);
+    return brs.results[0].statusCode;
+  }
+  
+  UA_Boolean isInverse;
+  UA_NodeId *childId = UA_NodeId_new();
+  UA_NodeId *refTypeId = UA_NodeId_new();
+  for (int i = 0; i < brs.results[0].referencesSize; i++) {
+    isInverse = UA_FALSE;
+    if (brs.results[0].references[i].isForward == UA_FALSE)
+      isInverse = UA_TRUE;
+    UA_NodeId_copy(&brs.results[0].references[i].nodeId.nodeId, childId);
+    UA_NodeId_copy(&brs.results[0].references[i].referenceTypeId, refTypeId);
+    //UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId
+    callback(*childId, isInverse, *refTypeId, handle);
+    
+    UA_NodeId_deleteMembers(childId);
+    UA_NodeId_deleteMembers(refTypeId);
+  }
+  UA_NodeId_delete(childId);
+  UA_NodeId_delete(refTypeId);
+  
+  UA_BrowseResponse_deleteMembers(&brs);
+  return retval;
+}
+
+#include "server/ua_nodes.h"
+
+UA_StatusCode 
+UA_Client_copyBaseAttributes(UA_Client *client, UA_ReadResponse *readResponseSrc, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_Node *target = (UA_Node *) dst;
+  if (readResponseSrc->results[0].value.data != UA_NULL)
+    retval |= UA_NodeId_copy((UA_NodeId *) readResponseSrc->results[0].value.data, &target->nodeId);
+  if (readResponseSrc->results[1].value.data != UA_NULL)
+    retval |= UA_NodeClass_copy((UA_NodeClass *) readResponseSrc->results[1].value.data, &target->nodeClass);
+  if (readResponseSrc->results[2].value.data != UA_NULL)
+    retval |= UA_QualifiedName_copy((UA_QualifiedName *) readResponseSrc->results[2].value.data, &target->browseName);
+  if (readResponseSrc->results[3].value.data != UA_NULL)
+    retval |= UA_LocalizedText_copy((UA_LocalizedText *) readResponseSrc->results[3].value.data, &target->displayName);
+  if (readResponseSrc->results[4].value.data != UA_NULL)
+    retval |= UA_LocalizedText_copy((UA_LocalizedText *) readResponseSrc->results[4].value.data, &target->description);
+  if (readResponseSrc->results[5].value.data != UA_NULL)  
+    retval |= UA_UInt32_copy((UA_UInt32 *) readResponseSrc->results[5].value.data, &target->writeMask);
+  if (readResponseSrc->results[6].value.data != UA_NULL)  
+    retval |= UA_UInt32_copy((UA_UInt32 *) readResponseSrc->results[6].value.data, &target->userWriteMask);
+  
+  target->referencesSize = 0;
+  target->references = UA_NULL;
+  
+  UA_BrowseRequest *brq = UA_BrowseRequest_new();
+  UA_BrowseResponse brs;
+  UA_BrowseResult_init(&brs);
+  brq->nodesToBrowseSize = 1;
+  brq->requestedMaxReferencesPerNode = 0;
+  brq->nodesToBrowse = UA_BrowseDescription_new();
+  brq->nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_BOTH;
+  brq->nodesToBrowse[0].includeSubtypes = UA_TRUE;
+  UA_NodeId_copy((UA_NodeId *) readResponseSrc->results[0].value.data, &brq->nodesToBrowse[0].nodeId);
+  brq->nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
+  brs = UA_Client_browse(client, brq);
+  UA_BrowseRequest_delete(brq);
+  
+  if (brs.responseHeader.serviceResult!= UA_STATUSCODE_GOOD || brs.resultsSize != 1) {
+    UA_BrowseResponse_deleteMembers(&brs);
+    return retval;
+  }
+  if (brs.results[0].statusCode != UA_STATUSCODE_GOOD) {
+    UA_BrowseResponse_deleteMembers(&brs);
+    return retval;
+  }
+  
+  /* typedef struct {
+   U A_NodeId referenceTypeId; *
+   UA_Boolean isInverse;
+   UA_ExpandedNodeId targetId; */
+  target->referencesSize = brs.results[0].referencesSize;
+  target->references = (UA_ReferenceNode *) UA_malloc(sizeof(UA_ReferenceNode) *  brs.results[0].referencesSize);
+  for (int i=0; i<brs.results[0].referencesSize; i++) {
+    target->references[i].isInverse = UA_FALSE;
+    if (brs.results[0].references->isForward == UA_FALSE)
+      target->references[i].isInverse = UA_TRUE;
+    UA_NodeId_copy(&brs.results[0].references->nodeId.nodeId, &target->references[i].referenceTypeId); 
+    UA_ExpandedNodeId_init(&target->references[i].targetId);
+    UA_NodeId_copy(&brs.results[0].references->referenceTypeId, &target->references[i].targetId.nodeId);
+  }
+  
+  UA_BrowseResponse_deleteMembers(&brs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendObjectNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_ObjectNode *target = (UA_ObjectNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+      UA_ATTRIBUTEID_EVENTNOTIFIER           = 12
+   */
+  rrq->nodesToReadSize = 1;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+12;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Byte_copy((UA_Byte *) rrs.results[0].value.data, &target->eventNotifier);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendObjectTypeNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_ObjectTypeNode *target = (UA_ObjectTypeNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+   * UA_ATTRIBUTEID_ISABSTRACT              = 8,
+   */
+  rrq->nodesToReadSize = 1;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+8;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->isAbstract);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendVariableNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_VariableNode *target = (UA_VariableNode *) dst;
+  
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+   /* Read node attributes:    
+      UA_ATTRIBUTEID_VALUE                   = 13,
+      UA_ATTRIBUTEID_DATATYPE                = 14, // Req. but not used (is in variant)
+      UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16, // Req. but not used (is in variant)
+      
+      UA_ATTRIBUTEID_VALUERANK               = 15,
+      UA_ATTRIBUTEID_ACCESSLEVEL             = 17,
+      UA_ATTRIBUTEID_USERACCESSLEVEL         = 18,
+      UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL = 19,
+      UA_ATTRIBUTEID_HISTORIZING             = 20,
+   */
+  rrq->nodesToReadSize = 8;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+13;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+
+  if (rrs.results[0].value.data != NULL)
+    UA_Variant_copy((UA_Variant *) &rrs.results[0].value, &target->value.variant);
+  if (rrs.results[3].value.data != NULL)
+    UA_Int32_copy((UA_Int32 *) rrs.results[3].value.data, &target->valueRank);
+  if (rrs.results[4].value.data != NULL)
+    UA_Byte_copy((UA_Byte *) rrs.results[4].value.data, &target->accessLevel);
+  if (rrs.results[5].value.data != NULL)
+    UA_Byte_copy((UA_Byte *) rrs.results[5].value.data, &target->userAccessLevel);
+  if (rrs.results[6].value.data != NULL)
+    UA_Double_copy((UA_Double *) rrs.results[6].value.data, &target->minimumSamplingInterval);
+  if (rrs.results[7].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[7].value.data, &target->historizing);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendVariableTypeNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_VariableTypeNode *target = (UA_VariableTypeNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+      UA_ATTRIBUTEID_ISABSTRACT              = 8,
+      UA_ATTRIBUTEID_VALUE                   = 13,
+      UA_ATTRIBUTEID_DATATYPE                = 14, // Req. but not used (is in variant)
+      UA_ATTRIBUTEID_ARRAYDIMENSIONS         = 16, // Req. but not used (is in variant)
+      
+      UA_ATTRIBUTEID_VALUERANK               = 15,
+      
+   */
+  rrq->nodesToReadSize = 8;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+13;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrq->nodesToRead[0].attributeId = UA_ATTRIBUTEID_ISABSTRACT;
+  
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->isAbstract);
+  if (rrs.results[1].value.data != NULL)
+    UA_Variant_copy((UA_Variant *) &rrs.results[1].value, &target->value.variant);
+  if (rrs.results[2].value.data != NULL)
+    UA_Int32_copy((UA_Int32 *) rrs.results[2].value.data, &target->valueRank);
+  
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendReferenceTypeNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_ReferenceTypeNode *target = (UA_ReferenceTypeNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+      UA_ATTRIBUTEID_SYMMETRIC               = 9,
+      UA_ATTRIBUTEID_INVERSENAME             = 10,
+   *  UA_ATTRIBUTEID_ISABSTRACT              = 8,
+   */
+  rrq->nodesToReadSize = 3;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+8;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->isAbstract);
+  if (rrs.results[1].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[1].value.data, &target->symmetric);
+  if (rrs.results[2].value.data != NULL)
+    UA_LocalizedText_copy((UA_LocalizedText *) rrs.results[2].value.data, &target->inverseName);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendViewNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_ViewNode *target = (UA_ViewNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+      UA_ATTRIBUTEID_CONTAINSNOLOOPS         = 11,
+      UA_ATTRIBUTEID_EVENTNOTIFIER           = 12,
+   */
+  rrq->nodesToReadSize = 2;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+11;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->containsNoLoops);
+  if (rrs.results[1].value.data != NULL)
+    UA_Byte_copy((UA_Byte *) rrs.results[1].value.data, &target->eventNotifier);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_appendDataTypeNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_DataTypeNode *target = (UA_DataTypeNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+   * UA_ATTRIBUTEID_ISABSTRACT              = 8,
+   */
+  rrq->nodesToReadSize = 1;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+8;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->isAbstract);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode UA_Client_appendMethodNodeAttributes(UA_Client *client, void *dst) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_MethodNode *target = (UA_MethodNode *) dst;
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  
+  /* Read node attributes:
+   * UA_ATTRIBUTEID_USEREXECUTABLE          = 22
+   * UA_ATTRIBUTEID_EXECUTABLE              = 21,
+   */
+  rrq->nodesToReadSize = 2;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+21;
+    UA_NodeId_copy(&target->nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  
+  if (rrs.results[0].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[0].value.data, &target->executable);
+  if (rrs.results[1].value.data != NULL)
+    UA_Boolean_copy((UA_Boolean *) rrs.results[1].value.data, &target->userExecutable);
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode 
+UA_Client_getNodeCopy(UA_Client *client, UA_NodeId nodeId, void **copyInto) {
+  UA_ReadRequest  *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  *copyInto = UA_NULL;
+  /* Read default node attributes:
+      UA_ATTRIBUTEID_NODEID                  = 1,
+      UA_ATTRIBUTEID_NODECLASS               = 2,
+      UA_ATTRIBUTEID_BROWSENAME              = 3,
+      UA_ATTRIBUTEID_DISPLAYNAME             = 4,
+      UA_ATTRIBUTEID_DESCRIPTION             = 5,
+      UA_ATTRIBUTEID_WRITEMASK               = 6,
+      UA_ATTRIBUTEID_USERWRITEMASK           = 7,
+   */
+  rrq->nodesToReadSize = 7;
+  rrq->nodesToRead = (UA_ReadValueId*) UA_malloc(sizeof(UA_ReadValueId) * rrq->nodesToReadSize);
+  for(int i = 0; i < rrq->nodesToReadSize; i++) {
+    UA_ReadValueId_init(&rrq->nodesToRead[i]);
+    rrq->nodesToRead[i].attributeId = (UA_UInt32 ) i+1;
+    UA_NodeId_copy(&nodeId, &rrq->nodesToRead[i].nodeId);
+  }
+  rrs = UA_Client_read(client, rrq);
+  if (rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.resultsSize != rrq->nodesToReadSize)
+    return rrs.responseHeader.serviceResult;
+  UA_ReadRequest_delete(rrq);
+  
+  UA_UInt32 *nodeClass = rrs.results[1].value.data;
+  switch(*nodeClass) {
+    case UA_NODECLASS_OBJECT:
+      *copyInto = UA_ObjectNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendObjectNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_OBJECTTYPE:
+      *copyInto = UA_ObjectTypeNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendObjectTypeNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_VARIABLE:
+      *copyInto = UA_VariableNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendVariableNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_VARIABLETYPE:
+      *copyInto = UA_VariableTypeNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendVariableTypeNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_REFERENCETYPE:
+      *copyInto = UA_ReferenceTypeNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendReferenceTypeNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_METHOD:
+      *copyInto = UA_MethodNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendMethodNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_VIEW:
+      *copyInto = UA_ViewNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendViewNodeAttributes(client, *copyInto);
+      break;
+    case UA_NODECLASS_DATATYPE:
+      *copyInto = UA_DataTypeNode_new();
+      retval |= UA_Client_copyBaseAttributes(client, &rrs, *copyInto);
+      retval |= UA_Client_appendDataTypeNodeAttributes(client, *copyInto);
+      break;
+    default:
+      UA_ReadRequest_delete(rrq);
+      UA_ReadResponse_deleteMembers(&rrs);
+      return UA_STATUSCODE_BADNODECLASSINVALID;
+  }
+ 
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
+}
+
+UA_StatusCode UA_Client_deleteNodeCopy(UA_Client *client, void **node) {
+  return UA_Server_deleteNodeCopy(UA_NULL, node);
+}
+
+#define SETATTRIBUTE_COPYTYPEVALUE(TYPE)                                                       \
+wrq->nodesToWrite[0].value.value.data = UA_##TYPE##_new();                                     \
+UA_##TYPE##_copy((UA_##TYPE *) value, (UA_##TYPE *) wrq->nodesToWrite[0].value.value.data);
+
+UA_StatusCode 
+UA_Client_setAttributeValue(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId, void *value) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  
+  UA_WriteRequest *wrq = UA_WriteRequest_new();
+  UA_WriteResponse wrs;
+  UA_WriteResponse_init(&wrs);
+  wrq->nodesToWriteSize = 1;
+  wrq->nodesToWrite = UA_WriteValue_new();
+  UA_NodeId_copy(&nodeId, &wrq->nodesToWrite[0].nodeId);
+  wrq->nodesToWrite[0].attributeId = attributeId;
+  wrq->nodesToWrite[0].value.hasValue = UA_TRUE;
+  wrq->nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA;
+  
+  switch(attributeId) {
+    case UA_ATTRIBUTEID_NODEID:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_NODECLASS:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_BROWSENAME:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_QUALIFIEDNAME];
+      SETATTRIBUTE_COPYTYPEVALUE(QualifiedName);
+      break;
+    case UA_ATTRIBUTEID_DISPLAYNAME:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+      SETATTRIBUTE_COPYTYPEVALUE(LocalizedText);
+      break;
+    case UA_ATTRIBUTEID_DESCRIPTION:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+      SETATTRIBUTE_COPYTYPEVALUE(LocalizedText);
+      break;
+    case UA_ATTRIBUTEID_WRITEMASK:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_UINT32];
+      SETATTRIBUTE_COPYTYPEVALUE(UInt32);
+      break;
+    case UA_ATTRIBUTEID_USERWRITEMASK:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_UINT32];
+      SETATTRIBUTE_COPYTYPEVALUE(UInt32);
+      break;    
+    case UA_ATTRIBUTEID_ISABSTRACT:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    case UA_ATTRIBUTEID_SYMMETRIC:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    case UA_ATTRIBUTEID_INVERSENAME:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+      SETATTRIBUTE_COPYTYPEVALUE(LocalizedText);
+      break;
+    case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    case UA_ATTRIBUTEID_EVENTNOTIFIER:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BYTE];
+      SETATTRIBUTE_COPYTYPEVALUE(Byte); 
+      break;
+    case UA_ATTRIBUTEID_VALUE:
+      UA_Variant_copy((UA_Variant *) value, (UA_Variant *) &wrq->nodesToWrite[0].value.value);
+      break;
+    case UA_ATTRIBUTEID_DATATYPE:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_VALUERANK:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_ACCESSLEVEL:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_UINT32];
+      SETATTRIBUTE_COPYTYPEVALUE(Byte);
+      break;
+    case UA_ATTRIBUTEID_USERACCESSLEVEL:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_UINT32];
+      SETATTRIBUTE_COPYTYPEVALUE(Byte);
+      break;
+    case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_DOUBLE];
+      SETATTRIBUTE_COPYTYPEVALUE(Double);
+      break;
+    case UA_ATTRIBUTEID_HISTORIZING:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    case UA_ATTRIBUTEID_EXECUTABLE:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    case UA_ATTRIBUTEID_USEREXECUTABLE:
+      wrq->nodesToWrite[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+      SETATTRIBUTE_COPYTYPEVALUE(Boolean);
+      break;
+    default:
+      UA_WriteRequest_delete(wrq);
+      UA_WriteResponse_deleteMembers(&wrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+  }
+  
+  wrs = UA_Client_write(client, wrq);
+  UA_WriteRequest_delete(wrq);
+  
+  if (wrs.responseHeader.serviceResult)
+    return wrs.responseHeader.serviceResult;
+  if (wrs.resultsSize != 1)
+    return wrs.responseHeader.serviceResult;
+  
+  retval = wrs.results[0];
+  
+  UA_WriteResponse_deleteMembers(&wrs);
+  return retval;
+}
+
+#define GETATTRIBUTE_COPYTYPEVALUE(TYPE)                                           \
+if (rrs.results[0].hasValue == UA_TRUE) {                                          \
+  *value = (void *) UA_##TYPE##_new();                                             \
+  UA_##TYPE##_copy((UA_##TYPE *) rrs.results[0].value.data, (UA_##TYPE *) *value  ); \
+}
+  
+UA_StatusCode UA_EXPORT 
+UA_Client_getAttributeValue(UA_Client *client, UA_NodeId nodeId, UA_AttributeId attributeId, void **value) {
+  UA_StatusCode retval = UA_STATUSCODE_GOOD;
+  UA_ReadRequest *rrq = UA_ReadRequest_new();
+  UA_ReadResponse rrs;
+  UA_ReadResponse_init(&rrs);
+  *value = UA_NULL;
+  
+  rrq->timestampsToReturn = UA_TIMESTAMPSTORETURN_NEITHER;
+  rrq->nodesToReadSize = 1;
+  rrq->nodesToRead = UA_ReadValueId_new();
+  rrq->nodesToRead[0].attributeId = attributeId;
+  UA_NodeId_copy(&nodeId, &rrq->nodesToRead[0].nodeId);
+  
+  rrs = UA_Client_read(client, rrq);
+  UA_ReadRequest_delete(rrq);
+  
+  if(rrs.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
+    return rrs.responseHeader.serviceResult;
+  if(rrs.resultsSize < 1)
+    return rrs.responseHeader.serviceResult;
+  if (rrs.results[0].status != UA_STATUSCODE_GOOD)
+    return rrs.results[0].status;
+  
+  switch(attributeId) {
+    case UA_ATTRIBUTEID_NODEID:
+      GETATTRIBUTE_COPYTYPEVALUE(NodeId)
+      break;
+    case UA_ATTRIBUTEID_NODECLASS:
+      GETATTRIBUTE_COPYTYPEVALUE(NodeClass)
+      break;
+    case UA_ATTRIBUTEID_BROWSENAME:
+      GETATTRIBUTE_COPYTYPEVALUE(QualifiedName)
+      break;
+    case UA_ATTRIBUTEID_DISPLAYNAME:
+      GETATTRIBUTE_COPYTYPEVALUE(LocalizedText)
+      break;
+    case UA_ATTRIBUTEID_DESCRIPTION:
+      GETATTRIBUTE_COPYTYPEVALUE(LocalizedText)
+      break;
+    case UA_ATTRIBUTEID_WRITEMASK:
+      GETATTRIBUTE_COPYTYPEVALUE(UInt32)
+      break;
+    case UA_ATTRIBUTEID_USERWRITEMASK:
+      GETATTRIBUTE_COPYTYPEVALUE(UInt32)
+      break;    
+    case UA_ATTRIBUTEID_ISABSTRACT:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    case UA_ATTRIBUTEID_SYMMETRIC:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    case UA_ATTRIBUTEID_INVERSENAME:
+      GETATTRIBUTE_COPYTYPEVALUE(LocalizedText)
+      break;
+    case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    case UA_ATTRIBUTEID_EVENTNOTIFIER:
+      GETATTRIBUTE_COPYTYPEVALUE(Byte)
+      break;
+    case UA_ATTRIBUTEID_VALUE:
+      GETATTRIBUTE_COPYTYPEVALUE(Variant)
+      break;
+    case UA_ATTRIBUTEID_DATATYPE:
+      *value = UA_NULL;
+      UA_ReadResponse_deleteMembers(&rrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_VALUERANK:
+      *value = UA_NULL;
+      UA_ReadResponse_deleteMembers(&rrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+      *value = UA_NULL;
+      UA_ReadResponse_deleteMembers(&rrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+      break;
+    case UA_ATTRIBUTEID_ACCESSLEVEL:
+      GETATTRIBUTE_COPYTYPEVALUE(Byte)
+      break;
+    case UA_ATTRIBUTEID_USERACCESSLEVEL:
+      GETATTRIBUTE_COPYTYPEVALUE(Byte)
+      break;
+    case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+      GETATTRIBUTE_COPYTYPEVALUE(Double)
+      break;
+    case UA_ATTRIBUTEID_HISTORIZING:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    case UA_ATTRIBUTEID_EXECUTABLE:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    case UA_ATTRIBUTEID_USEREXECUTABLE:
+      GETATTRIBUTE_COPYTYPEVALUE(Boolean)
+      break;
+    default:
+      *value = UA_NULL;
+      UA_ReadResponse_deleteMembers(&rrs);
+      return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+  }
+  
+  UA_ReadResponse_deleteMembers(&rrs);
+  return retval;
 }

+ 3 - 0
src/server/ua_nodes.c

@@ -239,6 +239,7 @@ void UA_MethodNode_init(UA_MethodNode *p) {
     p->executable = UA_FALSE;
     p->userExecutable = UA_FALSE;
 #ifdef ENABLE_METHODCALLS
+    p->methodHandle        = UA_NULL;
     p->attachedMethod      = UA_NULL;
 #endif
 }
@@ -260,6 +261,7 @@ void UA_MethodNode_deleteMembers(UA_MethodNode *p) {
 void UA_MethodNode_delete(UA_MethodNode *p) {
     UA_MethodNode_deleteMembers(p);
 #ifdef ENABLE_METHODCALLS
+    p->methodHandle   = UA_NULL;
     p->attachedMethod = UA_NULL;
 #endif
     UA_free(p);
@@ -272,6 +274,7 @@ UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) {
     dst->executable = src->executable;
     dst->userExecutable = src->userExecutable;
 #ifdef ENABLE_METHODCALLS
+    dst->methodHandle  = src->methodHandle;
     dst->attachedMethod = src->attachedMethod;
 #endif
     return retval;

+ 1 - 0
src/server/ua_nodes.h

@@ -109,6 +109,7 @@ typedef struct {
     UA_Boolean executable;
     UA_Boolean userExecutable;
 #ifdef ENABLE_METHODCALLS
+    void *methodHandle;
     UA_MethodCallback attachedMethod;
 #endif
 } UA_MethodNode;

+ 22 - 22
src/server/ua_server.c

@@ -271,8 +271,8 @@ static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp,
 
 static void copyNames(UA_Node *node, char *name) {
     node->browseName = UA_QUALIFIEDNAME_ALLOC(0, name);
-    node->displayName = UA_LOCALIZEDTEXT_ALLOC("", name);
-    node->description = UA_LOCALIZEDTEXT_ALLOC("", name);
+    node->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", name);
+    node->description = UA_LOCALIZEDTEXT_ALLOC("en_US", name);
 }
 
 static void addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_Int32 parent) {
@@ -351,7 +351,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     server->description.applicationUri = UA_STRING_ALLOC(server->config.Application_applicationURI);
     server->description.discoveryUrlsSize = 0;
 
-    server->description.applicationName = UA_LOCALIZEDTEXT_ALLOC("", server->config.Application_applicationName);
+    server->description.applicationName = UA_LOCALIZEDTEXT_ALLOC("en_US", server->config.Application_applicationName);
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
@@ -477,13 +477,13 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     references->nodeId.identifier.numeric = UA_NS0ID_REFERENCES;
     references->isAbstract = UA_TRUE;
     references->symmetric  = UA_TRUE;
-    references->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "References");
+    references->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "References");
     /* The reference to root is later inserted */
     UA_NodeStore_insert(server->nodestore, (UA_Node*)references, UA_NULL);
 
     UA_ReferenceTypeNode *hassubtype = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hassubtype, "HasSubtype");
-    hassubtype->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "HasSupertype");
+    hassubtype->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "HasSupertype");
     hassubtype->nodeId.identifier.numeric = UA_NS0ID_HASSUBTYPE;
     hassubtype->isAbstract = UA_FALSE;
     hassubtype->symmetric  = UA_FALSE;
@@ -517,7 +517,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)organizes, "Organizes");
-    organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
+    organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OrganizedBy");
     organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
     organizes->isAbstract = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
@@ -526,7 +526,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseventsource, "HasEventSource");
-    haseventsource->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "EventSourceOf");
+    haseventsource->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "EventSourceOf");
     haseventsource->nodeId.identifier.numeric = UA_NS0ID_HASEVENTSOURCE;
     haseventsource->isAbstract = UA_FALSE;
     haseventsource->symmetric  = UA_FALSE;
@@ -535,7 +535,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodellingrule, "HasModellingRule");
-    hasmodellingrule->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ModellingRuleOf");
+    hasmodellingrule->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ModellingRuleOf");
     hasmodellingrule->nodeId.identifier.numeric = UA_NS0ID_HASMODELLINGRULE;
     hasmodellingrule->isAbstract = UA_FALSE;
     hasmodellingrule->symmetric  = UA_FALSE;
@@ -544,7 +544,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasencoding, "HasEncoding");
-    hasencoding->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "EncodingOf");
+    hasencoding->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "EncodingOf");
     hasencoding->nodeId.identifier.numeric = UA_NS0ID_HASENCODING;
     hasencoding->isAbstract = UA_FALSE;
     hasencoding->symmetric  = UA_FALSE;
@@ -552,7 +552,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasdescription, "HasDescription");
-    hasdescription->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "DescriptionOf");
+    hasdescription->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "DescriptionOf");
     hasdescription->nodeId.identifier.numeric = UA_NS0ID_HASDESCRIPTION;
     hasdescription->isAbstract = UA_FALSE;
     hasdescription->symmetric  = UA_FALSE;
@@ -561,7 +561,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hastypedefinition, "HasTypeDefinition");
-    hastypedefinition->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "TypeDefinitionOf");
+    hastypedefinition->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "TypeDefinitionOf");
     hastypedefinition->nodeId.identifier.numeric = UA_NS0ID_HASTYPEDEFINITION;
     hastypedefinition->isAbstract = UA_FALSE;
     hastypedefinition->symmetric  = UA_FALSE;
@@ -570,7 +570,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)generatesevent, "GeneratesEvent");
-    generatesevent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "GeneratedBy");
+    generatesevent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "GeneratedBy");
     generatesevent->nodeId.identifier.numeric = UA_NS0ID_GENERATESEVENT;
     generatesevent->isAbstract = UA_FALSE;
     generatesevent->symmetric  = UA_FALSE;
@@ -592,7 +592,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasproperty = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasproperty, "HasProperty");
-    hasproperty->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "PropertyOf");
+    hasproperty->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "PropertyOf");
     hasproperty->nodeId.identifier.numeric = UA_NS0ID_HASPROPERTY;
     hasproperty->isAbstract = UA_FALSE;
     hasproperty->symmetric  = UA_FALSE;
@@ -601,7 +601,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascomponent, "HasComponent");
-    hascomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ComponentOf");
+    hascomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ComponentOf");
     hascomponent->nodeId.identifier.numeric = UA_NS0ID_HASCOMPONENT;
     hascomponent->isAbstract = UA_FALSE;
     hascomponent->symmetric  = UA_FALSE;
@@ -610,7 +610,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasnotifier, "HasNotifier");
-    hasnotifier->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "NotifierOf");
+    hasnotifier->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "NotifierOf");
     hasnotifier->nodeId.identifier.numeric = UA_NS0ID_HASNOTIFIER;
     hasnotifier->isAbstract = UA_FALSE;
     hasnotifier->symmetric  = UA_FALSE;
@@ -619,7 +619,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasorderedcomponent, "HasOrderedComponent");
-    hasorderedcomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrderedComponentOf");
+    hasorderedcomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OrderedComponentOf");
     hasorderedcomponent->nodeId.identifier.numeric = UA_NS0ID_HASORDEREDCOMPONENT;
     hasorderedcomponent->isAbstract = UA_FALSE;
     hasorderedcomponent->symmetric  = UA_FALSE;
@@ -628,7 +628,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodelparent, "HasModelParent");
-    hasmodelparent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ModelParentOf");
+    hasmodelparent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ModelParentOf");
     hasmodelparent->nodeId.identifier.numeric = UA_NS0ID_HASMODELPARENT;
     hasmodelparent->isAbstract = UA_FALSE;
     hasmodelparent->symmetric  = UA_FALSE;
@@ -637,7 +637,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)fromstate, "FromState");
-    fromstate->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ToTransition");
+    fromstate->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ToTransition");
     fromstate->nodeId.identifier.numeric = UA_NS0ID_FROMSTATE;
     fromstate->isAbstract = UA_FALSE;
     fromstate->symmetric  = UA_FALSE;
@@ -645,7 +645,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)tostate, "ToState");
-    tostate->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "FromTransition");
+    tostate->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "FromTransition");
     tostate->nodeId.identifier.numeric = UA_NS0ID_TOSTATE;
     tostate->isAbstract = UA_FALSE;
     tostate->symmetric  = UA_FALSE;
@@ -653,7 +653,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascause, "HasCause");
-    hascause->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "MayBeCausedBy");
+    hascause->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "MayBeCausedBy");
     hascause->nodeId.identifier.numeric = UA_NS0ID_HASCAUSE;
     hascause->isAbstract = UA_FALSE;
     hascause->symmetric  = UA_FALSE;
@@ -661,7 +661,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     
     UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseffect, "HasEffect");
-    haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "MayBeEffectedBy");
+    haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "MayBeEffectedBy");
     haseffect->nodeId.identifier.numeric = UA_NS0ID_HASEFFECT;
     haseffect->isAbstract = UA_FALSE;
     haseffect->symmetric  = UA_FALSE;
@@ -669,7 +669,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
 
     UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hashistoricalconfiguration, "HasHistoricalConfiguration");
-    hashistoricalconfiguration->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "HistoricalConfigurationOf");
+    hashistoricalconfiguration->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "HistoricalConfigurationOf");
     hashistoricalconfiguration->nodeId.identifier.numeric = UA_NS0ID_HASHISTORICALCONFIGURATION;
     hashistoricalconfiguration->isAbstract = UA_FALSE;
     hashistoricalconfiguration->symmetric  = UA_FALSE;

Datei-Diff unterdrückt, da er zu groß ist
+ 1088 - 33
src/server/ua_server_addressspace.c


+ 3 - 0
src/server/ua_server_binary.c

@@ -352,6 +352,9 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     case UA_NS0ID_ADDNODESREQUEST:
         INVOKE_SERVICE(AddNodes, UA_TYPES_ADDNODESRESPONSE);
         break;
+    case UA_NS0ID_DELETENODESREQUEST:
+      INVOKE_SERVICE(DeleteNodes, UA_TYPES_DELETENODESRESPONSE);
+      break;
 #endif
     default: {
         if(requestType.namespaceIndex == 0 && requestType.identifier.numeric==787)

+ 4 - 1
src/server/ua_server_internal.h

@@ -86,8 +86,11 @@ UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_E
 UA_StatusCode UA_Server_addReferenceWithSession(UA_Server *server, UA_Session *session,
                                                 const UA_AddReferencesItem *item);
 
+UA_StatusCode deleteOneWayReferenceWithSession(UA_Server *server, UA_Session *session, 
+                                               const UA_DeleteReferencesItem *item);
+
 UA_StatusCode addOneWayReferenceWithSession(UA_Server *server, UA_Session *session, 
-                                                   const UA_AddReferencesItem *item);
+                                            const UA_AddReferencesItem *item);
 
 UA_StatusCode UA_Server_addDelayedJob(UA_Server *server, UA_Job job);
 

+ 9 - 1
src/server/ua_services.h

@@ -299,12 +299,20 @@ void Service_Publish(UA_Server *server, UA_Session *session,
 // Service_TransferSubscription
 // Service_DeleteSubscription
 /** @} */
-/** @} */
 #endif
 
 #ifdef ENABLE_METHODCALLS
+/**
+ * @name Call Service Set
+ *
+ * Calls are used to execute serverside methods.
+ *
+ * @{
+ */
 void Service_Call(UA_Server *server, UA_Session *session,
                   const UA_CallRequest *request,
                   UA_CallResponse *response);
+/** @} */
 #endif
 #endif /* UA_SERVICES_H_ */
+/** @} */

+ 78 - 15
src/server/ua_services_attribute.c

@@ -426,6 +426,16 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #endif
 }
 
+#define SETATTRIBUTE_IF_DATATYPE_IS(EXP_DT)                                                                             \
+/* The Userspace setAttribute expects the value being passed being the correct type and has no own means for checking   \
+   We need to check for setAttribute if the dataType is correct                                                         \
+*/                                                                                                                      \
+expectType = UA_NODEID_NUMERIC(0, UA_NS0ID_##EXP_DT);                                                                   \
+if (! UA_NodeId_equal(&expectType, &wvalue->value.value.type->typeId))                                                  \
+  retval |= UA_STATUSCODE_BADTYPEMISMATCH;                                                                              \
+else                                                                                                                    \
+  retval = UA_Server_setAttributeValue(server, wvalue->nodeId, wvalue->attributeId, (void *) wvalue->value.value.data); \
+
 #ifndef BUILD_UNIT_TESTS
 static
 #endif
@@ -438,26 +448,87 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
 
     // we might repeat writing, e.g. when the node got replaced mid-work
     UA_Boolean done = UA_FALSE;
+    UA_NodeId expectType;
     while(!done) {
         const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
         if(!node)
             return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
         switch(wvalue->attributeId) {
-        case UA_ATTRIBUTEID_NODEID:
-        case UA_ATTRIBUTEID_NODECLASS:
         case UA_ATTRIBUTEID_BROWSENAME:
+          SETATTRIBUTE_IF_DATATYPE_IS(QUALIFIEDNAME)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_DISPLAYNAME:
+          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_DESCRIPTION:
+          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_WRITEMASK:
+          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_USERWRITEMASK:
+          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_ISABSTRACT:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_SYMMETRIC:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_INVERSENAME:
+          SETATTRIBUTE_IF_DATATYPE_IS(LOCALIZEDTEXT)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
         case UA_ATTRIBUTEID_EVENTNOTIFIER:
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
+          SETATTRIBUTE_IF_DATATYPE_IS(BYTE)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_VALUERANK:
+          SETATTRIBUTE_IF_DATATYPE_IS(INT32)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
+          SETATTRIBUTE_IF_DATATYPE_IS(INT32)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_ACCESSLEVEL:
+          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_USERACCESSLEVEL:
+          SETATTRIBUTE_IF_DATATYPE_IS(UINT32)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+          SETATTRIBUTE_IF_DATATYPE_IS(DOUBLE)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_HISTORIZING:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_EXECUTABLE:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
+        case UA_ATTRIBUTEID_USEREXECUTABLE:
+          SETATTRIBUTE_IF_DATATYPE_IS(BOOLEAN)
+          done = UA_TRUE;
+          break;
+        // The value logic implemented by jpfr is far more advanced that that in the userspace, so we use that
+        // here
         case UA_ATTRIBUTEID_VALUE: {
             if(node->nodeClass != UA_NODECLASS_VARIABLE &&
                node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
@@ -552,19 +623,11 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                 UA_free(range.dimensions);
             }
             break;
+        case UA_ATTRIBUTEID_NODEID:
+        case UA_ATTRIBUTEID_NODECLASS:
         case UA_ATTRIBUTEID_DATATYPE:
-        case UA_ATTRIBUTEID_VALUERANK:
-        case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-        case UA_ATTRIBUTEID_ACCESSLEVEL:
-        case UA_ATTRIBUTEID_USERACCESSLEVEL:
-        case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-        case UA_ATTRIBUTEID_HISTORIZING:
-        case UA_ATTRIBUTEID_EXECUTABLE:
-        case UA_ATTRIBUTEID_USEREXECUTABLE:
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
         default:
-            retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
             break;
         }
 

+ 1 - 1
src/server/ua_services_call.c

@@ -181,7 +181,7 @@ static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequ
                                                outputArguments->value.variant.arrayLength);
         result->outputArgumentsSize = outputArguments->value.variant.arrayLength;
         result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments,
-                                                          result->outputArguments);
+                                                          result->outputArguments, methodCalled->methodHandle);
     }
     else
         result->statusCode = UA_STATUSCODE_BADNOTWRITABLE; // There is no NOTEXECUTABLE?

+ 39 - 2
src/server/ua_services_nodemanagement.c

@@ -236,6 +236,30 @@ static void addNodeFromAttributes(UA_Server *server, UA_Session *session, UA_Add
     }
 }
 
+static UA_StatusCode deleteNode(UA_Server *server, UA_NodeId nodeId, UA_Boolean deleteReferences) {
+  const UA_Node *delNode = UA_NodeStore_get(server->nodestore, &nodeId);
+  if (!delNode)
+    return UA_STATUSCODE_BADNODEIDINVALID;
+  
+  // Find and remove all References to this node if so requested.
+  if(deleteReferences == UA_TRUE) {
+    UA_DeleteReferencesItem *delItem = UA_DeleteReferencesItem_new();
+    delItem->deleteBidirectional = UA_TRUE; // WARNING: Current semantics in deleteOneWayReference is 'delete forward or inverse'
+    UA_NodeId_copy(&nodeId, &delItem->targetNodeId.nodeId);
+    
+    for(int i=0; i<delNode->referencesSize; i++) {
+      UA_NodeId_copy(&delNode->references[i].targetId.nodeId, &delItem->sourceNodeId);
+      
+      UA_NodeId_deleteMembers(&delItem->sourceNodeId);
+    }
+    
+    UA_DeleteReferencesItem_delete(delItem);
+  }
+  
+  UA_NodeStore_release(delNode);
+  return UA_NodeStore_remove(server->nodestore, &nodeId);
+}
+
 void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response) {
     if(request->nodesToAddSize <= 0) {
@@ -339,10 +363,23 @@ void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddR
 
 void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response) {
-
+  UA_StatusCode retval = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
+  
+  response->resultsSize = request->nodesToDeleteSize;
+  response->results = (UA_StatusCode *) UA_malloc(sizeof(UA_StatusCode) * request->nodesToDeleteSize);
+  
+  UA_DeleteNodesItem *item;
+  for(int i=0; i<request->nodesToDeleteSize; i++) {
+    item = &request->nodesToDelete[i];
+    response->results[i] = deleteNode(server, item->nodeId, item->deleteTargetReferences);
+  }
+  
+  response->responseHeader.serviceResult = retval;
 }
 
 void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response) {
-
+  UA_StatusCode retval = UA_STATUSCODE_BADSERVICEUNSUPPORTED;
+  
+  response->responseHeader.serviceResult = retval;
 }

+ 5 - 4
src/server/ua_subscription.c

@@ -154,7 +154,7 @@ void Subscription_updateNotifications(UA_Subscription *subscription) {
                 monItemsChangeT += MonitoredItem_QueueToDataChangeNotifications(&changeNotification->monitoredItems[monItemsChangeT], mon);
                 MonitoredItem_ClearQueue(mon);
             }
-
+            
             changeNotification->monitoredItemsSize  = monItemsChangeT;
             changeNotification->diagnosticInfosSize = 0;
             changeNotification->diagnosticInfos     = UA_NULL;
@@ -353,10 +353,11 @@ int MonitoredItem_QueueToDataChangeNotifications(UA_MonitoredItemNotification *d
     TAILQ_FOREACH(queueItem, &monitoredItem->queue, listEntry) {
         dst[queueSize].clientHandle = monitoredItem->clientHandle;
         dst[queueSize].value.hasServerPicoseconds = UA_FALSE;
-        dst[queueSize].value.hasServerTimestamp   = UA_FALSE;
-        dst[queueSize].value.serverTimestamp      = UA_FALSE;
+        dst[queueSize].value.hasServerTimestamp   = UA_TRUE;
+        dst[queueSize].value.serverTimestamp      = UA_DateTime_now();
         dst[queueSize].value.hasSourcePicoseconds = UA_FALSE;
-        dst[queueSize].value.hasSourceTimestamp   = UA_FALSE;
+        dst[queueSize].value.hasSourceTimestamp   = UA_TRUE;
+        dst[queueSize].value.sourceTimestamp      = UA_DateTime_now();
         dst[queueSize].value.hasValue             = UA_TRUE;
         dst[queueSize].value.status = UA_STATUSCODE_GOOD;
     

+ 96 - 52
tests/check_services_attributes.c

@@ -12,7 +12,6 @@
 #include "ua_util.h"
 #include "server/ua_server_internal.h"
 
-
 //#include "server/ua_services_attribute.c"
 
 #ifdef UA_MULTITHREADING
@@ -28,33 +27,35 @@ static void copyNames(UA_Node *node, char *name) {
 
 
 static UA_Server* makeTestSequence(void) {
-	UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
-
-	/* VariableNode */
-	UA_Variant *myIntegerVariant = UA_Variant_new();
-	UA_Int32 myInteger = 42;
-	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
-			&UA_TYPES[UA_TYPES_INT32]);
-	const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-	const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
-	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
-	UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-	UA_Server_addVariableNode(server, myIntegerVariant, myIntegerName,
-                                  myIntegerNodeId, parentNodeId, parentReferenceNodeId, NULL);
-
-	/* ObjectNode */
-	UA_Server_addObjectNode(server,UA_QUALIFIEDNAME(1, "Demo"), UA_NODEID_NUMERIC(1, 50), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
-
-	/* ReferenceTypeNode */
-	UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
-	copyNames((UA_Node*)organizes, "Organizes");
-	organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
-	organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
-	organizes->isAbstract = UA_FALSE;
-	organizes->symmetric  = UA_FALSE;
-	UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
-
-	/* ViewNode */
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+    /* VariableNode */
+    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
+                    &UA_TYPES[UA_TYPES_INT32]);
+    const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+    const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US","the answer"), UA_LOCALIZEDTEXT("en_US","the answer"),
+                              parentNodeId, parentReferenceNodeId, 0, 0, myIntegerVariant, NULL);
+
+    /* ObjectNode */
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"), 
+                            UA_LOCALIZEDTEXT("en_US","Demo"), UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), 
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 0, 0, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+
+    /* ReferenceTypeNode */
+    UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
+    copyNames((UA_Node*)organizes, "Organizes");
+    organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
+    organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
+    organizes->isAbstract = UA_FALSE;
+    organizes->symmetric  = UA_FALSE;
+    UA_Server_addNode(server, (UA_Node*)organizes, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE));
+
+    /* ViewNode */
     UA_ViewNode *viewtest = UA_ViewNode_new();
     copyNames((UA_Node*)viewtest, "Viewtest");
     viewtest->nodeId.identifier.numeric = UA_NS0ID_VIEWNODE;
@@ -83,14 +84,16 @@ static UA_VariableNode* makeCompareSequence(void) {
 	UA_Variant_setScalarCopy(myIntegerVariant, &myInteger,
 			&UA_TYPES[UA_TYPES_INT32]);
 	const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-	const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+	const UA_LocalizedText myIntegerDisplName = UA_LOCALIZEDTEXT("en_US", "the answer");
+        const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
 	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
 	//UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
 	node->value.variant=*myIntegerVariant;
 	UA_NodeId_copy(&myIntegerNodeId,&node->nodeId);
 	UA_QualifiedName_copy(&myIntegerName,&node->browseName);
-	UA_String_copy(&myIntegerName.name,&node->displayName.text);
-	UA_ExpandedNodeId parentId;
+        UA_LocalizedText_copy(&myIntegerDisplName, &node->displayName);
+        UA_LocalizedText_copy(&myIntegerDisplName, &node->description);
+        UA_ExpandedNodeId parentId;
 	UA_ExpandedNodeId_init(&parentId);
 	UA_NodeId_copy(&parentNodeId,&parentId.nodeId);
 
@@ -374,7 +377,7 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp)
 
 		UA_LocalizedText* respval;
 		respval = (UA_LocalizedText*) resp.value.data;
-		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "OrganizedBy");
+		const UA_LocalizedText comp = UA_LOCALIZEDTEXT("en_US", "OrganizedBy");
 
 		ck_assert_int_eq(-1, resp.value.arrayLength);
 		ck_assert_int_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
@@ -664,14 +667,16 @@ START_TEST(WriteSingleAttributeNodeclass)
 START_TEST(WriteSingleAttributeBrowseName)
 	{
 		UA_Server *server = makeTestSequence();
-
+		
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_QualifiedName testValue = UA_QUALIFIEDNAME(1, "the.answer");
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_BROWSENAME;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeDisplayName)
@@ -680,11 +685,13 @@ START_TEST(WriteSingleAttributeDisplayName)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_LocalizedText testValue = UA_LOCALIZEDTEXT("en_EN", "the.answer");
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+		wValue.value.hasValue = UA_TRUE;
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_DISPLAYNAME;
-		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeDescription)
@@ -693,11 +700,15 @@ START_TEST(WriteSingleAttributeDescription)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_LocalizedText testValue = UA_LOCALIZEDTEXT("en_EN", "the.answer");
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+		wValue.value.hasValue = UA_TRUE;
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_DESCRIPTION;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeWriteMask)
@@ -706,11 +717,14 @@ START_TEST(WriteSingleAttributeWriteMask)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Int32 testValue = 0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_UINT32]);
+		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_WRITEMASK;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeUserWriteMask)
@@ -719,11 +733,13 @@ START_TEST(WriteSingleAttributeUserWriteMask)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Int32 testValue = 0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_UINT32]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_USERWRITEMASK;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeIsAbstract)
@@ -732,11 +748,13 @@ START_TEST(WriteSingleAttributeIsAbstract)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_ISABSTRACT;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeSymmetric)
@@ -745,11 +763,13 @@ START_TEST(WriteSingleAttributeSymmetric)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_SYMMETRIC;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeInverseName)
@@ -758,11 +778,13 @@ START_TEST(WriteSingleAttributeInverseName)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_LocalizedText testValue = UA_LOCALIZEDTEXT("en_US", "not.the.answer");
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_INVERSENAME;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeContainsNoLoops)
@@ -771,11 +793,13 @@ START_TEST(WriteSingleAttributeContainsNoLoops)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_CONTAINSNOLOOPS;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeEventNotifier)
@@ -784,11 +808,13 @@ START_TEST(WriteSingleAttributeEventNotifier)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Byte testValue = 0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BYTE]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 
@@ -850,11 +876,14 @@ START_TEST(WriteSingleAttributeValueRank)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Int32 testValue = -1;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_INT32]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_VALUERANK;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		// Returns attributeInvalid, since variant/value may be writable
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADATTRIBUTEIDINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeArrayDimensions)
@@ -863,11 +892,14 @@ START_TEST(WriteSingleAttributeArrayDimensions)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Int32 testValue[] = {-1,-1,-1};
+		UA_Variant_setArrayCopy(&wValue.value.value, &testValue, 3, &UA_TYPES[UA_TYPES_INT32]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_ARRAYDIMENSIONS;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		// Returns attributeInvalid, since variant/value may be writable
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADATTRIBUTEIDINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeAccessLevel)
@@ -876,11 +908,13 @@ START_TEST(WriteSingleAttributeAccessLevel)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_UInt32 testValue = 0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_UINT32]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_ACCESSLEVEL;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeUserAccessLevel)
@@ -889,11 +923,13 @@ START_TEST(WriteSingleAttributeUserAccessLevel)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_UInt32 testValue = 0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_UINT32]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_USERACCESSLEVEL;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeMinimumSamplingInterval)
@@ -902,11 +938,13 @@ START_TEST(WriteSingleAttributeMinimumSamplingInterval)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Double testValue = 0.0;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_DOUBLE]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeHistorizing)
@@ -915,11 +953,13 @@ START_TEST(WriteSingleAttributeHistorizing)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_HISTORIZING;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeExecutable)
@@ -928,11 +968,13 @@ START_TEST(WriteSingleAttributeExecutable)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_EXECUTABLE;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeUserExecutable)
@@ -941,11 +983,13 @@ START_TEST(WriteSingleAttributeUserExecutable)
 
 		UA_WriteValue wValue;
 		UA_WriteValue_init(&wValue);
+		UA_Boolean testValue = UA_TRUE;
+		UA_Variant_setScalarCopy(&wValue.value.value, &testValue, &UA_TYPES[UA_TYPES_BOOLEAN]);
 		wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
 		wValue.attributeId = UA_ATTRIBUTEID_USEREXECUTABLE;
 		wValue.value.hasValue = UA_TRUE;
 		UA_StatusCode retval = writeValue(server, &wValue);
-		ck_assert_int_eq(retval, UA_STATUSCODE_BADWRITENOTSUPPORTED);
+		ck_assert_int_eq(retval, UA_STATUSCODE_BADNODECLASSINVALID);
 	}END_TEST
 
 START_TEST(WriteSingleAttributeNoValue)

Datei-Diff unterdrückt, da er zu groß ist
+ 1514 - 0
tools/pyUANamespace/NodeID_Blacklist_FullNS0.txt


+ 50 - 3
tools/pyUANamespace/open62541_MacroHelper.py

@@ -38,7 +38,16 @@ class open62541_MacroHelper():
       return ""
     else:
       return ""
-
+  
+  def getNodeIdDefineString(self, node):
+    code = []
+    extrNs = node.browseName().split(":")
+    if len(extrNs) > 1:
+      code.append("#define UA_NS"  + str(node.id().ns) + "ID_" + extrNs[1].upper() + " " + str(node.id().i))
+    else:
+      code.append("#define UA_NS"  + str(node.id().ns) + "ID_" + extrNs[0].upper() + " " + str(node.id().i))
+    return code
+  
   def getCreateNodeIDMacro(self, node):
     if node.id().i != None:
       return "UA_NODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
@@ -76,8 +85,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 = []
 

+ 10 - 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,9 @@ 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;")
+        # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
+        #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 +384,16 @@ 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;")
+          # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
+          #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;")
+          # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
+          #if (bootstrapping == True):
+          #  code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
     return code
 
 

+ 125 - 7
tools/pyUANamespace/ua_namespace.py

@@ -23,6 +23,7 @@ from struct import pack as structpack
 from logger import *;
 from ua_builtin_types import *;
 from ua_node_types import *;
+from ua_constants import *;
 from open62541_MacroHelper import open62541_MacroHelper
 
 def getNextElementNode(xmlvalue):
@@ -438,7 +439,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.
 
@@ -493,16 +517,101 @@ class opcua_namespace():
     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)
+    
+    log(self, "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
+    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] += 1
+          # isSubTypeOf/typeDefinition of this node has precedence over this node
+          elif ref.referenceType() in subTypeRefs and not ref.isForward():
+            nmatrix[nind][tind+1] += 1
+          # Else the target depends on us
+          elif ref.isForward():
+            nmatrix[tind][nind+1] += 1
+    
+    log(self, "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] -= 1
+          elif ref.referenceType() in subTypeRefs and not ref.isForward():
+            nmatrix[nind][tind+1] -= 1
+          elif ref.isForward():
+            nmatrix[tind][nind+1] -= 1
+      nmatrix[nind][0] = None
+    self.nodes = reorder
+    log(self, "Nodes reordered.")
+    return
+  
   def printOpen62541Header(self, printedExternally=[], supressGenerationOfAttribute=[], outfilename=""):
     unPrintedNodes = []
     unPrintedRefs  = []
     code = []
     header = []
-
+    
+    # Reorder our nodes to produce a bare minimum of bootstrapping dependencies
+    log(self, "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)
-
+    
     # Populate the unPrinted-Lists with everything we have.
     # Every Time a nodes printfunction is called, it will pop itself and
     #   all printed references from these lists.
@@ -526,8 +635,6 @@ class opcua_namespace():
     header.append('#include "server/ua_server_internal.h"')
     header.append('#include "server/ua_nodes.h"')
     header.append('#include "ua_types.h"')
-    header.append("extern void "+outfilename+"(UA_Server *server);\n")
-    header.append("#endif /* "+outfilename.upper()+"_H_ */")
 
     code.append('#include "'+outfilename+'.h"')
     code.append("inline void "+outfilename+"(UA_Server *server) {")
@@ -538,13 +645,24 @@ class opcua_namespace():
     log(self, "Collecting all references used in the namespace.", LOG_LEVEL_DEBUG)
     refsUsed = []
     for n in self.nodes:
+      # Since we are already looping over all nodes, use this chance to print NodeId defines
+      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)
+          
+      # Now for the actual references...
       for r in n.getReferences():
-        if not r.referenceType() in refsUsed:
+        # Only print valid refernces 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())
     log(self, str(len(refsUsed)) + " reference types are used in the namespace, which will now get bootstrapped.", LOG_LEVEL_DEBUG)
     for r in refsUsed:
       code = code + r.printOpen62541CCode(unPrintedNodes, unPrintedRefs);
-
+    
+    header.append("extern void "+outfilename+"(UA_Server *server);\n")
+    header.append("#endif /* "+outfilename.upper()+"_H_ */")
+    
     # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
     #               Nodes remove themselves from this list when printed.
     log(self, "Printing all other nodes.", LOG_LEVEL_DEBUG)

+ 248 - 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, unPrintedReferences=[], 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,41 @@ 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, unPrintedReferences=[], 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
+      myTypeRef = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myTypeRef = ref
+          break
+      if myTypeRef==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myTypeRef = ref
+            break
+      if myTypeRef==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(myTypeRef.target()) + ",")
+        if myTypeRef in unPrintedReferences:
+          unPrintedReferences.remove(myTypeRef)
+          
+      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 +855,36 @@ 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, unPrintedReferences=[], 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
+      myTypeRef = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myTypeRef = ref
+          break
+      if myTypeRef==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myTypeRef = ref
+            break
+      if myTypeRef==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(myTypeRef.target()) + ",")
+        if myTypeRef in unPrintedReferences:
+          unPrintedReferences.remove(myTypeRef)
+      
+      #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 +1045,32 @@ 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
+    code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_Variant_new();")
+    return code
+  
+  def printOpen62541CCode_Subtype(self, unPrintedReferences=[], 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 +1078,8 @@ 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()
+    # The variant is guaranteed to exist by SubtypeEarly()
+    code.append(self.getCodePrintableID() + "->value.variant = *" + self.getCodePrintableID() + "_variant;")
     return code
 
 class opcua_node_method_t(opcua_node_t):
@@ -1054,9 +1129,21 @@ 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, unPrintedReferences=[], 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("       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 +1177,41 @@ 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, unPrintedReferences=[], 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
+      myTypeRef = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myTypeRef = ref
+          break
+      if myTypeRef==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myTypeRef = ref
+            break
+      if myTypeRef==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(myTypeRef.target()) + ",")
+        if myTypeRef in unPrintedReferences:
+          code.append("       // removed " + str(myTypeRef))
+          unPrintedReferences.remove(myTypeRef)
+      
+      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,13 +1299,39 @@ 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
+    code.append("UA_Variant *" + self.getCodePrintableID() + "_variant = UA_Variant_new();")
+    return code
+  
+  def printOpen62541CCode_Subtype(self, unPrintedReferences=[], 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:
       code.append(self.getCodePrintableID() + "->isAbstract = UA_FALSE;")
+    
+    # The variant is guaranteed to exist by SubtypeEarly()
+    code.append(self.getCodePrintableID() + "->value.variant = *" + self.getCodePrintableID() + "_variant;")
     return code
 
 class opcua_node_dataType_t(opcua_node_t):
@@ -1500,9 +1641,38 @@ 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, unPrintedReferences=[], 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
+      myTypeRef = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myTypeRef = ref
+          break
+      if myTypeRef==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myTypeRef = ref
+            break
+      if myTypeRef==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(myTypeRef.target()) + ",")
+        if myTypeRef in unPrintedReferences:
+          unPrintedReferences.remove(myTypeRef)
+      
+      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 +1706,36 @@ 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, unPrintedReferences=[], 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
+      myTypeRef = None
+      for ref in self.getReferences():
+        if ref.referenceType() in typeDefs:
+          myTypeRef = ref
+          break
+      if myTypeRef==None:
+        for ref in self.getReferences():
+          if ref.referenceType().browseName() == "HasSubtype" and ref.isForward() == False:
+            myTypeRef = ref
+            break
+      if myTypeRef==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(myTypeRef.target()) + ",")
+        if myTypeRef in unPrintedReferences:
+          unPrintedReferences.remove(myTypeRef)
+          
+      code.append("       // FIXME: Missing eventNotifier")
+      code.append("       // FIXME: Missing containsNoLoops")
+      return code
+    
     if self.containsNoLoops():
       code.append(self.getCodePrintableID() + "->containsNoLoops = UA_TRUE;")
     else: