Ver código fonte

Merge pull request #378 from acplt/inline_setattribute

use inline methods for type safety; userspace functions call the normal services internally
Julius Pfrommer 9 anos atrás
pai
commit
999205c795

+ 4 - 5
CMakeLists.txt

@@ -43,7 +43,7 @@ endif()
 if(CMAKE_COMPILER_IS_GNUCC OR "x${CMAKE_C_COMPILER_ID}" STREQUAL "xClang")
     add_definitions(-std=c99 -pipe -Wall -Wextra -Werror -Wformat -Wno-unused-parameter
                       -Wno-unused-function -Wno-unused-label -Wpointer-arith -Wreturn-type -Wsign-compare -Wmultichar
-                      -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes # -Wshadow -Wconversion
+                      -Wcast-qual -Wmissing-prototypes -Wstrict-prototypes #-Wshadow #-Wconversion
                       -Winit-self -Wuninitialized -Wformat-security -Wformat-nonliteral)
     # binary size reduction settings
     add_definitions(-ffunction-sections -fdata-sections -fno-stack-protector -fno-unwind-tables
@@ -113,7 +113,6 @@ set(lib_sources ${PROJECT_SOURCE_DIR}/src/ua_types.c
                 ${PROJECT_SOURCE_DIR}/src/ua_securechannel.c
                 ${PROJECT_SOURCE_DIR}/src/ua_session.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server.c
-                ${PROJECT_SOURCE_DIR}/src/server/ua_server_addressspace.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_binary.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_nodes.c
                 ${PROJECT_SOURCE_DIR}/src/server/ua_server_worker.c
@@ -234,9 +233,9 @@ if(ENABLE_EXTERNAL_NAMESPACES)
 endif()
 
 ## enable dynamic nodeset
-option(ENABLE_ADDNODES "Enable dynamic addition of nodes" ON)
-if(ENABLE_ADDNODES)
-    add_definitions(-DENABLE_ADDNODES )
+option(ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes" ON)
+if(ENABLE_NODEMANAGEMENT)
+    add_definitions(-DENABLE_NODEMANAGEMENT)
 endif()
 
 ## set the precompiler flags

+ 2 - 2
doc/building.rst

@@ -119,8 +119,8 @@ ENABLE_* group
 
 This group contains build options related to the supported OPC UA features.
 
-**ENABLE_ADDNODES**
-   AddNodes services in sever and client
+**ENABLE_NODEMANAGEMENT**
+   Node management services (adding and removing nodes and references) in server and client
 **ENABLE_AMALGAMATION**
    Compile a single-file release files open62541.c and open62541.h
 **ENABLE_COVERAGE**

+ 1 - 1
examples/client.c

@@ -157,7 +157,7 @@ int main(int argc, char *argv[]) {
 
 #endif
 
-#ifdef ENABLE_ADDNODES 
+#ifdef ENABLE_NODEMANAGEMENT 
     /* Create a new object type node */
     // New ReferenceType
     UA_AddNodesResponse *adResp = UA_Client_createReferenceTypeNode(client,

+ 2 - 0
examples/networklayer_tcp.c

@@ -384,6 +384,8 @@ static size_t ServerNetworkLayerTCP_getJobs(ServerNetworkLayerTCP *layer, UA_Job
 }
 
 static size_t ServerNetworkLayerTCP_stop(ServerNetworkLayerTCP *layer, UA_Job **jobs) {
+    shutdown(layer->serversockfd,2);
+    CLOSESOCKET(layer->serversockfd);
     UA_Job *items = malloc(sizeof(UA_Job) * layer->mappingsSize * 2);
     if(!items)
         return 0;

+ 242 - 213
examples/server.c

@@ -46,7 +46,9 @@ UA_Logger logger;
 /*************************/
 /* Read-only data source */
 /*************************/
-static UA_StatusCode readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
+static UA_StatusCode
+readTimeData(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
+             const UA_NumericRange *range, UA_DataValue *value) {
     if(range) {
         value->hasStatus = UA_TRUE;
         value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
@@ -72,7 +74,9 @@ static UA_StatusCode readTimeData(void *handle, const UA_NodeId nodeId, UA_Boole
 /*      Only on Linux        */
 /*****************************/
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
+static UA_StatusCode
+readTemperature(void *handle, const UA_NodeId nodeId, UA_Boolean sourceTimeStamp,
+                const UA_NumericRange *range, UA_DataValue *value) {
     if(range) {
         value->hasStatus = UA_TRUE;
         value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
@@ -115,24 +119,29 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 
-static UA_StatusCode readLedStatus(void *handle, UA_NodeId nodeid, UA_Boolean sourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value) {
-  if(range)
-    return UA_STATUSCODE_BADINDEXRANGEINVALID;
+static UA_StatusCode
+readLedStatus(void *handle, UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
+              const UA_NumericRange *range, UA_DataValue *value) {
+    if(range)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
-  value->hasValue = UA_TRUE;
-  UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    value->hasValue = UA_TRUE;
+    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &ledStatus,
+                                                    &UA_TYPES[UA_TYPES_BOOLEAN]);
 
-  if(retval != UA_STATUSCODE_GOOD)
-    return retval;
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
   
-  if(sourceTimeStamp) {
-          value->sourceTimestamp = UA_DateTime_now();
-          value->hasSourceTimestamp = UA_TRUE;
-  }
-  return UA_STATUSCODE_GOOD;
+    if(sourceTimeStamp) {
+        value->sourceTimestamp = UA_DateTime_now();
+        value->hasSourceTimestamp = UA_TRUE;
+    }
+    return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode writeLedStatus(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range) {
+static UA_StatusCode
+writeLedStatus(void *handle, const UA_NodeId nodeid,
+               const UA_Variant *data, const UA_NumericRange *range) {
     if(range)
         return UA_STATUSCODE_BADINDEXRANGEINVALID;
 
@@ -159,9 +168,10 @@ static UA_StatusCode writeLedStatus(void *handle, const UA_NodeId nodeid, const
 }
 
 #ifdef ENABLE_METHODCALLS
-static UA_StatusCode getMonitoredItems(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
+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;
@@ -197,234 +207,253 @@ 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;
+static UA_StatusCode
+nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {  
+    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));
-
-  // add node with the datetime data source
-  UA_NodeId nodeId_currentTime;
-  UA_DataSource dateDataSource = (UA_DataSource) {.handle = NULL, .read = readTimeData, .write = NULL};
-  const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
-  const UA_LocalizedText dateNameBrowseName = UA_LOCALIZEDTEXT("en_US","current time");
-  UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, dateName, dateNameBrowseName, dateNameBrowseName, 0, 0,
-
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                  dateDataSource,
-
-                                  &nodeId_currentTime);
-
-  // Get and reattach the datasource
-  UA_DataSource *dataSourceCopy = NULL;
-  UA_Server_getAttribute_DataSource(server, nodeId_currentTime, &dataSourceCopy);
-  if (dataSourceCopy == NULL)
-    UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is invalid");
-  else if (dataSourceCopy->read != dateDataSource.read)
-    UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is not the same as we set?");
-  else
-    UA_Server_setAttribute_DataSource(server, nodeId_currentTime, *dataSourceCopy);
-  free(dataSourceCopy);
+    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};
+    UA_VariableAttributes v_attr;
+    UA_VariableAttributes_init(&v_attr);
+    v_attr.description = UA_LOCALIZEDTEXT("en_US","current time");
+    v_attr.displayName = UA_LOCALIZEDTEXT("en_US","current time");
+    const UA_QualifiedName dateName = UA_QUALIFIEDNAME(1, "current time");
+    UA_NodeId dataSourceId;
+    UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), dateName,
+                                        UA_NODEID_NULL, v_attr, dateDataSource, &dataSourceId);
+
+    // Get and reattach the datasource
+    UA_DataSource dataSourceCopy;
+    UA_Server_getNodeAttribute_value_dataSource(server, dataSourceId, &dataSourceCopy);
+    if (dataSourceCopy.read != dateDataSource.read)
+        UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is not the same as we set?");
+    else
+        UA_Server_setNodeAttribute_value_dataSource(server, dataSourceId, dataSourceCopy);
 #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");
-          const UA_LocalizedText tempNameBrowseName = UA_LOCALIZEDTEXT("en_US","temperature");
-          UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, tempName, tempNameBrowseName, tempNameBrowseName, 0, 0,
-
+    /* 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_VariableAttributes_init(&v_attr);
+        v_attr.description = UA_LOCALIZEDTEXT("en_US","temperature");
+        v_attr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
+        UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
                                             UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                            temperatureDataSource,
-
-                                            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");
-      const UA_LocalizedText statusNameBrowseName = UA_LOCALIZEDTEXT("en_US","status LED");
-      UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL, statusName, statusNameBrowseName, statusNameBrowseName, 0, 0,
-
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                        ledStatusDataSource,
+                                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), tempName,
+                                            UA_NODEID_NULL, v_attr, temperatureDataSource, NULL);
+    }
 
-                                        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)");
+    /* 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};
+            UA_VariableAttributes_init(&v_attr);
+            v_attr.description = UA_LOCALIZEDTEXT("en_US","status LED");
+            v_attr.displayName = UA_LOCALIZEDTEXT("en_US","status LED");
+            const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
+            UA_Server_addDataSourceVariableNode(server, UA_NODEID_NULL,
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), statusName,
+                                                UA_NODEID_NULL, v_attr, ledStatusDataSource, NULL);
+        } else
+            UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND,
+                           "[Raspberry Pi] LED file exist, but is not accessible (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, myIntegerNodeId, myIntegerName, UA_LOCALIZEDTEXT("en_US", "the answer"), UA_LOCALIZEDTEXT("en_US", "the answer"),  0, 0,
-                            parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
-  /**************/
-  /* Demo Nodes */
-  /**************/
+    // add a static variable node to the adresspace
+    UA_VariableAttributes myVar;
+    UA_VariableAttributes_init(&myVar);
+    myVar.description = UA_LOCALIZEDTEXT("en_US", "the answer");
+    myVar.displayName = UA_LOCALIZEDTEXT("en_US", "the answer");
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalarCopy(&myVar.value, &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, parentNodeId, parentReferenceNodeId,
+                              myIntegerName, UA_NODEID_NULL, myVar, NULL);
+    UA_Variant_deleteMembers(&myVar.value);
+
+    /**************/
+    /* Demo Nodes */
+    /**************/
 
 #define DEMOID 50000
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID), UA_QUALIFIEDNAME(1, "Demo"), UA_LOCALIZEDTEXT("en_US","Demo"),
-                          UA_LOCALIZEDTEXT("en_US","Demo"), 0, 0, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    UA_ObjectAttributes object_attr;
+    UA_ObjectAttributes_init(&object_attr);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Demo"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
 
 #define SCALARID 50001
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID), UA_QUALIFIEDNAME(1, "Scalar"), UA_LOCALIZEDTEXT("en_US","Scalar"),
-                          UA_LOCALIZEDTEXT("en_US","Scalar"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Scalar");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Scalar");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, SCALARID),
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Scalar"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
 
 #define ARRAYID 50002
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID), UA_QUALIFIEDNAME(1, "Array"), UA_LOCALIZEDTEXT("en_US","Array"),
-                          UA_LOCALIZEDTEXT("en_US","Array"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), NULL);
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Array");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Array");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, ARRAYID),
+                            UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                            UA_QUALIFIEDNAME(1, "Array"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL);
 
 #define MATRIXID 50003
-  UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_QUALIFIEDNAME(1, "Matrix"), UA_LOCALIZEDTEXT("en_US","Matrix"),
-                          UA_LOCALIZEDTEXT("en_US","Matrix"), 0, 0, UA_NODEID_NUMERIC(1, DEMOID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          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",name), UA_LOCALIZEDTEXT("en_US",name), 0, 0,
-                              UA_NODEID_NUMERIC(1, SCALARID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 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",name), UA_LOCALIZEDTEXT("en_US",name), 0, 0,
-                              UA_NODEID_NUMERIC(1, ARRAYID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), 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",name), UA_LOCALIZEDTEXT("en_US",name),
-                              0, 0, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), arrayvar, NULL);
-  }
+    object_attr.description = UA_LOCALIZEDTEXT("en_US","Matrix");
+    object_attr.displayName = UA_LOCALIZEDTEXT("en_US","Matrix");
+    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, MATRIXID), UA_NODEID_NUMERIC(1, DEMOID),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, "Matrix"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, 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;
+
+        UA_VariableAttributes attr;
+        UA_VariableAttributes_init(&attr);
+        char name[15];
+        sprintf(name, "%02d", type);
+        attr.displayName = UA_LOCALIZEDTEXT("en_US",name);
+        UA_QualifiedName qualifiedName = UA_QUALIFIEDNAME(1, name);
+
+        /* add a scalar node for every built-in type */
+        void *value = UA_new(&UA_TYPES[type]);
+        UA_Variant_setScalar(&attr.value, value, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, SCALARID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+
+        /* add an array node for every built-in type */
+        UA_Variant_setArray(&attr.value, UA_Array_new(&UA_TYPES[type], 10),
+                            10, &UA_TYPES[type]);
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, ARRAYID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+
+        /* add an matrix node for every built-in type */
+        void* myMultiArray = UA_Array_new(&UA_TYPES[type],9);
+        attr.value.arrayDimensions = UA_Array_new(&UA_TYPES[UA_TYPES_INT32],2);
+        attr.value.arrayDimensions[0] = 3;
+        attr.value.arrayDimensions[1] = 3;
+        attr.value.arrayDimensionsSize = 2;
+        attr.value.arrayLength = 9;
+        attr.value.data = myMultiArray;
+        attr.value.type = &UA_TYPES[type];
+        UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id),
+                                  UA_NODEID_NUMERIC(1, MATRIXID),
+                                  UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                  qualifiedName, UA_NODEID_NULL, attr, NULL);
+        UA_Variant_deleteMembers(&attr.value);
+    }
 
 #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_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);
-  
-  // Dettach the method from the methodNode
-  UA_Server_setAttribute_method(server, UA_NODEID_NUMERIC(1,62541), NULL, NULL);
-  
-  // Reaettach the method from the methodNode
-  UA_Server_setAttribute_method(server, UA_NODEID_NUMERIC(1,62541), &getMonitoredItems, (void *) server);
+    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_MethodAttributes addmethodattributes;
+    UA_MethodAttributes_init(&addmethodattributes);
+    addmethodattributes.description = UA_LOCALIZEDTEXT("en_US", "Return a single argument as passed by the caller");
+    addmethodattributes.displayName = UA_LOCALIZEDTEXT("en_US", "ping");
+    addmethodattributes.executable = UA_TRUE;
+    addmethodattributes.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1,"ping"), addmethodattributes,
+                            &getMonitoredItems, // Call this method
+                            (void *) server,    // Pass our server pointer as a handle to the method
+                            1, &inputArguments, 1, &outputArguments, NULL);
 #endif
    
-  // 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);
+    // 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_setAttribute_displayName(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), &objectsName);
+    // Some easy localization
+    UA_LocalizedText objectsName = UA_LOCALIZEDTEXT("de_DE", "Objekte");
+    UA_Server_setNodeAttribute_displayName(server, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), objectsName);
   
-  //start server
-  UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+    //start server
+    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
 
-  //ctrl-c received -> clean up
-  UA_Server_delete(server);
+    //ctrl-c received -> clean up
+    UA_Server_delete(server);
 
-  if(temperatureFile)
-          fclose(temperatureFile);
+    if(temperatureFile)
+        fclose(temperatureFile);
 
-  if(triggerFile) {
-          fseek(triggerFile, 0, SEEK_SET);
-          //setting led mode to default
-          fprintf(triggerFile, "%s", "mmc0");
-          fclose(triggerFile);
-  }
+    if(triggerFile) {
+        fseek(triggerFile, 0, SEEK_SET);
+        //setting led mode to default
+        fprintf(triggerFile, "%s", "mmc0");
+        fclose(triggerFile);
+    }
   
-  if(ledFile)
-    fclose(ledFile);
+    if(ledFile)
+        fclose(ledFile);
 
 #ifdef UA_MULTITHREADING
-  pthread_rwlock_destroy(&writeLock);
+    pthread_rwlock_destroy(&writeLock);
 #endif
 
-  return retval;
+    return retval;
 }

+ 29 - 14
examples/server.cpp

@@ -2,6 +2,8 @@
  * 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 <iostream>
 #include <cstring>
 
@@ -21,29 +23,42 @@
 
 using namespace std;
 
-int main()
-{
+UA_Logger logger;
+UA_Boolean running = 1;
+
+static void stopHandler(int sign) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
+    running = 0;
+}
+
+int main() {
+    signal(SIGINT, stopHandler); /* catches ctrl-c */
+
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
-
-    UA_Logger logger = Logger_Stdout_new();
+    logger = Logger_Stdout_new();
     UA_Server_setLogger(server, logger);
 
     // add a variable node to the adresspace
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_NULL; /* assign a random free nodeid */
+    UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(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("", "the.answer"),
-                              UA_LOCALIZEDTEXT("", ""), 0, 0,
-                              parentNodeId, parentReferenceNodeId,
-                              myIntegerVariant, NULL);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
+
+    /* allocations on the heap need to be freed */
+    UA_VariableAttributes_deleteMembers(&attr);
+    UA_NodeId_deleteMembers(&myIntegerNodeId);
+    UA_QualifiedName_deleteMembers(&myIntegerName);
 
-    UA_Boolean running = UA_TRUE;
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
 

+ 12 - 13
examples/server_datasource.c

@@ -54,20 +54,19 @@ int main(int argc, char** argv) {
     UA_Int32 myInteger = 42;
 
     /* add a variable node to the address space */
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* UA_NODEID_NULL would assign a random free nodeid */
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_LocalizedText myIntegerBrowseName = UA_LOCALIZEDTEXT("en_US","the answer");
-
-    UA_DataSource dateDataSource = (UA_DataSource) {.handle = &myInteger, .read = readInteger, .write = writeInteger};
-
-    UA_Server_addDataSourceVariableNode(server, myIntegerNodeId, myIntegerName, myIntegerBrowseName, myIntegerBrowseName, 0, 0,
-
-                                    UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                    UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-
-                                    dateDataSource,
-
-                                    NULL);
+    UA_DataSource dateDataSource = (UA_DataSource) {
+        .handle = &myInteger, .read = readInteger, .write = writeInteger};
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+
+    UA_Server_addDataSourceVariableNode(server, myIntegerNodeId,
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                        myIntegerName, UA_NODEID_NULL, attr, dateDataSource, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);

+ 37 - 22
examples/server_method.c

@@ -18,7 +18,9 @@
 UA_Boolean running = UA_TRUE;
 UA_Logger logger;
 
-static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant *input, UA_Variant *output, void *handle) {
+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) {
@@ -32,15 +34,12 @@ static UA_StatusCode helloWorldMethod(const UA_NodeId objectId, const UA_Variant
         return UA_STATUSCODE_GOOD;
 } 
 
-static UA_StatusCode IncInt32ArrayValuesMethod(const UA_NodeId objectId,
-                                         const UA_Variant *input, UA_Variant *output, void *handle) {
-
-
-	UA_Variant_setArrayCopy(output,input->data,5,&UA_TYPES[UA_TYPES_INT32]);
-	for(int i = 0; i< input->arrayLength; i++){
+static UA_StatusCode
+IncInt32ArrayValuesMethod(const UA_NodeId objectId, const UA_Variant *input,
+                          UA_Variant *output, void *handle) {
+	UA_Variant_setArrayCopy(output, input->data, 5, &UA_TYPES[UA_TYPES_INT32]);
+	for(int i = 0; i< input->arrayLength; i++)
 		((UA_Int32*)output->data)[i] = ((UA_Int32*)input->data)[i] + 1;
-	}
-
 	return UA_STATUSCODE_GOOD;
 }
 
@@ -48,6 +47,7 @@ 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 */
 
@@ -77,10 +77,18 @@ int main(int argc, char** argv) {
     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_HASCOMPONENT),
-                            0, 0, &helloWorldMethod, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+    UA_MethodAttributes helloAttr;
+    UA_MethodAttributes_init(&helloAttr);
+    helloAttr.description = UA_LOCALIZEDTEXT("en_US","Say `Hello World`");
+    helloAttr.displayName = UA_LOCALIZEDTEXT("en_US","Hello World");
+    helloAttr.executable = UA_TRUE;
+    helloAttr.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1,62541),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                            UA_QUALIFIEDNAME(1, "hello world"), 
+                            helloAttr, &helloWorldMethod, NULL,
+                            1, &inputArguments, 1, &outputArguments, NULL);
 
     //END OF EXAMPLE 1
 
@@ -105,20 +113,27 @@ int main(int argc, char** argv) {
     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 by one");
+    outputArguments.description = UA_LOCALIZEDTEXT("en_US", "increment each array index");
+    outputArguments.name = UA_STRING("output is the array, each index is incremented by one");
     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, &IncInt32ArrayValuesMethod, NULL, 1, &inputArguments, 1, &outputArguments, NULL);
+    
+    UA_MethodAttributes incAttr;
+    UA_MethodAttributes_init(&incAttr);
+    incAttr.description = UA_LOCALIZEDTEXT("en_US","1dArrayExample");
+    incAttr.displayName = UA_LOCALIZEDTEXT("en_US","1dArrayExample");
+    incAttr.executable = UA_TRUE;
+    incAttr.userExecutable = UA_TRUE;
+    UA_Server_addMethodNode(server, UA_NODEID_STRING(1, "IncInt32ArrayValues"),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT), 
+                            UA_QUALIFIEDNAME(1, "IncInt32ArrayValues"),
+                            incAttr, &IncInt32ArrayValuesMethod, NULL,
+                            1, &inputArguments, 1, &outputArguments, NULL);
     //END OF EXAMPLE 2
 
     /* start server */
-    UA_StatusCode retval = UA_Server_run(server, 1, &running); //blocks until running=false
+    UA_StatusCode retval = UA_Server_run(server, 1, &running);
 
     /* ctrl-c received -> clean up */
     UA_UInt32_delete(pInputDimensions);

+ 10 - 8
examples/server_udp.c

@@ -30,17 +30,19 @@ int main(int argc, char** argv) {
     UA_Server_addNetworkLayer(server, nl);
 
 	// add a variable node to the adresspace
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     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_LocalizedText myIntegerBrowseName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(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, myIntegerBrowseName,
-                              myIntegerBrowseName, 0,0,
-                              parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);

+ 20 - 14
examples/server_variable.c

@@ -22,12 +22,15 @@ static void stopHandler(int sign) {
     running = 0;
 }
 
-static void onRead(void *handle, const UA_NodeId nodeid,  const UA_Variant *data, const UA_NumericRange *range){
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onRead; handle is: %i", (uintptr_t)handle);
+static void onRead(void *handle, const UA_NodeId nodeid, const UA_Variant *data,
+                   const UA_NumericRange *range) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND,
+                "onRead; handle is: %i", (uintptr_t)handle);
 }
 
-static void onWrite(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range){
-    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onWrite; handle is: %i", (uintptr_t)handle);
+static void onWrite(void *h, const UA_NodeId nodeid, const UA_Variant *data,
+                    const UA_NumericRange *range) {
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onWrite; handle: %i", (uintptr_t)h);
 }
 
 int main(int argc, char** argv) {
@@ -36,24 +39,27 @@ int main(int argc, char** argv) {
     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));
+    UA_ServerNetworkLayer *nl;
+    nl = ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664);
+    UA_Server_addNetworkLayer(server, nl);
 
     /* add a variable node to the address space */
-    UA_Variant *myIntegerVariant = UA_Variant_new();
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    //NOTE: the link between myInteger and the value of the node is lost here, you can safely reuse myInteger
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
     UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
-    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); /* UA_NODEID_NULL would assign a random free nodeid */
-    UA_LocalizedText myIntegerBrowseName = UA_LOCALIZEDTEXT("en_US","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, myIntegerBrowseName, myIntegerBrowseName, 0, 0,
-                              parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
+    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                              parentReferenceNodeId, myIntegerName,
+                              UA_NODEID_NULL, attr, NULL);
 
     UA_ValueCallback callback = {(void*)7, onRead, onWrite};
-    UA_Server_setAttribute_valueCallback(server, myIntegerNodeId, callback);
+    UA_Server_setAttribute_value_callback(server, myIntegerNodeId, callback);
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);

+ 381 - 385
include/ua_server.h

@@ -28,11 +28,9 @@ extern "C" {
 #include "ua_job.h"
 #include "ua_connection.h"
 
-/**
- * @defgroup server Server
- *
- * @{
- */
+/*********************************/
+/* Initialize and run the server */
+/*********************************/
 
 typedef struct UA_ServerConfig {
     UA_Boolean  Login_enableAnonymous;
@@ -54,31 +52,112 @@ void UA_EXPORT UA_Server_delete(UA_Server *server);
 
 /** Sets the logger used by the server */
 void UA_EXPORT UA_Server_setLogger(UA_Server *server, UA_Logger logger);
-UA_Logger UA_EXPORT UA_Server_getLogger(UA_Server *server);
 
 /**
  * Runs the main loop of the server. In each iteration, this calls into the networklayers to see if
  * jobs have arrived and checks if repeated jobs need to be triggered.
  *
  * @param server The server object
- *
  * @param nThreads The number of worker threads. Is ignored if MULTITHREADING is not activated.
- *
  * @param running Points to a boolean value on the heap. When running is set to false, the worker
- * threads and the main loop close and the server is shut down.
- *
+ *        threads and the main loop close and the server is shut down.
  * @return Indicates whether the server shut down cleanly
- *
  */
 UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
 
-/* The prologue part of UA_Server_run (no need to use if you call UA_Server_run) */
+/** The prologue part of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_startup(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
-/* The epilogue part of UA_Server_run (no need to use if you call UA_Server_run) */
+
+/** The epilogue part of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads);
-/* One iteration of UA_Server_run (no need to use if you call UA_Server_run) */
+
+/** One iteration of UA_Server_run (no need to use if you call UA_Server_run) */
 UA_StatusCode UA_EXPORT UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running);
 
+/**
+ * @param server The server object.
+ * @param job The job that shall be added.
+ * @param interval The job shall be repeatedly executed with the given interval (in ms). The
+ *        interval must be larger than 5ms. The first execution occurs at now() + interval at the
+ *        latest.
+ * @param jobId Set to the guid of the repeated job. This can be used to cancel the job later on. If
+ *        the pointer is null, the guid is not set.
+ * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
+ */
+UA_StatusCode UA_EXPORT UA_Server_addRepeatedJob(UA_Server *server, UA_Job job,
+                                                 UA_UInt32 interval, UA_Guid *jobId);
+
+/**
+ * Remove repeated job. The entry will be removed asynchronously during the next iteration of the
+ * server main loop.
+ *
+ * @param server The server object.
+ * @param jobId The id of the job that shall be removed.
+ * @return Upon sucess, UA_STATUSCODE_GOOD is returned. An error code otherwise.
+ */
+UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId);
+
+/**
+ * Interface to the binary network layers. This structure is returned from the
+ * function that initializes the network layer. The layer is already bound to a
+ * specific port and listening. The functions in the structure are never called
+ * in parallel but only sequentially from the server's main loop. So the network
+ * layer does not need to be thread-safe.
+ */
+typedef struct UA_ServerNetworkLayer {
+    UA_String discoveryUrl;
+    UA_Logger logger; ///< Set during _start
+
+    /**
+     * Starts listening on the the networklayer.
+     *
+     * @param nl The network layer
+     * @param logger The logger
+     * @return Returns UA_STATUSCODE_GOOD or an error code.
+     */
+    UA_StatusCode (*start)(struct UA_ServerNetworkLayer *nl, UA_Logger logger);
+    
+    /**
+     * Gets called from the main server loop and returns the jobs (accumulated messages and close
+     * events) for dispatch.
+     *
+     * @param nl The network layer
+     * @param jobs When the returned integer is >0, *jobs points to an array of UA_Job of the
+     * returned size.
+     * @param timeout The timeout during which an event must arrive in microseconds
+     * @return The size of the jobs array. If the result is negative, an error has occurred.
+     */
+    size_t (*getJobs)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout);
+
+    /**
+     * Closes the network connection and returns all the jobs that need to be finished before the
+     * network layer can be safely deleted.
+     *
+     * @param nl The network layer
+     * @param jobs When the returned integer is >0, jobs points to an array of UA_Job of the
+     * returned size.
+     * @return The size of the jobs array. If the result is negative, an error has occurred.
+     */
+    size_t (*stop)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs);
+
+    /** Deletes the network layer. Call only after a successful shutdown. */
+    void (*deleteMembers)(struct UA_ServerNetworkLayer *nl);
+} UA_ServerNetworkLayer;
+
+/**
+ * Adds a network layer to the server. The network layer is destroyed together
+ * with the server. Do not use it after adding it as it might be moved around on
+ * the heap.
+ */
+void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer *networkLayer);
+
+/** @brief Add a new namespace to the server. Returns the index of the new namespace */
+UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
+
+/***************/
+/* Data Source */
+/***************/
+
 /**
  * Datasources are the interface to local data providers. It is expected that
  * the read and release callbacks are implemented. The write callback can be set
@@ -126,398 +205,346 @@ typedef struct {
     void (*onWrite)(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range);
 } UA_ValueCallback;
 
-/** @brief Add a new namespace to the server. Returns the index of the new namespace */
-UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
+/*******************/
+/* Node Management */
+/*******************/
 
 /** Add a reference to the server's address space */
-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
+UA_StatusCode UA_EXPORT
+UA_Server_addReference(UA_Server *server, const UA_NodeId sourceId, const UA_NodeId refTypeId,
+                       const UA_ExpandedNodeId targetId, UA_Boolean isForward);
 
- * @return Return UA_STATUSCODE_GOOD if the node was deleted or an appropriate errorcode if the node was not found
- *         or cannot be deleted.
- */
+/* Don't use this function. There are typed versions as inline functions. */
 UA_StatusCode UA_EXPORT
-UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId);
+UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass, const UA_NodeId requestedNewNodeId,
+                  const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                  const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                  const UA_NodeAttributes *attr, const UA_DataType *attributeType, UA_NodeId *outNewNodeId);
 
-#define UA_SERVER_DELETENODEALIAS_DECL(TYPE) \
-UA_StatusCode UA_EXPORT UA_Server_delete##TYPE##Node(UA_Server *server, UA_NodeId nodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_addVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                          const UA_VariableAttributes attr, UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_VARIABLE, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, typeDefinition, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_VARIABLEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                              const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                              const UA_QualifiedName browseName, const UA_VariableTypeAttributes attr,
+                              UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_VARIABLETYPE, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_VARIABLETYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addObjectNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                        const UA_ObjectAttributes attr, UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_OBJECT, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, typeDefinition, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_OBJECTATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                            const UA_QualifiedName browseName, const UA_ObjectTypeAttributes attr,
+                            UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_OBJECTTYPE, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addViewNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                      const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                      const UA_QualifiedName browseName, const UA_ViewAttributes attr,
+                      UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_VIEW, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_VIEWATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addReferenceTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                               const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                               const UA_QualifiedName browseName, const UA_ReferenceTypeAttributes attr,
+                               UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_REFERENCETYPE, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES], outNewNodeId); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_addDataTypeNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                          const UA_QualifiedName browseName, const UA_DataTypeAttributes attr,
+                          UA_NodeId *outNewNodeId) {
+    return UA_Server_addNode(server, UA_NODECLASS_DATATYPE, requestedNewNodeId, parentNodeId,
+                             referenceTypeId, browseName, UA_NODEID_NULL, (const UA_NodeAttributes*)&attr,
+                             &UA_TYPES[UA_TYPES_DATATYPEATTRIBUTES], outNewNodeId); }
 
-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)
+UA_StatusCode UA_EXPORT
+UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                                    const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                                    const UA_QualifiedName browseName, const UA_NodeId typeDefinition,
+                                    const UA_VariableAttributes attr, const UA_DataSource dataSource,
+                                    UA_NodeId *outNewNodeId);
 
 #ifdef ENABLE_METHODCALLS
-UA_SERVER_DELETENODEALIAS_DECL(Method)
+typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Variant *input,
+                                           UA_Variant *output, void *handle);
+UA_StatusCode UA_EXPORT
+UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
+                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_QualifiedName browseName, const UA_MethodAttributes attr,
+                        UA_MethodCallback method, void *handle,
+                        UA_Int32 inputArgumentsSize, const UA_Argument* inputArguments, 
+                        UA_Int32 outputArgumentsSize, const UA_Argument* outputArguments,
+                        UA_NodeId *outNewNodeId);
 #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);
+UA_StatusCode UA_EXPORT UA_Server_deleteNode(UA_Server *server, UA_NodeId nodeId);
 
-/** 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);
+typedef UA_StatusCode (*UA_NodeIteratorCallback) (UA_NodeId childId, UA_Boolean isInverse,
+                                                  UA_NodeId referenceTypeId, void *handle);
 
-/** 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 userWriteMask Bitmask defining the user write permissions
- * @param writeMask     Bitmask defining the write permissions
- * @param parentNodeId  The node under which this node exists ("parent")
- * @param referenceTypeId Reference type used by the parent to reference this node
- * @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.
+/** Iterate over all nodes referenced by parentNodeId by calling the callback function for each
+ * child node
  * 
- * @return Return UA_STATUSCODE_GOOD if the node was created or an appropriate error code if not.
+ * @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_addVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                          const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
+                               UA_NodeIteratorCallback callback, void *handle);
+
+/***********************/
+/* Set Node Attributes */
+/***********************/
+
+/* The following node attributes cannot be changed once the node is created
+   - NodeClass
+   - NodeId
+   - Symmetric
+   
+   The following attributes will eventually be managed by a userrights layer and are unsupported yet
+   - WriteMask
+   - UserWriteMask
+   - AccessLevel
+   - UserAccessLevel
+   - UserExecutable
+
+   The following attributes are currently taken from the value variant:
+   - DataType
+   - ValueRank
+   - ArrayDimensions
+   
+   - Historizing is currently unsupported
+  */
 
-                          const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                          UA_Variant *value,
-
-                          UA_NodeId *createdNodeId);
-
-// Missing: eventNotifier
 UA_StatusCode UA_EXPORT
-UA_Server_addObjectNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
-                        const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                        const UA_ExpandedNodeId typeDefinition,
-
-                        UA_NodeId *createdNodeId);
-
-// Missing: isAbstract, symmetric
-UA_StatusCode UA_EXPORT 
-UA_Server_addReferenceTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                               const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_setNodeAttribute_value(UA_Server *server, const UA_NodeId nodeId,
+                                 const UA_DataType *type, const UA_Variant value);
 
-                               const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                               const UA_ExpandedNodeId typeDefinition,
-                               const UA_LocalizedText inverseName,
-
-                               UA_NodeId *createdNodeId );
+/* The value is moved into the node (not copied). The value variant is _inited internally. */
+UA_StatusCode UA_EXPORT
+UA_Server_setNodeAttribute_value_destructive(UA_Server *server, const UA_NodeId nodeId,
+                                             const UA_DataType *type, UA_Variant *value);
 
+/* Succeeds only if the node contains a variant value */
 UA_StatusCode UA_EXPORT
-UA_Server_addObjectTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                            const UA_LocalizedText displayName, const UA_LocalizedText description,  const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_setAttribute_value_callback(UA_Server *server, const UA_NodeId nodeId,
+                                      const UA_ValueCallback callback);
 
-                            const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+UA_StatusCode UA_EXPORT
+UA_Server_setNodeAttribute_value_dataSource(UA_Server *server, const UA_NodeId nodeId,
+                                            const UA_DataSource dataSource);
 
-                            const UA_ExpandedNodeId typeDefinition,
-                            const UA_Boolean isAbstract,
+/* Don't use this function. There are typed versions with no additional overhead. */
+UA_StatusCode UA_EXPORT
+UA_Server_setNodeAttribute(UA_Server *server, const UA_NodeId nodeId, const UA_AttributeId attributeId,
+                           const UA_DataType *type, const void *value);
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_browseName(UA_Server *server, const UA_NodeId nodeId,
+                                      const UA_QualifiedName browseName) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_BROWSENAME,
+                                      &UA_TYPES[UA_TYPES_QUALIFIEDNAME], &browseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_displayName(UA_Server *server, const UA_NodeId nodeId,
+                                       const UA_LocalizedText displayName) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
+                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &displayName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_description(UA_Server *server, const UA_NodeId nodeId,
+                                       const UA_LocalizedText description) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_DESCRIPTION,
+                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &description); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_isAbstract(UA_Server *server, const UA_NodeId nodeId,
+                                      const UA_Boolean isAbstract) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_ISABSTRACT,
+                                      &UA_TYPES[UA_TYPES_BOOLEAN], &isAbstract); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_inverseName(UA_Server *server, const UA_NodeId nodeId,
+                                       const UA_LocalizedText inverseName) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_INVERSENAME,
+                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT], &inverseName); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_containtsNoLoops(UA_Server *server, const UA_NodeId nodeId,
+                                            const UA_Boolean containsNoLoops) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS,
+                                      &UA_TYPES[UA_TYPES_BOOLEAN], &containsNoLoops); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_eventNotifier(UA_Server *server, const UA_NodeId nodeId,
+                                         const UA_Byte eventNotifier) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
+                                      &UA_TYPES[UA_TYPES_BYTE], &eventNotifier); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_minimumSamplingInterval(UA_Server *server, const UA_NodeId nodeId,
+                                                   const UA_Double miniumSamplingInterval) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
+                                      &UA_TYPES[UA_TYPES_DOUBLE], &miniumSamplingInterval); }
+
+static UA_INLINE UA_StatusCode
+UA_Server_setNodeAttribute_executable(UA_Server *server, const UA_NodeId nodeId,
+                                      const UA_Boolean executable) {
+    return UA_Server_setNodeAttribute(server, nodeId, UA_ATTRIBUTEID_EXECUTABLE,
+                                      &UA_TYPES[UA_TYPES_BOOLEAN], &executable); }
 
-                            UA_NodeId *createdNodeId );
+#ifdef ENABLE_METHODCALLS
+UA_StatusCode UA_EXPORT
+UA_Server_setNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId,
+                                  UA_MethodCallback method, void *handle);
+#endif
 
-UA_StatusCode UA_EXPORT 
-UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                              const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+typedef struct {
+    void * (*constructor)(const UA_NodeId instance); ///< Returns the instance handle attached to the node
+    void (*destructor)(const UA_NodeId instance, void *instanceHandle);
+} UA_ObjectInstanceManagement;
 
-                              const UA_NodeId parentNodeId,
-                              const UA_NodeId referenceTypeId,
+UA_StatusCode UA_EXPORT
+UA_Server_setObjectInstanceManagement(UA_Server *server, UA_NodeId nodeId,
+                                      UA_ObjectInstanceManagement oim);
 
-                              UA_Variant *value,
-                              const UA_Int32 valueRank,
-                              const UA_Boolean isAbstract,
+/***********************/
+/* Get Node Attributes */
+/***********************/
 
-                              UA_NodeId *createdNodeId);
+/* The following attributes cannot be read. They make no sense to read internally since the "admin"
+   user always has all rights.
+   - UserWriteMask
+   - UserAccessLevel
+   - UserExecutable
+*/
 
+/* Don't use this function. There are typed versions for every supported attribute. */
 UA_StatusCode UA_EXPORT
-UA_Server_addDataTypeNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                          const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+UA_Server_getNodeAttribute(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void *v);
+  
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_nodeId(UA_Server *server, UA_NodeId nodeId, UA_NodeId *outNodeId) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_NODEID, outNodeId); }
 
-                          const UA_NodeId parentNodeId,
-                          const UA_NodeId referenceTypeId,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_nodeClass(UA_Server *server, UA_NodeId nodeId, UA_NodeClass *outNodeClass) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_NODECLASS, outNodeClass); }
 
-                          const UA_ExpandedNodeId typeDefinition,
-                          const UA_Boolean isAbstract,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_browseName(UA_Server *server, UA_NodeId nodeId, UA_QualifiedName *outBrowseName) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_BROWSENAME, outBrowseName); }
 
-                          UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_displayName(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outDisplayName) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_DISPLAYNAME, outDisplayName); }
 
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_description(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outDescription) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_DESCRIPTION, outDescription); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_addViewNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                      const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
-                      const UA_NodeId parentNodeId,
-                      const UA_NodeId referenceTypeId,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_writeMask(UA_Server *server, UA_NodeId nodeId, UA_UInt32 *outWriteMask) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_WRITEMASK, outWriteMask); }
 
-                      const UA_ExpandedNodeId typeDefinition,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_isAbstract(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outIsAbstract) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_ISABSTRACT, outIsAbstract); }
 
-                      UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_symmetric(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outSymmetric) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_SYMMETRIC, outSymmetric); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                                    const UA_LocalizedText displayName, const UA_LocalizedText description,  const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_inverseName(UA_Server *server, UA_NodeId nodeId, UA_LocalizedText *outInverseName) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_INVERSENAME, outInverseName); }
 
-                                    const UA_NodeId parentNodeId,
-                                    const UA_NodeId referenceTypeId,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_containsNoLoops(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outContainsNoLoops) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_CONTAINSNOLOOPS, outContainsNoLoops); }
 
-                                    const UA_DataSource dataSource,
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_eventNotifier(UA_Server *server, UA_NodeId nodeId, UA_Byte *outEventNotifier) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER, outEventNotifier); }
 
-                                    UA_NodeId *createdNodeId);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_value(UA_Server *server, UA_NodeId nodeId, UA_Variant *outValue) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_VALUE, outValue); }
 
-/* --------------------- */
 UA_StatusCode UA_EXPORT
-UA_Server_addMonodirectionalReference(UA_Server *server, UA_NodeId sourceNodeId,
-                                      UA_ExpandedNodeId targetNodeId, UA_NodeId referenceTypeId,
-                                      UA_Boolean isforward);
+UA_Server_getNodeAttribute_value_dataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *dataSource);
 
-#ifdef ENABLE_METHODCALLS
-typedef UA_StatusCode (*UA_MethodCallback)(const UA_NodeId objectId, const UA_Variant *input,
-                                           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_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
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_dataType(UA_Server *server, UA_NodeId nodeId, UA_Variant *outDataType) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_DATATYPE, outDataType); }
 
-#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
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_valueRank(UA_Server *server, UA_NodeId nodeId, UA_Int32 *outValueRank) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_VALUERANK, outValueRank); }
 
-/** 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);
-// Attribute specific macros for setAttribute_are defined in ua_server_addressspace.c
-#define UA_Server_setAttribute_nodeId(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODEID, (UA_NodeId *) VALUE);
-#define UA_Server_setAttribute_nodeClass(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODECLASS, (UA_NodeClass *) VALUE);
-#define UA_Server_setAttribute_browseName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_BROWSENAME, (UA_QualifiedName *) VALUE);
-#define UA_Server_setAttribute_displayName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DISPLAYNAME, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_description(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DESCRIPTION, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_writeMask(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_WRITEMASK, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_userWriteMask(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERWRITEMASK, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_isAbstract(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ISABSTRACT, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_symmetric(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_SYMMETRIC, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_inverseName(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_INVERSENAME, (UA_LocalizedText *) VALUE);
-#define UA_Server_setAttribute_containsNoLoops(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_CONTAINSNOLOOPS, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_eventNotifier(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EVENTNOTIFIER, (UA_Byte *) VALUE);
-#define UA_Server_setAttribute_value(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUE, (UA_Variant *) VALUE);
-#define UA_Server_setAttribute_dataType(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DATATYPE, (UA_NodeId *) VALUE);
-#define UA_Server_setAttribute_valueRank(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUERANK, (UA_Int32 *) VALUE);
-#define UA_Server_setAttribute_arrayDimensions(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ARRAYDIMENSIONS, (UA_Int32 *) VALUE);
-#define UA_Server_setAttribute_accessLevel(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ACCESSLEVEL, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_userAccessLevel(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERACCESSLEVEL, (UA_UInt32 *) VALUE);
-#define UA_Server_setAttribute_minimumSamplingInterval(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, (UA_Double *) VALUE);
-#define UA_Server_setAttribute_historizing(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_HISTORIZING, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_executable(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EXECUTABLE, (UA_Boolean *) VALUE);
-#define UA_Server_setAttribute_userExecutable(SERVER, NODEID, VALUE) UA_Server_setAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USEREXECUTABLE, (UA_Boolean *) VALUE);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_arrayDimensions(UA_Server *server, UA_NodeId nodeId, UA_Int32 *outArrayDimensions) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_ARRAYDIMENSIONS, outArrayDimensions); }
 
-#ifdef ENABLE_METHODCALLS
-UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback method, void *handle);
-#endif
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_accessLevel(UA_Server *server, UA_NodeId nodeId, UA_UInt32 *outAccessLevel) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_ACCESSLEVEL, outAccessLevel); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource value);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_minimumSamplingInterval(UA_Server *server, UA_NodeId nodeId,
+                                                   UA_Double *outMinimumSamplingInterval) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
+                                      outMinimumSamplingInterval); }
 
-/* Succeeds only if the node contains a variant value */
-UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_valueCallback(UA_Server *server, UA_NodeId nodeId, UA_ValueCallback callback);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_historizing(UA_Server *server, UA_NodeId nodeId, UA_Double *outHistorizing) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_HISTORIZING, outHistorizing); }
 
-UA_StatusCode UA_EXPORT
-UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void **value);
-#define UA_Server_getAttribute_nodeId(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODEID, (UA_NodeId **) VALUE);
-#define UA_Server_getAttribute_nodeClass(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_NODECLASS, (UA_NodeClass **) VALUE);
-#define UA_Server_getAttribute_browseName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_BROWSENAME, (UA_QualifiedName **) VALUE);
-#define UA_Server_getAttribute_displayName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DISPLAYNAME, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_description(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DESCRIPTION, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_writeMask(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_WRITEMASK, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_userWriteMask(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERWRITEMASK, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_isAbstract(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ISABSTRACT, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_symmetric(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_SYMMETRIC, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_inverseName(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_INVERSENAME, (UA_LocalizedText **) VALUE);
-#define UA_Server_getAttribute_containsNoLoops(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_CONTAINSNOLOOPS, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_eventNotifier(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EVENTNOTIFIER, (UA_Byte **) VALUE);
-#define UA_Server_getAttribute_value(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUE, (UA_Variant **) VALUE);
-#define UA_Server_getAttribute_dataType(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_DATATYPE, (UA_NodeId **) VALUE);
-#define UA_Server_getAttribute_valueRank(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_VALUERANK, (UA_Int32 **) VALUE);
-#define UA_Server_getAttribute_arrayDimensions(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ARRAYDIMENSIONS, (UA_Int32 **) VALUE);
-#define UA_Server_getAttribute_accessLevel(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_ACCESSLEVEL, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_userAccessLevel(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USERACCESSLEVEL, (UA_UInt32 **) VALUE);
-#define UA_Server_getAttribute_minimumSamplingInterval(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL, (UA_Double **) VALUE);
-#define UA_Server_getAttribute_historizing(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_HISTORIZING, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_executable(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_EXECUTABLE, (UA_Boolean **) VALUE);
-#define UA_Server_getAttribute_userExecutable(SERVER, NODEID, VALUE) UA_Server_getAttributeValue(SERVER, NODEID, UA_ATTRIBUTEID_USEREXECUTABLE, (UA_Boolean **) VALUE);
+static UA_INLINE UA_StatusCode
+UA_Server_getNodeAttribute_executable(UA_Server *server, UA_NodeId nodeId, UA_Boolean *outExecutable) {
+    return UA_Server_getNodeAttribute(server, nodeId, UA_ATTRIBUTEID_EXECUTABLE, outExecutable); }
 
 #ifdef ENABLE_METHODCALLS
 UA_StatusCode UA_EXPORT
-UA_Server_getAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback *method);
+UA_Server_getNodeAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_MethodCallback *method);
 #endif
 
-UA_StatusCode UA_EXPORT
-UA_Server_getAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource **value);
-
-/**
- * @param server The server object.
- *
- * @param job Pointer to the job that shall be added. The pointer is not freed but copied to an
- *        internal representation.
- *
- * @param interval The job shall be repeatedly executed with the given interval (in ms). The
- *        interval must be larger than 5ms. The first execution occurs at now() + interval at the
- *        latest.
- *
- * @param jobId Set to the guid of the repeated job. This can be used to cancel the job later on. If
- *        the pointer is null, the guid is not set.
- *
- * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code otherwise.
- */
-UA_StatusCode UA_EXPORT UA_Server_addRepeatedJob(UA_Server *server, UA_Job job, UA_UInt32 interval,
-                                                 UA_Guid *jobId);
-
-/**
- * Remove repeated job. The entry will be removed asynchronously during the next iteration of the
- * server main loop.
- *
- * @param server The server object.
- *
- * @param jobId The id of the job that shall be removed.
- *
- * @return Upon sucess, UA_STATUSCODE_GOOD is returned. An error code otherwise.
- */
-UA_StatusCode UA_EXPORT UA_Server_removeRepeatedJob(UA_Server *server, UA_Guid jobId);
-
-/**
- * Interface to the binary network layers. This structure is returned from the
- * function that initializes the network layer. The layer is already bound to a
- * specific port and listening. The functions in the structure are never called
- * in parallel but only sequentially from the server's main loop. So the network
- * layer does not need to be thread-safe.
- */
-typedef struct UA_ServerNetworkLayer {
-    UA_String discoveryUrl;
-    UA_Logger logger; ///< Set during _start
-
-    /**
-     * Starts listening on the the networklayer.
-     *
-     * @param nl The network layer
-     * @param logger The logger
-     * @return Returns UA_STATUSCODE_GOOD or an error code.
-     */
-    UA_StatusCode (*start)(struct UA_ServerNetworkLayer *nl, UA_Logger logger);
-    
-    /**
-     * Gets called from the main server loop and returns the jobs (accumulated messages and close
-     * events) for dispatch.
-     *
-     * @param nl The network layer
-     * @param jobs When the returned integer is >0, *jobs points to an array of UA_Job of the
-     * returned size.
-     * @param timeout The timeout during which an event must arrive in microseconds
-     * @return The size of the jobs array. If the result is negative, an error has occurred.
-     */
-    size_t (*getJobs)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs, UA_UInt16 timeout);
-
-    /**
-     * Closes the network connection and returns all the jobs that need to be finished before the
-     * network layer can be safely deleted.
-     *
-     * @param nl The network layer
-     * @param jobs When the returned integer is >0, jobs points to an array of UA_Job of the
-     * returned size.
-     * @return The size of the jobs array. If the result is negative, an error has occurred.
-     */
-    size_t (*stop)(struct UA_ServerNetworkLayer *nl, UA_Job **jobs);
-
-    /** Deletes the network layer. Call only after a successful shutdown. */
-    void (*deleteMembers)(struct UA_ServerNetworkLayer *nl);
-} UA_ServerNetworkLayer;
-
-/**
- * Adds a network layer to the server. The network layer is destroyed together
- * with the server. Do not use it after adding it as it might be moved around on
- * the heap.
- */
-void UA_EXPORT UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer *networkLayer);
-
-/** @} */
-
+#ifdef UA_EXTERNAL_NAMESPACES
 /**
- * @ingroup nodestore
- *
- * @defgroup external_nodestore External Nodestore
- *
- * @brief An external application that manages its own data and data model
- *
- * To plug in outside data sources, one can use
+ * An external application that manages its own data and data model. To plug in
+ * outside data sources, one can use
  *
  * - VariableNodes with a data source (functions that are called for read and write access)
  * - An external nodestore that is mapped to specific namespaces
@@ -580,44 +607,13 @@ typedef struct UA_ExternalNodeStore {
 	UA_ExternalNodeStore_delete destroy;
 } UA_ExternalNodeStore;
 
-#ifdef UA_EXTERNAL_NAMESPACES
 UA_StatusCode UA_EXPORT
-UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url, UA_ExternalNodeStore *nodeStore);
-#endif /* UA_EXTERNAL_NAMESPACES*/
-/** @} */
-
-
-#ifndef _HAVE_UA_INSTANTIONCALLBACK_D
-#define _HAVE_UA_INSTANTIONCALLBACK_D
-typedef UA_StatusCode (*UA_InstantiationCallback)(UA_NodeId objectId, UA_NodeId definitionId, void *handle);
+UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url,
+                               UA_ExternalNodeStore *nodeStore);
 #endif
 
-typedef struct arrayOfNodeIds_s {
-  UA_Int32  size;
-  UA_NodeId *ids;
-} arrayOfNodeIds;
-
-UA_StatusCode UA_EXPORT
-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_EXPORT
-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
 
 #endif /* UA_SERVER_H_ */

+ 143 - 35
include/ua_types.h

@@ -318,11 +318,15 @@ void UA_EXPORT UA_String_init(UA_String *p);
 void UA_EXPORT UA_String_delete(UA_String *p);
 void UA_EXPORT UA_String_deleteMembers(UA_String *p);
 UA_StatusCode UA_EXPORT UA_String_copy(const UA_String *src, UA_String *dst);
-UA_String UA_EXPORT UA_String_fromChars(char const src[]); ///> Copies the char-array on the heap. Returns a null-string when alloc fails.
-UA_Boolean UA_EXPORT UA_String_equal(const UA_String *string1, const UA_String *string2); ///> Compares two strings
+UA_String UA_EXPORT UA_String_fromChars(char const src[]); ///> Copies the content on the heap. Returns a null-string when alloc fails.
+UA_Boolean UA_EXPORT UA_String_equal(const UA_String *s1, const UA_String *s2); ///> Compares two strings
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const fmt[], UA_String *dst, ...); ///> Printf a char-array into a UA_String. Memory for the string data is allocated.
-#define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
-#define UA_STRING(CHARS) (UA_String) {strlen(CHARS), (UA_Byte*)CHARS }
+UA_EXPORT extern const UA_String UA_STRING_NULL;
+static UA_INLINE UA_String UA_STRING(char *chars) {
+    UA_String str;
+    str.length = strlen(chars);
+    str.data = (UA_Byte*)chars;
+    return str; }
 #define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
 
 /* DateTime */
@@ -350,7 +354,7 @@ UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime time);
 
 /* Guid */
 UA_Guid UA_EXPORT * UA_Guid_new(void);
-static UA_INLINE void UA_Guid_init(UA_Guid *p) { *p = (UA_Guid){0,0,0,{0,0,0,0,0,0,0,0}}; }
+static UA_INLINE void UA_Guid_init(UA_Guid *p) { memset(p, 0, sizeof(UA_Guid)); }
 void UA_EXPORT UA_Guid_delete(UA_Guid *p);
 static UA_INLINE void UA_Guid_deleteMembers(UA_Guid *p) { }
 static UA_INLINE UA_StatusCode UA_Guid_copy(const UA_Guid *src, UA_Guid *dst) { *dst = *src; return UA_STATUSCODE_GOOD; }
@@ -362,17 +366,32 @@ static UA_INLINE UA_ByteString * UA_ByteString_new(void) { return UA_String_new(
 static UA_INLINE void UA_ByteString_init(UA_ByteString *p) { UA_String_init(p); }
 static UA_INLINE void UA_ByteString_delete(UA_ByteString *p) { UA_String_delete(p); }
 static UA_INLINE void UA_ByteString_deleteMembers(UA_ByteString *p) { UA_String_deleteMembers(p); }
-static UA_INLINE UA_StatusCode UA_ByteString_copy(const UA_ByteString *src, UA_ByteString *dst) { return UA_String_copy(src, dst); }
-#define UA_BYTESTRING_NULL (UA_ByteString) {-1, (UA_Byte*)0 }
+static UA_INLINE UA_StatusCode UA_ByteString_copy(const UA_ByteString *src, UA_ByteString *dst) {
+    return UA_String_copy(src, dst); }
 #define UA_ByteString_equal(string1, string2) UA_String_equal((const UA_String*) string1, (const UA_String*)string2)
-UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length); ///> Allocates memory of size length for the bytestring. The content is not set to zero.
+/* Allocates memory of size length for the bytestring. The content is not set to zero. */
+UA_StatusCode UA_EXPORT UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length);
+UA_EXPORT extern const UA_ByteString UA_BYTESTRING_NULL;
+static UA_INLINE UA_ByteString UA_BYTESTRING(char *chars) {
+    UA_ByteString str;
+    str.length = strlen(chars);
+    str.data = (UA_Byte*)chars;
+    return str; }
+static UA_INLINE UA_ByteString UA_BYTESTRING_ALLOC(const char *chars) {
+    UA_String str = UA_String_fromChars(chars);
+    UA_ByteString bstr;
+    bstr.length = str.length;
+    bstr.data = str.data;
+    return bstr;
+}
 
 /* XmlElement */
 static UA_INLINE UA_XmlElement * UA_XmlElement_new(void) { return UA_String_new(); }
 static UA_INLINE void UA_XmlElement_init(UA_XmlElement *p) { UA_String_init(p); }
 static UA_INLINE void UA_XmlElement_delete(UA_XmlElement *p) { UA_String_delete(p); }
 static UA_INLINE void UA_XmlElement_deleteMembers(UA_XmlElement *p) { UA_String_deleteMembers(p); }
-static UA_INLINE UA_StatusCode UA_XmlElement_copy(const UA_XmlElement *src, UA_XmlElement *dst) { return UA_String_copy(src, dst); }
+static UA_INLINE UA_StatusCode UA_XmlElement_copy(const UA_XmlElement *src, UA_XmlElement *dst) {
+    return UA_String_copy(src, dst); }
 
 /* NodeId */
 UA_NodeId UA_EXPORT * UA_NodeId_new(void);
@@ -392,13 +411,43 @@ UA_NodeId UA_EXPORT UA_NodeId_fromCharByteString(UA_UInt16 nsIndex, char identif
 UA_NodeId UA_EXPORT UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char const identifier[]);
 UA_NodeId UA_EXPORT UA_NodeId_fromByteString(UA_UInt16 nsIndex, UA_ByteString identifier);
 UA_NodeId UA_EXPORT UA_NodeId_fromByteStringCopy(UA_UInt16 nsIndex, UA_ByteString identifier);
-#define UA_NODEID_NUMERIC(NSID, NUMERICID) UA_NodeId_fromInteger(NSID, NUMERICID)
-#define UA_NODEID_STRING(NSID, CHARS) UA_NodeId_fromCharString(NSID, CHARS)
-#define UA_NODEID_STRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharStringCopy(NSID, CHARS)
-#define UA_NODEID_GUID(NSID, GUID) UA_NodeId_fromGuid(NSID, GUID)
-#define UA_NODEID_BYTESTRING(NSID, CHARS) UA_NodeId_fromCharByteString(NSID, CHARS)
-#define UA_NODEID_BYTESTRING_ALLOC(NSID, CHARS) UA_NodeId_fromCharByteStringCopy(NSID, CHARS)
-#define UA_NODEID_NULL (UA_NodeId){0, UA_NODEIDTYPE_NUMERIC, {0}}
+UA_EXPORT extern const UA_NodeId UA_NODEID_NULL;
+static UA_INLINE UA_NodeId UA_NODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_NUMERIC;
+    id.identifier.numeric = identifier;
+    return id; }
+static UA_INLINE UA_NodeId UA_NODEID_STRING(UA_UInt16 nsIndex, char *chars) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_STRING;
+    id.identifier.string = UA_STRING(chars);
+    return id; }
+static UA_INLINE UA_NodeId UA_NODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_STRING;
+    id.identifier.string = UA_STRING_ALLOC(chars);
+    return id; }
+static UA_INLINE UA_NodeId UA_NODEID_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_GUID;
+    id.identifier.guid = guid;
+    return id; }
+static UA_INLINE UA_NodeId UA_NODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    id.identifier.byteString = UA_BYTESTRING(chars);
+    return id; }
+static UA_INLINE UA_NodeId UA_NODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_NodeId id;
+    id.namespaceIndex = nsIndex;
+    id.identifierType = UA_NODEIDTYPE_BYTESTRING;
+    id.identifier.byteString = UA_BYTESTRING_ALLOC(chars);
+    return id; }
 
 /* ExpandedNodeId */
 UA_ExpandedNodeId UA_EXPORT * UA_ExpandedNodeId_new(void);
@@ -406,18 +455,51 @@ void UA_EXPORT UA_ExpandedNodeId_init(UA_ExpandedNodeId *p);
 void UA_EXPORT UA_ExpandedNodeId_delete(UA_ExpandedNodeId *p);
 void UA_EXPORT UA_ExpandedNodeId_deleteMembers(UA_ExpandedNodeId *p);
 UA_StatusCode UA_EXPORT UA_ExpandedNodeId_copy(const UA_ExpandedNodeId *src, UA_ExpandedNodeId *dst);
-UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
-#define UA_EXPANDEDNODEID_NUMERIC(NSID, NUMERICID) (UA_ExpandedNodeId) {            \
-        .nodeId = {.namespaceIndex = NSID, .identifierType = UA_NODEIDTYPE_NUMERIC, \
-                   .identifier = {.numeric = NUMERICID }},                          \
-        .namespaceUri = {.length = -1, .data = (UA_Byte*)0}, .serverIndex = 0 }
+UA_EXPORT extern const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL;
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_NUMERIC(UA_UInt16 nsIndex, UA_Int32 identifier) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_NUMERIC(nsIndex, identifier);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING(UA_UInt16 nsIndex, char *chars) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_STRING(nsIndex, chars);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_STRING_ALLOC(nsIndex, chars);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_STRING_GUID(UA_UInt16 nsIndex, UA_Guid guid) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_GUID(nsIndex, guid);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING(UA_UInt16 nsIndex, char *chars) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_BYTESTRING(nsIndex, chars);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
+static UA_INLINE UA_ExpandedNodeId UA_EXPANDEDNODEID_BYTESTRING_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_ExpandedNodeId id;
+    id.nodeId = UA_NODEID_BYTESTRING_ALLOC(nsIndex, chars);
+    id.serverIndex = 0;
+    id.namespaceUri = UA_STRING_NULL;
+    return id; }
 
 /* StatusCode */
-UA_StatusCode UA_EXPORT * UA_StatusCode_new(void);
+static UA_INLINE UA_StatusCode * UA_StatusCode_new(void) { return (UA_StatusCode*)UA_Int32_new(); }
 static UA_INLINE void UA_StatusCode_init(UA_StatusCode *p) { *p = UA_STATUSCODE_GOOD; }
 void UA_EXPORT UA_StatusCode_delete(UA_StatusCode *p);
 static UA_INLINE void UA_StatusCode_deleteMembers(UA_StatusCode *p) { }
-static UA_INLINE UA_StatusCode UA_StatusCode_copy(const UA_StatusCode *src, UA_StatusCode *dst) { *dst = *src; return UA_STATUSCODE_GOOD; }
+static UA_INLINE UA_StatusCode UA_StatusCode_copy(const UA_StatusCode *src, UA_StatusCode *dst) {
+    *dst = *src; return UA_STATUSCODE_GOOD; }
 
 /* QualifiedName */
 UA_QualifiedName UA_EXPORT * UA_QualifiedName_new(void);
@@ -425,8 +507,16 @@ void UA_EXPORT UA_QualifiedName_init(UA_QualifiedName *p);
 void UA_EXPORT UA_QualifiedName_delete(UA_QualifiedName *p);
 void UA_EXPORT UA_QualifiedName_deleteMembers(UA_QualifiedName *p);
 UA_StatusCode UA_EXPORT UA_QualifiedName_copy(const UA_QualifiedName *src, UA_QualifiedName *dst);
-#define UA_QUALIFIEDNAME(NSID, CHARS) (const UA_QualifiedName) { .namespaceIndex = NSID, .name = UA_STRING(CHARS) }
-#define UA_QUALIFIEDNAME_ALLOC(NSID, CHARS) (UA_QualifiedName) { .namespaceIndex = NSID, .name = UA_STRING_ALLOC(CHARS) }
+static UA_INLINE UA_QualifiedName UA_QUALIFIEDNAME(UA_UInt16 nsIndex, char *chars) {
+    UA_QualifiedName qn;
+    qn.namespaceIndex = nsIndex;
+    qn.name = UA_STRING(chars);
+    return qn; }
+static UA_INLINE UA_QualifiedName UA_QUALIFIEDNAME_ALLOC(UA_UInt16 nsIndex, const char *chars) {
+    UA_QualifiedName qn;
+    qn.namespaceIndex = nsIndex;
+    qn.name = UA_STRING_ALLOC(chars);
+    return qn; }
 
 /* LocalizedText */
 UA_LocalizedText UA_EXPORT * UA_LocalizedText_new(void);
@@ -434,8 +524,16 @@ void UA_EXPORT UA_LocalizedText_init(UA_LocalizedText *p);
 void UA_EXPORT UA_LocalizedText_delete(UA_LocalizedText *p);
 void UA_EXPORT UA_LocalizedText_deleteMembers(UA_LocalizedText *p);
 UA_StatusCode UA_EXPORT UA_LocalizedText_copy(const UA_LocalizedText *src, UA_LocalizedText *dst);
-#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (const UA_LocalizedText) { .locale = UA_STRING(LOCALE), .text = UA_STRING(TEXT) }
-#define UA_LOCALIZEDTEXT_ALLOC(LOCALE, TEXT) (UA_LocalizedText) { .locale = UA_STRING_ALLOC(LOCALE), .text = UA_STRING_ALLOC(TEXT) }
+static UA_INLINE UA_LocalizedText UA_LOCALIZEDTEXT(char *locale, char *text) {
+    UA_LocalizedText lt;
+    lt.locale = UA_STRING(locale);
+    lt.text = UA_STRING(text);
+    return lt; }
+static UA_INLINE UA_LocalizedText UA_LOCALIZEDTEXT_ALLOC(const char *locale, const char *text) {
+    UA_LocalizedText lt;
+    lt.locale = UA_STRING_ALLOC(locale);
+    lt.text = UA_STRING_ALLOC(text);
+    return lt; }
 
 /* ExtensionObject */
 UA_ExtensionObject UA_EXPORT * UA_ExtensionObject_new(void);
@@ -491,7 +589,8 @@ UA_StatusCode UA_EXPORT UA_Variant_setScalarCopy(UA_Variant *v, const void *p, c
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 elements, const UA_DataType *type);
+UA_StatusCode UA_EXPORT
+UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32 elements, const UA_DataType *type);
 
 /**
  * Set the variant to an array that is copied from an existing array.
@@ -502,7 +601,8 @@ UA_StatusCode UA_EXPORT UA_Variant_setArray(UA_Variant *v, void *array, UA_Int32
  * @param type The datatype of the array
  * @return Indicates whether the operation succeeded or returns an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 elements, const UA_DataType *type);
+UA_StatusCode UA_EXPORT
+UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32 elements, const UA_DataType *type);
 
 /**
  * Copy the variant, but use only a subset of the (multidimensional) array into a variant. Returns
@@ -513,12 +613,13 @@ UA_StatusCode UA_EXPORT UA_Variant_setArrayCopy(UA_Variant *v, const void *array
  * @param range The range of the copied data
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range);
 
 /**
  * Insert a range of data into an existing variant. The data array can't be reused afterwards if it
- * contains types without a fixed size (e.g. strings) since they take on the lifetime of the
- * variant.
+ * contains types without a fixed size (e.g. strings) since the members are moved into the variant
+ * and take on its lifecycle.
  *
  * @param v The variant
  * @param dataArray The data array. The type must match the variant
@@ -526,7 +627,8 @@ UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *
  * @param range The range of where the new data is inserted
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize, const UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_Int32 dataArraySize, const UA_NumericRange range);
 
 /**
  * Deep-copy a range of data into an existing variant.
@@ -537,7 +639,9 @@ UA_StatusCode UA_EXPORT UA_Variant_setRange(UA_Variant *v, void *dataArray, UA_I
  * @param range The range of where the new data is inserted
  * @return Returns UA_STATUSCODE_GOOD or an error code
  */
-UA_StatusCode UA_EXPORT UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize, const UA_NumericRange range);
+UA_StatusCode UA_EXPORT
+UA_Variant_setRangeCopy(UA_Variant *v, const void *dataArray, UA_Int32 dataArraySize,
+                        const UA_NumericRange range);
 
 /* DataValue */
 UA_DataValue UA_EXPORT * UA_DataValue_new(void);
@@ -626,7 +730,7 @@ UA_StatusCode UA_EXPORT UA_copy(const void *src, void *dst, const UA_DataType *d
  */
 void UA_EXPORT UA_deleteMembers(void *p, const UA_DataType *dataType);
 
-void UA_EXPORT UA_deleteMembersUntil(void *p, const UA_DataType *dataType, UA_Int32 lastMember);
+void UA_EXPORT UA_deleteMembersUntil(void *p, const UA_DataType *dataType, size_t lastMember);
 
 /**
  * Deletes (frees) a variable and all of its content.
@@ -671,6 +775,10 @@ UA_StatusCode UA_EXPORT UA_Array_copy(const void *src, void **dst, const UA_Data
  */
 void UA_EXPORT UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 elements);
 
+/**********************/
+/* Node Attribute Ids */
+/**********************/
+
 /* These are not generated from XML. Server *and* client need them. */
 typedef enum {
     UA_ATTRIBUTEID_NODEID                  = 1,

+ 2 - 1
src/client/ua_client.c

@@ -420,11 +420,12 @@ static UA_StatusCode EndpointsHandshake(UA_Client *client) {
 
     UA_Boolean endpointFound = UA_FALSE;
     UA_Boolean tokenFound = UA_FALSE;
+    UA_String securityNone = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
     for(UA_Int32 i=0; i<response.endpointsSize; ++i){
         UA_EndpointDescription* endpoint = &response.endpoints[i];
         /* look out for an endpoint without security */
         if(!UA_String_equal(&endpoint->securityPolicyUri,
-                            &UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None")))
+                            &securityNone))
             continue;
         endpointFound = UA_TRUE;
         /* endpoint with no security found */

+ 102 - 2
src/server/ua_nodes.c

@@ -40,11 +40,108 @@ static UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) {
 	return retval;
 }
 
+void UA_Node_deleteAnyNodeClass(UA_Node *node) {
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        UA_ObjectNode_delete((UA_ObjectNode*)node);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        UA_VariableNode_delete((UA_VariableNode*)node);
+        break;
+    case UA_NODECLASS_METHOD:
+        UA_MethodNode_delete((UA_MethodNode*)node);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        UA_ObjectTypeNode_delete((UA_ObjectTypeNode*)node);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        UA_VariableTypeNode_delete((UA_VariableTypeNode*)node);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode*)node);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        UA_DataTypeNode_delete((UA_DataTypeNode*)node);
+        break;
+    case UA_NODECLASS_VIEW:
+        UA_ViewNode_delete((UA_ViewNode*)node);
+        break;
+    default:
+        break;
+    }
+}
+
+typedef UA_Node *(*UA_NewNodeFunction)(void);
+typedef UA_StatusCode (*UA_CopyNodeFunction)(const UA_Node *src, UA_Node *dst);
+typedef void (*UA_DeleteNodeFunction)(UA_Node *p);
+
+UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node) {
+    UA_NewNodeFunction newNode;
+    UA_CopyNodeFunction copyNode;
+    UA_DeleteNodeFunction deleteNode;
+
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        newNode = (UA_NewNodeFunction)UA_ObjectNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ObjectNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ObjectNode_delete;
+        break;
+    case UA_NODECLASS_VARIABLE:
+        newNode = (UA_NewNodeFunction)UA_VariableNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_VariableNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_VariableNode_delete;
+        break;
+    case UA_NODECLASS_METHOD:
+        newNode = (UA_NewNodeFunction)UA_MethodNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_MethodNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_MethodNode_delete;
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        newNode = (UA_NewNodeFunction)UA_ObjectTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ObjectTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ObjectTypeNode_delete;
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        newNode = (UA_NewNodeFunction)UA_VariableTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_VariableTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_VariableTypeNode_delete;
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        newNode = (UA_NewNodeFunction)UA_ReferenceTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ReferenceTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ReferenceTypeNode_delete;
+        break;
+    case UA_NODECLASS_DATATYPE:
+        newNode = (UA_NewNodeFunction)UA_DataTypeNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_DataTypeNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_DataTypeNode_delete;
+        break;
+    case UA_NODECLASS_VIEW:
+        newNode = (UA_NewNodeFunction)UA_ViewNode_new;
+        copyNode = (UA_CopyNodeFunction)UA_ViewNode_copy;
+        deleteNode = (UA_DeleteNodeFunction)UA_ViewNode_delete;
+        break;
+    default:
+        return UA_NULL;
+        break;
+    }
+
+    UA_Node *copy = newNode();
+    if(!copy)
+        return UA_NULL;
+    if(copyNode(node, copy) != UA_STATUSCODE_GOOD) {
+        deleteNode(copy);
+        return UA_NULL;
+    }
+    return copy;
+}
+
 /* UA_ObjectNode */
 void UA_ObjectNode_init(UA_ObjectNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_OBJECT;
     p->eventNotifier = 0;
+    p->instanceHandle = UA_NULL;
 }
 
 UA_ObjectNode * UA_ObjectNode_new(void) {
@@ -65,6 +162,7 @@ void UA_ObjectNode_delete(UA_ObjectNode *p) {
 
 UA_StatusCode UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
     dst->eventNotifier = src->eventNotifier;
+    dst->instanceHandle = src->instanceHandle;
 	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
 }
 
@@ -73,6 +171,7 @@ void UA_ObjectTypeNode_init(UA_ObjectTypeNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_OBJECTTYPE;
     p->isAbstract = UA_FALSE;
+    p->instanceManagement = (UA_ObjectInstanceManagement){.constructor = UA_NULL, .destructor = UA_NULL};
 }
 
 UA_ObjectTypeNode * UA_ObjectTypeNode_new(void) {
@@ -93,6 +192,7 @@ void UA_ObjectTypeNode_delete(UA_ObjectTypeNode *p) {
 
 UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
     dst->isAbstract = src->isAbstract;
+    dst->instanceManagement = src->instanceManagement;
 	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
 }
 
@@ -133,10 +233,10 @@ UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT){
+    if(src->valueSource == UA_VALUESOURCE_VARIANT) {
         retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
         dst->value.variant.callback = src->value.variant.callback;
-    }else
+    } else
         dst->value.dataSource = src->value.dataSource;
     if(retval) {
         UA_VariableNode_deleteMembers(dst);

+ 7 - 1
src/server/ua_nodes.h

@@ -27,6 +27,9 @@ typedef struct {
     UA_STANDARD_NODEMEMBERS
 } UA_Node;
 
+void UA_Node_deleteAnyNodeClass(UA_Node *node);
+UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node);
+
 /**************/
 /* ObjectNode */
 /**************/
@@ -34,6 +37,7 @@ typedef struct {
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Byte eventNotifier;
+    void *instanceHandle;
 } UA_ObjectNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 
@@ -44,6 +48,7 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;
+    UA_ObjectInstanceManagement instanceManagement;
 } UA_ObjectTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectTypeNode)
 
@@ -134,8 +139,9 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_MethodNode)
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
-    UA_Boolean containsNoLoops;
     UA_Byte eventNotifier;
+    /* <-- the same as objectnode until here --> */
+    UA_Boolean containsNoLoops;
 } UA_ViewNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ViewNode)
 

Diferenças do arquivo suprimidas por serem muito extensas
+ 465 - 186
src/server/ua_server.c


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 1754
src/server/ua_server_addressspace.c


+ 7 - 4
src/server/ua_server_binary.c

@@ -324,9 +324,6 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
     case UA_NS0ID_BROWSENEXTREQUEST:
         INVOKE_SERVICE(BrowseNext, UA_TYPES_BROWSENEXTRESPONSE);
         break;
-    case UA_NS0ID_ADDREFERENCESREQUEST:
-        INVOKE_SERVICE(AddReferences, UA_TYPES_ADDREFERENCESRESPONSE);
-        break;
     case UA_NS0ID_REGISTERNODESREQUEST:
         INVOKE_SERVICE(RegisterNodes, UA_TYPES_REGISTERNODESRESPONSE);
         break;
@@ -361,13 +358,19 @@ static void processMSG(UA_Connection *connection, UA_Server *server, const UA_By
         INVOKE_SERVICE(Call, UA_TYPES_CALLRESPONSE);
 	break;
 #endif
-#ifdef ENABLE_ADDNODES 
+#ifdef ENABLE_NODEMANAGEMENT
     case UA_NS0ID_ADDNODESREQUEST:
         INVOKE_SERVICE(AddNodes, UA_TYPES_ADDNODESRESPONSE);
         break;
+    case UA_NS0ID_ADDREFERENCESREQUEST:
+        INVOKE_SERVICE(AddReferences, UA_TYPES_ADDREFERENCESRESPONSE);
+        break;
     case UA_NS0ID_DELETENODESREQUEST:
       INVOKE_SERVICE(DeleteNodes, UA_TYPES_DELETENODESRESPONSE);
       break;
+    case UA_NS0ID_DELETEREFERENCESREQUEST:
+      INVOKE_SERVICE(DeleteReferences, UA_TYPES_DELETEREFERENCESRESPONSE);
+      break;
 #endif
     default: {
         if(requestType.namespaceIndex == 0 && requestType.identifier.numeric==787)

+ 15 - 14
src/server/ua_server_internal.h

@@ -15,6 +15,7 @@
 #define ANONYMOUS_POLICY "open62541-anonymous-policy"
 #define USERNAME_POLICY "open62541-username-policy"
 
+#ifdef UA_EXTERNAL_NAMESPACES
 /** Mapping of namespace-id and url to an external nodestore. For namespaces
     that have no mapping defined, the internal nodestore is used by default. */
 typedef struct UA_ExternalNamespace {
@@ -22,6 +23,7 @@ typedef struct UA_ExternalNamespace {
 	UA_String url;
 	UA_ExternalNodeStore externalNodeStore;
 } UA_ExternalNamespace;
+#endif
 
 struct UA_Server {
     /* Config */
@@ -49,8 +51,11 @@ struct UA_Server {
     UA_NodeStore *nodestore;
     size_t namespacesSize;
     UA_String *namespaces;
+
+#ifdef UA_EXTERNAL_NAMESPACES
     size_t externalNamespacesSize;
     UA_ExternalNamespace *externalNamespaces;
+#endif
      
     /* Jobs with a repetition interval */
     LIST_HEAD(RepeatedJobsList, RepeatedJobs) repeatedJobs;
@@ -74,23 +79,19 @@ struct UA_Server {
 #endif
 };
 
-void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
+/* The node is assumed to be "finished", i.e. no instantiation from inheritance is necessary */
+void UA_Server_addExistingNode(UA_Server *server, UA_Session *session, UA_Node *node,
+                               const UA_NodeId *parentNodeId, const UA_NodeId *referenceTypeId,
+                               UA_AddNodesResult *result);
 
-UA_AddNodesResult UA_Server_addNodeWithSession(UA_Server *server, UA_Session *session, UA_Node *node,
-                                               const UA_ExpandedNodeId parentNodeId,
-                                               const UA_NodeId referenceTypeId);
+typedef UA_StatusCode (*UA_EditNodeCallback)(UA_Server *server, UA_Session*, UA_Node*, const void*);
 
-UA_AddNodesResult UA_Server_addNode(UA_Server *server, UA_Node *node, const UA_ExpandedNodeId parentNodeId,
-                                    const UA_NodeId referenceTypeId);
+/* Calls callback on the node. In the multithreaded case, the node is copied before and replaced in
+   the nodestore. */
+UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                                 UA_EditNodeCallback callback, const void *data);
 
-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);
+void UA_Server_processBinaryMessage(UA_Server *server, UA_Connection *connection, const UA_ByteString *msg);
 
 UA_StatusCode UA_Server_addDelayedJob(UA_Server *server, UA_Job job);
 

+ 5 - 0
src/server/ua_server_worker.c

@@ -628,6 +628,11 @@ UA_StatusCode UA_Server_run_mainloop(UA_Server *server, UA_Boolean *running) {
 }
 
 UA_StatusCode UA_Server_run_shutdown(UA_Server *server, UA_UInt16 nThreads){
+    UA_Job *stopJobs;
+    for(size_t i = 0; i < server->networkLayersSize; i++) {
+        size_t stopJobsSize = server->networkLayers[i]->stop(server->networkLayers[i], &stopJobs);
+        processJobs(server, stopJobs, stopJobsSize);
+    }
 #ifdef UA_MULTITHREADING
     /* Wait for all worker threads to finish */
     for(UA_UInt32 i=0;i<nThreads;i++) {

+ 27 - 32
src/server/ua_services.h

@@ -6,6 +6,7 @@
 #include "ua_types_generated.h"
 #include "ua_server.h"
 #include "ua_session.h"
+#include "ua_nodes.h"
 
 /**
  * @ingroup server
@@ -25,9 +26,8 @@
  * @{
  */
 // Service_FindServers
-void Service_FindServers(UA_Server                    *server,
-                          const UA_FindServersRequest *request,
-                          UA_FindServersResponse      *response);
+void Service_FindServers(UA_Server *server, const UA_FindServersRequest *request,
+                         UA_FindServersResponse *response);
 /**
  * Returns the Endpoints supported by a Server and all of the configuration
  * information required to establish a SecureChannel and a Session.
@@ -108,18 +108,26 @@ void Service_CloseSession(UA_Server *server, UA_Session *session, const UA_Close
 /** Used to add one or more Nodes into the AddressSpace hierarchy. */
 void Service_AddNodes(UA_Server *server, UA_Session *session, const UA_AddNodesRequest *request,
                       UA_AddNodesResponse *response);
+void Service_AddNodes_single(UA_Server *server, UA_Session *session, UA_AddNodesItem *item,
+                             UA_NodeAttributes *attr, UA_AddNodesResult *result);
 
 /** Used to add one or more References to one or more Nodes. */
 void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddReferencesRequest *request,
                            UA_AddReferencesResponse *response);
+UA_StatusCode Service_AddReferences_single(UA_Server *server, UA_Session *session,
+                                           const UA_AddReferencesItem *item);
 
 /** Used to delete one or more Nodes from the AddressSpace. */
 void Service_DeleteNodes(UA_Server *server, UA_Session *session, const UA_DeleteNodesRequest *request,
                          UA_DeleteNodesResponse *response);
+UA_StatusCode Service_DeleteNodes_single(UA_Server *server, UA_Session *session, UA_NodeId *nodeId,
+                                         UA_Boolean deleteReferences);
 
 /** Used to delete one or more References of a Node. */
 void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_DeleteReferencesRequest *request,
                               UA_DeleteReferencesResponse *response);
+UA_StatusCode Service_DeleteReferences_single(UA_Server *server, UA_Session *session,
+                                              const UA_DeleteReferencesItem *item);
 
 /** @} */
 
@@ -140,6 +148,9 @@ void Service_DeleteReferences(UA_Server *server, UA_Session *session, const UA_D
 void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseRequest *request,
                     UA_BrowseResponse *response);
 
+void Service_Browse_single(UA_Server *server, UA_Session *session, struct ContinuationPointEntry *cp,
+                           const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result);
+
 /**
  * Used to request the next set of Browse or BrowseNext response information
  * that is too large to be sent in a single response. "Too large" in this
@@ -202,12 +213,8 @@ UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range);
  */
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
                   UA_ReadResponse *response);
-
-/* Mock-Up of the function signature for Unit Tests */
-#ifdef BUILD_UNIT_TESTS
-void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
-                      const UA_ReadValueId *id, UA_DataValue *v);
-#endif
+void Service_Read_single(UA_Server *server, UA_Session *session, UA_TimestampsToReturn timestamps,
+                         const UA_ReadValueId *id, UA_DataValue *v);
 
 // Service_HistoryRead
 /**
@@ -219,10 +226,8 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response);
 
-/* Mock-Up of the function signature for Unit Tests */
-#ifdef BUILD_UNIT_TESTS
-UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
-#endif
+/** Single attribute writes are exposed to the userspace. The wvalue may be destroyed (deleteMembers) */
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue);
 
 // Service_HistoryUpdate
 /** @} */
@@ -235,7 +240,10 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue);
  *
  * @{
  */
-// Service_Call
+#ifdef ENABLE_METHODCALLS
+void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *request,
+                  UA_CallResponse *response);
+#endif
 /** @} */
 
 #ifdef ENABLE_SUBSCRIPTIONS
@@ -291,29 +299,16 @@ void Service_DeleteSubscriptions(UA_Server *server, UA_Session *session,
 void Service_Publish(UA_Server *server, UA_Session *session,
                      const UA_PublishRequest *request, UA_PublishResponse *response);
                          
-//~ Service_ModifySubscription
-//~ Service_SetPublishingMode
-//~ UA_Int32 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
-                                    //~ const UA_SetPublishingModeRequest *request,
-                                    //~ UA_SetPublishingModeResponse *response);
+// Service_ModifySubscription
+// Service_SetPublishingMode
+// UA_Int32 Service_SetPublishingMode(UA_Server *server, UA_Session *session,
+                                  // const UA_SetPublishingModeRequest *request,
+                                  // UA_SetPublishingModeResponse *response);
 // Service_Republish
 // 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_ */
 /** @} */

+ 375 - 372
src/server/ua_services_attribute.c

@@ -5,16 +5,20 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
+/******************/
+/* Read Attribute */
+/******************/
+
 #ifndef BUILD_UNIT_TESTS
 static
 #endif
 UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
     if(str.length < 0 || str.length >= 1023)
         return UA_STATUSCODE_BADINTERNALERROR;
-#ifdef NO_ALLOCA
-    char cstring[str.length+1];
+#ifdef _MSC_VER
+    char *cstring = (char*)UA_alloca(sizeof(char)*str.length+1);
 #else
-    char *cstring = UA_alloca(str.length+1);
+    char cstring[str.length+1];
 #endif
     UA_memcpy(cstring, str.data, str.length);
     cstring[str.length] = 0;
@@ -74,14 +78,12 @@ UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
 
 #define CHECK_NODECLASS(CLASS)                                  \
     if(!(node->nodeClass & (CLASS))) {                          \
-        v->hasStatus = UA_TRUE;                                 \
-        v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;        \
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
         break;                                                  \
     }
 
 static void handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
-	if (v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER
-			|| timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
+	if(v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER || timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
 		v->hasServerTimestamp = UA_TRUE;
 		v->serverTimestamp = UA_DateTime_now();
 	}
@@ -94,20 +96,88 @@ static void handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValu
 	}
 }
 
+static UA_StatusCode getVariableNodeValue(const UA_VariableNode *vn, const UA_TimestampsToReturn timestamps,
+                                          const UA_ReadValueId *id, UA_DataValue *v) {
+    UA_NumericRange range;
+    UA_NumericRange *rangeptr = UA_NULL;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(id->indexRange.length > 0) {
+        retval = parse_numericrange(id->indexRange, &range);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        rangeptr = &range;
+    }
+
+    if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
+        if(vn->value.variant.callback.onRead)
+            vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId,
+                                              &v->value, rangeptr);
+        if(rangeptr)
+            retval = UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
+        else
+            retval = UA_Variant_copy(&vn->value.variant.value, &v->value);
+        if(retval == UA_STATUSCODE_GOOD)
+            handleSourceTimestamps(timestamps, v);
+    } else {
+        UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
+                                      timestamps == UA_TIMESTAMPSTORETURN_BOTH);
+        retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId,
+                                           sourceTimeStamp, rangeptr, v);
+    }
+
+    if(rangeptr)
+        UA_free(range.dimensions);
+    return retval;
+}
+
+static UA_StatusCode getVariableNodeDataType(const UA_VariableNode *vn, UA_DataValue *v) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
+        retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.value.type->typeId,
+                                          &UA_TYPES[UA_TYPES_NODEID]);
+    } else {
+        /* Read from the datasource to see the data type */
+        UA_DataValue val;
+        UA_DataValue_init(&val);
+        retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, UA_NULL, &val);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+        UA_DataValue_deleteMembers(&val);
+    }
+    return retval;
+}
+
+static UA_StatusCode getVariableNodeArrayDimensions(const UA_VariableNode *vn, UA_DataValue *v) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
+        retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.value.arrayDimensions,
+                                         vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+    } else {
+        /* Read the datasource to see the array dimensions */
+        UA_DataValue val;
+        UA_DataValue_init(&val);
+        retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, UA_NULL, &val);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
+                                         val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
+        UA_DataValue_deleteMembers(&val);
+    }
+    return retval;
+}
+
+static const UA_String binEncoding = {sizeof("DefaultBinary")-1, (UA_Byte*)"DefaultBinary"};
+/* clang complains about unused variables */
+// static const UA_String xmlEncoding = {sizeof("DefaultXml")-1, (UA_Byte*)"DefaultXml"};
+
 /** Reads a single attribute from a node in the nodestore. */
-#ifndef BUILD_UNIT_TESTS
-static
-#endif
-void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_ReadValueId *id, UA_DataValue *v) {
-    UA_String binEncoding = UA_STRING("DefaultBinary");
-    UA_String xmlEncoding = UA_STRING("DefaultXml");
-	if(id->dataEncoding.name.length >= 0){
-		if(!UA_String_equal(&binEncoding, &id->dataEncoding.name) &&
-           !UA_String_equal(&xmlEncoding, &id->dataEncoding.name)) {
-			v->hasStatus = UA_TRUE;
-			v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
-			return;
-		}
+void Service_Read_single(UA_Server *server, UA_Session *session, const UA_TimestampsToReturn timestamps,
+                         const UA_ReadValueId *id, UA_DataValue *v) {
+	if(id->dataEncoding.name.length >= 0 && !UA_String_equal(&binEncoding, &id->dataEncoding.name)) {
+           v->hasStatus = UA_TRUE;
+           v->status = UA_STATUSCODE_BADDATAENCODINGINVALID;
+           return;
 	}
 
 	//index range for a non-value
@@ -117,224 +187,131 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_Rea
 		return;
 	}
 
-    UA_Node const *node = UA_NodeStore_get(server->nodestore, &(id->nodeId));
+    UA_Node const *node = UA_NodeStore_get(server->nodestore, &id->nodeId);
     if(!node) {
         v->hasStatus = UA_TRUE;
         v->status = UA_STATUSCODE_BADNODEIDUNKNOWN;
         return;
     }
 
+    /* When setting the value fails in the switch, we get an error code and set hasValue to false */
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    v->hasValue = UA_TRUE;
+
     switch(id->attributeId) {
     case UA_ATTRIBUTEID_NODEID:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->nodeId, &UA_TYPES[UA_TYPES_NODEID]);
         break;
     case UA_ATTRIBUTEID_NODECLASS:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->nodeClass, &UA_TYPES[UA_TYPES_INT32]);
         break;
     case UA_ATTRIBUTEID_BROWSENAME:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->browseName, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
         break;
     case UA_ATTRIBUTEID_DISPLAYNAME:
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
-        if(retval == UA_STATUSCODE_GOOD)
-            v->hasValue = UA_TRUE;
+        retval = UA_Variant_setScalarCopy(&v->value, &node->displayName, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_DESCRIPTION:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->description, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_WRITEMASK:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->writeMask, &UA_TYPES[UA_TYPES_UINT32]);
         break;
     case UA_ATTRIBUTEID_USERWRITEMASK:
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &node->userWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
         break;
     case UA_ATTRIBUTEID_ISABSTRACT:
-        CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_VARIABLETYPE |
-                        UA_NODECLASS_DATATYPE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->isAbstract,
+        CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE | UA_NODECLASS_OBJECTTYPE |
+                        UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract,
                                           &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->symmetric,
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->symmetric,
                                           &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_INVERSENAME:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode *)node)->inverseName,
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ReferenceTypeNode*)node)->inverseName,
                                           &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
         break;
     case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
         CHECK_NODECLASS(UA_NODECLASS_VIEW);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->containsNoLoops,
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->containsNoLoops,
                                           &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
     case UA_ATTRIBUTEID_EVENTNOTIFIER:
         CHECK_NODECLASS(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
-        v->hasValue = UA_TRUE;
-        if(node->nodeClass == UA_NODECLASS_VIEW){
-        	retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode *)node)->eventNotifier,
-                                          	  &UA_TYPES[UA_TYPES_BYTE]);
-        } else {
-        	retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_ObjectNode *)node)->eventNotifier,
-                                              &UA_TYPES[UA_TYPES_BYTE]);
-        }
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_ViewNode*)node)->eventNotifier,
+                                          &UA_TYPES[UA_TYPES_BYTE]);
         break;
-
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        {
-        	if(node->nodeClass != UA_NODECLASS_VARIABLE) {
-    			v->hasValue = UA_FALSE;
-    			handleSourceTimestamps(timestamps, v);
-            }
-
-            UA_NumericRange range;
-            UA_NumericRange *rangeptr = UA_NULL;
-            if(id->indexRange.length > 0) {
-                retval = parse_numericrange(id->indexRange, &range);
-                if(retval != UA_STATUSCODE_GOOD)
-                    break;
-                rangeptr = &range;
-            }
-
-            const UA_VariableNode *vn = (const UA_VariableNode*)node;
-
-            if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-                if(rangeptr)
-                    retval |= UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
-                else
-                    retval |= UA_Variant_copy(&vn->value.variant.value, &v->value);
-                if(retval == UA_STATUSCODE_GOOD) {
-                    v->hasValue = UA_TRUE;
-                    handleSourceTimestamps(timestamps, v);
-                }
-                if(vn->value.variant.callback.onRead)
-                    vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId, &v->value, rangeptr);
-            } else {
-                UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, sourceTimeStamp, rangeptr, v);
-            }
-
-            if(rangeptr)
-                UA_free(range.dimensions);
-        }
+        retval = getVariableNodeValue((const UA_VariableNode*)node, timestamps, id, v);
         break;
-
-    case UA_ATTRIBUTEID_DATATYPE: {
+    case UA_ATTRIBUTEID_DATATYPE:
 		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        const UA_VariableNode *vn = (const UA_VariableNode*)node;
-        if(vn->valueSource == UA_VALUESOURCE_VARIANT){
-            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
-            if(vn->value.variant.callback.onRead)
-                vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId, &vn->value.variant.value, UA_NULL);
-        } else {
-            UA_DataValue val;
-            UA_DataValue_init(&val);
-            retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, UA_NULL, &val);
-            if(retval != UA_STATUSCODE_GOOD)
-                break;
-            retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
-            UA_DataValue_deleteMembers(&val);
-        }
-
-        if(retval == UA_STATUSCODE_GOOD)
-            v->hasValue = UA_TRUE;
-        }
+        retval = getVariableNodeDataType((const UA_VariableNode*)node, v);
         break;
-
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode *)node)->valueRank, &UA_TYPES[UA_TYPES_INT32]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableTypeNode*)node)->valueRank,
+                                           &UA_TYPES[UA_TYPES_INT32]);
         break;
-
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        {
-            const UA_VariableNode *vn = (const UA_VariableNode *)node;
-            if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-                retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.value.arrayDimensions, vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
-                if(retval == UA_STATUSCODE_GOOD)
-                    v->hasValue = UA_TRUE;
-            } else {
-                UA_DataValue val;
-                UA_DataValue_init(&val);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, node->nodeId, UA_FALSE, UA_NULL, &val);
-                if(retval != UA_STATUSCODE_GOOD)
-                    break;
-                retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions, val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
-                UA_DataValue_deleteMembers(&val);
-            }
-        }
+        retval = getVariableNodeArrayDimensions((const UA_VariableNode*)node, v);
         break;
-
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->accessLevel, &UA_TYPES[UA_TYPES_BYTE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->accessLevel,
+                                          &UA_TYPES[UA_TYPES_BYTE]);
         break;
-
     case UA_ATTRIBUTEID_USERACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->userAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->userAccessLevel,
+                                          &UA_TYPES[UA_TYPES_BYTE]);
         break;
-
     case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->minimumSamplingInterval, &UA_TYPES[UA_TYPES_DOUBLE]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->minimumSamplingInterval,
+                                          &UA_TYPES[UA_TYPES_DOUBLE]);
         break;
-
     case UA_ATTRIBUTEID_HISTORIZING:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode *)node)->historizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_VariableNode*)node)->historizing,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
-
     case UA_ATTRIBUTEID_EXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->executable, &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->executable,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
-
     case UA_ATTRIBUTEID_USEREXECUTABLE:
         CHECK_NODECLASS(UA_NODECLASS_METHOD);
-        v->hasValue = UA_TRUE;
-        retval |= UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode *)node)->userExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = UA_Variant_setScalarCopy(&v->value, &((const UA_MethodNode*)node)->userExecutable,
+                                          &UA_TYPES[UA_TYPES_BOOLEAN]);
         break;
-
     default:
-        v->hasStatus = UA_TRUE;
-        v->status = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
         break;
     }
 
     UA_NodeStore_release(node);
 
     if(retval != UA_STATUSCODE_GOOD) {
+        v->hasValue = UA_FALSE;
         v->hasStatus = UA_TRUE;
         v->status = retval;
     }
 
+    // Todo: what if the timestamp from the datasource are already present?
     handleServerTimestamps(timestamps, v);
 }
 
 void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *request,
                   UA_ReadResponse *response) {
-
     if(request->nodesToReadSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
@@ -361,13 +338,8 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
     }
 
 #ifdef UA_EXTERNAL_NAMESPACES
-#ifdef NO_ALLOCA
     UA_Boolean isExternal[size];
     UA_UInt32 indices[size];
-#else
-    UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * size);
-    UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * size);
-#endif /*NO_ALLOCA */
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean) * size);
     for(size_t j = 0;j<server->externalNamespacesSize;j++) {
         size_t indexSize = 0;
@@ -390,7 +362,8 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif
-            readValue(server, request->timestampsToReturn, &request->nodesToRead[i], &response->results[i]);
+            Service_Read_single(server, session, request->timestampsToReturn,
+                                &request->nodesToRead[i], &response->results[i]);
     }
 
 #ifdef EXTENSION_STATELESS
@@ -427,234 +400,269 @@ 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
-UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
-    /* is there a value at all */
-    if(!wvalue->value.hasValue)
-        return UA_STATUSCODE_BADTYPEMISMATCH;
+/*******************/
+/* Write Attribute */
+/*******************/
 
-    // 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);
+UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
+                                 UA_EditNodeCallback callback, const void *data) {
+    UA_StatusCode retval;
+    do {
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
         if(!node)
             return UA_STATUSCODE_BADNODEIDUNKNOWN;
+#ifndef UA_MULTITHREADING
+        retval = callback(server, session, (UA_Node*)(uintptr_t)node, data);
+        UA_NodeStore_release(node);
+        return retval;
+#else
+        UA_Node *copy = UA_Node_copyAnyNodeClass(node);
+        UA_NodeStore_release(node);
+        if(!copy)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+        retval = callback(server, session, copy, data);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_Node_deleteAnyNodeClass(copy);
+            return retval;
+        }
+        retval = UA_NodeStore_replace(server->nodestore, node, copy, UA_NULL);
+        if(retval != UA_STATUSCODE_GOOD)
+            UA_Node_deleteAnyNodeClass(copy);
+#endif
+    } while(retval != UA_STATUSCODE_GOOD);
+    return UA_STATUSCODE_GOOD;
+}
 
-        switch(wvalue->attributeId) {
-        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:
-          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) {
-                retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                break;
-            }
+#define CHECK_DATATYPE(EXP_DT)                                          \
+    if(!wvalue->value.hasValue ||                                       \
+       &UA_TYPES[UA_TYPES_##EXP_DT] != wvalue->value.value.type ||      \
+       !UA_Variant_isScalar(&wvalue->value.value)) {                    \
+        retval = UA_STATUSCODE_BADTYPEMISMATCH;                         \
+        break;                                                          \
+    }
 
-            /* parse the range */
-            UA_Boolean hasRange = UA_FALSE;
-            UA_NumericRange range;
-            if(wvalue->indexRange.length > 0) {
-                retval = parse_numericrange(wvalue->indexRange, &range);
-                if(retval != UA_STATUSCODE_GOOD)
-                    break;
-                hasRange = UA_TRUE;
-            }
+#define CHECK_NODECLASS_WRITE(CLASS)                                    \
+    if((node->nodeClass & (CLASS)) == 0) {                              \
+        retval = UA_STATUSCODE_BADNODECLASSINVALID;                     \
+        break;                                                          \
+    }
 
-            /* the relevant members are similar for variables and variabletypes */
-            const UA_VariableNode *vn = (const UA_VariableNode*)node;
-            if(vn->valueSource == UA_VALUESOURCE_DATASOURCE) {
-                if(!vn->value.dataSource.write) {
-                    retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-                    goto clean_up_range;
-                }
-                // todo: writing ranges
-                if(hasRange)
-                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, wvalue->nodeId, &wvalue->value.value, &range);
-                else
-                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, wvalue->nodeId, &wvalue->value.value, UA_NULL);
-                done = UA_TRUE;
-                goto clean_up_range;
-            }
-            const UA_Variant *oldV = &vn->value.variant.value;
-
-            /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
-            if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
-                if(oldV->type->namespaceZero && wvalue->value.value.type->namespaceZero &&
-                   oldV->type->typeIndex == wvalue->value.value.type->typeIndex)
-                    /* An enum was sent as an int32, or an opaque type as a bytestring. This is
-                       detected with the typeIndex indicated the "true" datatype. */
-
-                    wvalue->value.value.type = oldV->type;
-                else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
-                        wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-                        UA_Variant_isScalar(&wvalue->value.value)) {
-                    /* a string is written to a byte array */
-                    UA_ByteString *str = (UA_ByteString*) wvalue->value.value.data;
-                    wvalue->value.value.arrayLength = str->length;
-                    wvalue->value.value.data = str->data;
-                    wvalue->value.value.type = &UA_TYPES[UA_TYPES_BYTE];
-                    UA_free(str);
-                } else {
-                    retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                    goto clean_up_range;
-                }
-            }
+static UA_StatusCode
+Service_Write_single_ValueDataSource(UA_Server *server, UA_Session *session, const UA_VariableNode *node,
+                                     UA_WriteValue *wvalue) {
+    UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
+    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
+    UA_assert(node->valueSource == UA_VALUESOURCE_DATASOURCE);
+
+    UA_StatusCode retval;
+    if(wvalue->indexRange.length <= 0) {
+        retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
+                                              &wvalue->value.value, UA_NULL);
+    } else {
+        UA_NumericRange range;
+        retval = parse_numericrange(wvalue->indexRange, &range);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        retval = node->value.dataSource.write(node->value.dataSource.handle, node->nodeId,
+                                              &wvalue->value.value, &range);
+        UA_free(range.dimensions);
+    }
+    return retval;
+}
 
-            /* copy the node */
-            UA_VariableNode *newVn = (node->nodeClass == UA_NODECLASS_VARIABLE) ?
-                UA_VariableNode_new() : (UA_VariableNode*)UA_VariableTypeNode_new();
-            if(!newVn) {
-                retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                goto clean_up_range;
-            }
-            retval = (node->nodeClass == UA_NODECLASS_VARIABLE) ? UA_VariableNode_copy(vn, newVn) : 
-                UA_VariableTypeNode_copy((const UA_VariableTypeNode*)vn, (UA_VariableTypeNode*)newVn);
-            if(retval != UA_STATUSCODE_GOOD)
-                goto clean_up;
-                
-            /* insert the new value */
-            if(hasRange)
-                retval = UA_Variant_setRangeCopy(&newVn->value.variant.value, wvalue->value.value.data,
-                                                 wvalue->value.value.arrayLength, range);
-            else {
-                UA_Variant_deleteMembers(&newVn->value.variant.value);
-                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant.value);
-            }
+/* In the multithreaded case, node is a copy */
+static UA_StatusCode
+MoveValueIntoNode(UA_Server *server, UA_Session *session, UA_VariableNode *node, UA_WriteValue *wvalue) {
+    UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);
+    UA_assert(node->nodeClass == UA_NODECLASS_VARIABLE || node->nodeClass == UA_NODECLASS_VARIABLETYPE);
+    UA_assert(node->valueSource == UA_VALUESOURCE_VARIANT);
 
-            if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node,
-                                                   (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
-                //trigger onWrite
-                if(vn->value.variant.callback.onWrite){
-                    if(hasRange)
-                        vn->value.variant.callback.onWrite(vn->value.variant.callback.handle,
-                                                           wvalue->nodeId, &wvalue->value.value, &range);
-                    else
-                        vn->value.variant.callback.onWrite(vn->value.variant.callback.handle,
-                                                           wvalue->nodeId, &wvalue->value.value, UA_NULL);
-                }
-
-                done = UA_TRUE;
-                goto clean_up_range;
-            }
+    /* Parse the range */
+    UA_NumericRange range;
+    UA_NumericRange *rangeptr = UA_NULL;
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    if(wvalue->indexRange.length > 0) {
+        retval = parse_numericrange(wvalue->indexRange, &range);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
+        rangeptr = &range;
+    }
 
-            clean_up:
-            if(node->nodeClass == UA_NODECLASS_VARIABLE)
-                UA_VariableNode_delete(newVn);
-            else
-                UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn);
-            clean_up_range:
-            if(hasRange)
+    /* The nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings.
+       nodeV contains the correct type definition. */
+    UA_Variant *newV = &wvalue->value.value;
+    UA_Variant *oldV = &node->value.variant.value;
+    UA_Variant cast_v;
+    if(!UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
+        cast_v = wvalue->value.value;
+        newV = &cast_v;
+        if(oldV->type->namespaceZero && newV->type->namespaceZero &&
+           oldV->type->typeIndex == newV->type->typeIndex) {
+            /* An enum was sent as an int32, or an opaque type as a bytestring. This is
+               detected with the typeIndex indicated the "true" datatype. */
+            newV->type = oldV->type;
+        } else if(oldV->type == &UA_TYPES[UA_TYPES_BYTE] && !UA_Variant_isScalar(oldV) &&
+                  newV->type == &UA_TYPES[UA_TYPES_BYTESTRING] && UA_Variant_isScalar(newV)) {
+            /* a string is written to a byte array */
+            UA_ByteString *str = (UA_ByteString*) newV->data;
+            newV->arrayLength = str->length;
+            newV->data = str->data;
+            newV->type = &UA_TYPES[UA_TYPES_BYTE];
+        } else {
+            if(rangeptr)
                 UA_free(range.dimensions);
-            }
-            break;
-        case UA_ATTRIBUTEID_NODEID:
-        case UA_ATTRIBUTEID_NODECLASS:
-        case UA_ATTRIBUTEID_DATATYPE:
-        default:
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
+            return UA_STATUSCODE_BADTYPEMISMATCH;
         }
-
-        UA_NodeStore_release(node);
-        if(retval != UA_STATUSCODE_GOOD)
-            break;
     }
+    
+    if(!rangeptr) {
+        // TODO: Avoid copying the whole node and then delete the old value for multithreading
+        UA_Variant_deleteMembers(&node->value.variant.value);
+        node->value.variant.value = *newV;
+        UA_Variant_init(&wvalue->value.value);
+    } else {
+        retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data, newV->arrayLength, range);
+    }
+    if(node->value.variant.callback.onWrite)
+        node->value.variant.callback.onWrite(node->value.variant.callback.handle, node->nodeId,
+                                             &node->value.variant.value, rangeptr);
 
+    if(rangeptr)
+        UA_free(range.dimensions);
     return retval;
 }
 
+static UA_StatusCode
+MoveAttributeIntoNode(UA_Server *server, UA_Session *session, UA_Node *node, UA_WriteValue *wvalue) {
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    void *value = wvalue->value.value.data;
+	switch(wvalue->attributeId) {
+    case UA_ATTRIBUTEID_NODEID:
+    case UA_ATTRIBUTEID_NODECLASS:
+    case UA_ATTRIBUTEID_DATATYPE:
+		retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+		break;
+	case UA_ATTRIBUTEID_BROWSENAME:
+		CHECK_DATATYPE(QUALIFIEDNAME);
+		UA_QualifiedName_deleteMembers(&node->browseName);
+        node->browseName = *(UA_QualifiedName*)value;
+        UA_QualifiedName_init((UA_QualifiedName*)value);
+		break;
+	case UA_ATTRIBUTEID_DISPLAYNAME:
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+		UA_LocalizedText_deleteMembers(&node->displayName);
+        node->displayName = *(UA_LocalizedText*)value;
+		UA_LocalizedText_init((UA_LocalizedText*)value);
+		break;
+	case UA_ATTRIBUTEID_DESCRIPTION:
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+		UA_LocalizedText_deleteMembers(&node->description);
+        node->description = *(UA_LocalizedText*)value;
+		UA_LocalizedText_init((UA_LocalizedText*)value);
+		break;
+	case UA_ATTRIBUTEID_WRITEMASK:
+		CHECK_DATATYPE(UINT32);
+		node->writeMask = *(UA_UInt32*)value;
+		break;
+	case UA_ATTRIBUTEID_USERWRITEMASK:
+		CHECK_DATATYPE(UINT32);
+		node->userWriteMask = *(UA_UInt32*)value;
+		break;    
+	case UA_ATTRIBUTEID_ISABSTRACT:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_OBJECTTYPE | UA_NODECLASS_REFERENCETYPE |
+                              UA_NODECLASS_VARIABLETYPE | UA_NODECLASS_DATATYPE);
+		CHECK_DATATYPE(BOOLEAN);
+		((UA_ObjectTypeNode*)node)->isAbstract = *(UA_Boolean*)value;
+		break;
+	case UA_ATTRIBUTEID_SYMMETRIC:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
+		CHECK_DATATYPE(BOOLEAN);
+		((UA_ReferenceTypeNode*)node)->symmetric = *(UA_Boolean*)value;
+		break;
+	case UA_ATTRIBUTEID_INVERSENAME:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
+		CHECK_DATATYPE(LOCALIZEDTEXT);
+        UA_ReferenceTypeNode *n = (UA_ReferenceTypeNode*)node;
+		UA_LocalizedText_deleteMembers(&n->inverseName);
+        n->inverseName = *(UA_LocalizedText*)value;
+		UA_LocalizedText_init((UA_LocalizedText*)value);
+		break;
+	case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW);
+		CHECK_DATATYPE(BOOLEAN);
+        ((UA_ViewNode*)node)->containsNoLoops = *(UA_Boolean*)value;
+		break;
+	case UA_ATTRIBUTEID_EVENTNOTIFIER:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VIEW | UA_NODECLASS_OBJECT);
+		CHECK_DATATYPE(BYTE);
+        ((UA_ViewNode*)node)->eventNotifier = *(UA_Byte*)value;
+		break;
+	case UA_ATTRIBUTEID_VALUE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
+        retval = MoveValueIntoNode(server, session, (UA_VariableNode*)node, wvalue);
+		break;
+	case UA_ATTRIBUTEID_ACCESSLEVEL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BYTE);
+		((UA_VariableNode*)node)->accessLevel = *(UA_Byte*)value;
+		break;
+	case UA_ATTRIBUTEID_USERACCESSLEVEL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BYTE);
+		((UA_VariableNode*)node)->userAccessLevel = *(UA_Byte*)value;
+		break;
+	case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(DOUBLE);
+		((UA_VariableNode*)node)->minimumSamplingInterval = *(UA_Double*)value;
+		break;
+	case UA_ATTRIBUTEID_HISTORIZING:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE);
+		CHECK_DATATYPE(BOOLEAN);
+		((UA_VariableNode*)node)->historizing = *(UA_Boolean*)value;
+		break;
+	case UA_ATTRIBUTEID_EXECUTABLE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
+		CHECK_DATATYPE(BOOLEAN);
+		((UA_MethodNode*)node)->executable = *(UA_Boolean*)value;
+		break;
+	case UA_ATTRIBUTEID_USEREXECUTABLE:
+		CHECK_NODECLASS_WRITE(UA_NODECLASS_METHOD);
+		CHECK_DATATYPE(BOOLEAN);
+		((UA_MethodNode*)node)->userExecutable = *(UA_Boolean*)value;
+		break;
+	default:
+		retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
+		break;
+	}
+    return retval;
+}
+
+UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session, UA_WriteValue *wvalue) {
+    if(!wvalue->value.hasValue || !wvalue->value.value.data)
+        return UA_STATUSCODE_BADNODATA; // TODO: is this the right return code?
+    if(wvalue->attributeId == UA_ATTRIBUTEID_VALUE) {
+        const UA_Node *orig = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
+        if(!orig)
+            return UA_STATUSCODE_BADNODEIDUNKNOWN;
+        if(orig->nodeClass & (UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLE) &&
+           ((const UA_VariableNode*)orig)->valueSource == UA_VALUESOURCE_DATASOURCE) {
+            UA_StatusCode retval = Service_Write_single_ValueDataSource(server, session, (const UA_VariableNode*)orig, wvalue);
+            UA_NodeStore_release(orig);
+            return retval;
+        }
+        UA_NodeStore_release(orig);
+    }
+    return UA_Server_editNode(server, session, &wvalue->nodeId,
+                              (UA_EditNodeCallback)MoveAttributeIntoNode, wvalue);
+}
+
 void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
                    UA_WriteResponse *response) {
     UA_assert(server != UA_NULL && session != UA_NULL && request != UA_NULL && response != UA_NULL);
 
-    if(request->nodesToWriteSize <= 0){
+    if(request->nodesToWriteSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;
         return;
     }
@@ -666,13 +674,8 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
     }
 
 #ifdef UA_EXTERNAL_NAMESPACES
-#ifdef NO_ALLOCA
     UA_Boolean isExternal[request->nodesToWriteSize];
     UA_UInt32 indices[request->nodesToWriteSize];
-#else
-    UA_Boolean *isExternal = UA_alloca(sizeof(UA_Boolean) * request->nodesToWriteSize);
-    UA_UInt32 *indices = UA_alloca(sizeof(UA_UInt32) * request->nodesToWriteSize);
-#endif /*NO_ALLOCA */
     UA_memset(isExternal, UA_FALSE, sizeof(UA_Boolean)*request->nodesToWriteSize);
     for(size_t j = 0; j < server->externalNamespacesSize; j++) {
         UA_UInt32 indexSize = 0;
@@ -697,6 +700,6 @@ void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest
 #ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif
-            response->results[i] = writeValue(server, &request->nodesToWrite[i]);
+		  response->results[i] = Service_Write_single(server, session, &request->nodesToWrite[i]);
     }
 }

+ 14 - 11
src/server/ua_services_call.c

@@ -5,8 +5,9 @@
 #include "ua_nodestore.h"
 #include "ua_nodes.h"
 
-static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
-                                                       UA_String withBrowseName) {
+static const UA_VariableNode
+*getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
+                          UA_String withBrowseName) {
     const UA_Node *refTarget;
     UA_NodeId hasProperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
     
@@ -27,7 +28,8 @@ static const UA_VariableNode *getArgumentsVariableNode(UA_Server *server, const
     return UA_NULL;
 }
 
-static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
+static UA_StatusCode
+statisfySignature(UA_Variant *var, UA_Argument arg) {
     if(!UA_NodeId_equal(&var->type->typeId, &arg.dataType) )
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     
@@ -72,7 +74,8 @@ static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
     return UA_STATUSCODE_GOOD;
 }
 
-static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
+static UA_StatusCode
+argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
     if(argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
         argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
         return UA_STATUSCODE_BADINTERNALERROR;
@@ -104,17 +107,16 @@ static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_
     return retval;
 }
 
-static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request,
-                       UA_CallMethodResult *result) {
-    const UA_MethodNode *methodCalled = (const UA_MethodNode*) UA_NodeStore_get(server->nodestore,
-                                                                                &request->methodId);
+static void
+callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequest *request,
+           UA_CallMethodResult *result) {
+    const UA_MethodNode *methodCalled = (const UA_MethodNode*)UA_NodeStore_get(server->nodestore, &request->methodId);
     if(!methodCalled) {
         result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
         return;
     }
     
-    const UA_ObjectNode *withObject = (const UA_ObjectNode *) UA_NodeStore_get(server->nodestore,
-                                                                               &request->objectId);
+    const UA_ObjectNode *withObject = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, &request->objectId);
     if(!withObject) {
         result->statusCode = UA_STATUSCODE_BADNODEIDINVALID;
         goto releaseMethodReturn;
@@ -203,7 +205,8 @@ void Service_Call(UA_Server *server, UA_Session *session, const UA_CallRequest *
         return;
     }
 
-    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT], request->methodsToCallSize);
+    response->results = UA_Array_new(&UA_TYPES[UA_TYPES_CALLMETHODRESULT],
+                                     request->methodsToCallSize);
     if(!response->results) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADOUTOFMEMORY;
         return;

Diferenças do arquivo suprimidas por serem muito extensas
+ 771 - 224
src/server/ua_services_nodemanagement.c


+ 48 - 54
src/server/ua_services_view.c

@@ -4,12 +4,11 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 
-static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
-                                  UA_UInt32 mask, UA_ReferenceDescription *descr)
-{
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+static UA_StatusCode
+fillReferenceDescription(UA_NodeStore *ns, const UA_Node *curr, UA_ReferenceNode *ref,
+                         UA_UInt32 mask, UA_ReferenceDescription *descr) {
     UA_ReferenceDescription_init(descr);
-    retval |= UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
+    UA_StatusCode retval = UA_NodeId_copy(&curr->nodeId, &descr->nodeId.nodeId);
     //TODO: ExpandedNodeId is mocked up
     descr->nodeId.serverIndex = 0;
     descr->nodeId.namespaceUri.length = -1;
@@ -43,10 +42,9 @@ static UA_StatusCode fillrefdescr(UA_NodeStore *ns, const UA_Node *curr, UA_Refe
 
 /* Tests if the node is relevant to the browse request and shall be returned. If
    so, it is retrieved from the Nodestore. If not, null is returned. */
-static const UA_Node *relevant_node(UA_Server *server, UA_NodeStore *ns, const UA_BrowseDescription *descr,
-                                    UA_Boolean return_all, UA_ReferenceNode *reference,
-                                    UA_NodeId *relevant, size_t relevant_count, UA_Boolean *isExternal)
-{
+static const UA_Node *
+isRelevantNode(UA_Server *server, const UA_BrowseDescription *descr, UA_Boolean return_all,
+               UA_ReferenceNode *reference, UA_NodeId *relevant, size_t relevant_count, UA_Boolean *isExternal) {
     if(reference->isInverse == UA_TRUE && descr->browseDirection == UA_BROWSEDIRECTION_FORWARD)
         return UA_NULL;
     else if(reference->isInverse == UA_FALSE && descr->browseDirection == UA_BROWSEDIRECTION_INVERSE)
@@ -72,8 +70,8 @@ is_relevant: ;
 			break;
 		}
 	}
-	if(*isExternal == UA_FALSE){
-		node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
+	if(*isExternal == UA_FALSE) {
+		node = UA_NodeStore_get(server->nodestore, &reference->targetId.nodeId);
 	} else {
 		/*	prepare a read request in the external nodestore	*/
 		UA_ExternalNodeStore *ens = &server->externalNamespaces[nsIndex].externalNodeStore;
@@ -102,24 +100,18 @@ is_relevant: ;
 		/*	create and fill a dummy nodeStructure	*/
 		UA_Node *tempNode = (UA_Node*) UA_ObjectNode_new();
 		UA_NodeId_copy(&(reference->targetId.nodeId), &(tempNode->nodeId));
-		if(readNodesResults[0].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[0].status == UA_STATUSCODE_GOOD)
 			UA_NodeClass_copy((UA_NodeClass*)readNodesResults[0].value.data, &(tempNode->nodeClass));
-		}
-		if(readNodesResults[1].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[1].status == UA_STATUSCODE_GOOD)
 			UA_QualifiedName_copy((UA_QualifiedName*)readNodesResults[1].value.data, &(tempNode->browseName));
-		}
-		if(readNodesResults[2].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[2].status == UA_STATUSCODE_GOOD)
 			UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[2].value.data, &(tempNode->displayName));
-		}
-		if(readNodesResults[3].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[3].status == UA_STATUSCODE_GOOD)
 			UA_LocalizedText_copy((UA_LocalizedText*)readNodesResults[3].value.data, &(tempNode->description));
-		}
-		if(readNodesResults[4].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[4].status == UA_STATUSCODE_GOOD)
 			UA_UInt32_copy((UA_UInt32*)readNodesResults[4].value.data, &(tempNode->writeMask));
-		}
-		if(readNodesResults[5].status == UA_STATUSCODE_GOOD){
+		if(readNodesResults[5].status == UA_STATUSCODE_GOOD)
 			UA_UInt32_copy((UA_UInt32*)readNodesResults[5].value.data, &(tempNode->userWriteMask));
-		}
 		UA_Array_delete(readValueIds, &UA_TYPES[UA_TYPES_READVALUEID], 6);
 		UA_Array_delete(indices, &UA_TYPES[UA_TYPES_UINT32], 6);
 		UA_Array_delete(readNodesResults, &UA_TYPES[UA_TYPES_DATAVALUE], 6);
@@ -127,7 +119,7 @@ is_relevant: ;
 		node = tempNode;
 	}
 #else
-    const UA_Node *node = UA_NodeStore_get(ns, &reference->targetId.nodeId);
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &reference->targetId.nodeId);
 #endif
     if(node && descr->nodeClassMask != 0 && (node->nodeClass & descr->nodeClassMask) == 0) {
 #ifdef UA_EXTERNAL_NAMESPACES
@@ -147,8 +139,8 @@ is_relevant: ;
  * the array and increase the size. Since the hierarchy is not cyclic, we can safely progress in the
  * array to process the newly found referencetype nodeids (emulated recursion).
  */
-static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes,
-                                  size_t *reftypes_count) {
+static UA_StatusCode
+findSubTypes(UA_NodeStore *ns, const UA_NodeId *root, UA_NodeId **reftypes, size_t *reftypes_count) {
     const UA_Node *node = UA_NodeStore_get(ns, root);
     if(!node)
         return UA_STATUSCODE_BADNOMATCH;
@@ -209,7 +201,7 @@ static UA_StatusCode findsubtypes(UA_NodeStore *ns, const UA_NodeId *root, UA_No
     return UA_STATUSCODE_GOOD;
 }
 
-static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session){
+static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session) {
     session->availableContinuationPoints++;
     UA_ByteString_deleteMembers(&cp->identifier);
     UA_BrowseDescription_deleteMembers(&cp->browseDescription);
@@ -221,14 +213,15 @@ static void removeCp(struct ContinuationPointEntry *cp, UA_Session* session){
  * Results for a single browsedescription. This is the inner loop for both Browse and BrowseNext
  * @param session Session to save continuationpoints
  * @param ns The nodstore where the to-be-browsed node can be found
- * @param cp If cp points to a continuationpoint, we continue from there.
+ * @param cp If cp is not null, we continue from here
  *           If cp is null, we can add a new continuation point if possible and necessary.
  * @param descr If no cp is set, we take the browsedescription from there
  * @param maxrefs The maximum number of references the client has requested
  * @param result The entry in the request
  */
-static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, struct ContinuationPointEntry *cp,
-                   const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) {
+void
+Service_Browse_single(UA_Server *server, UA_Session *session, struct ContinuationPointEntry *cp,
+                      const UA_BrowseDescription *descr, UA_UInt32 maxrefs, UA_BrowseResult *result) {
     UA_UInt32 continuationIndex = 0;
     size_t referencesCount = 0;
     UA_Int32 referencesIndex = 0;
@@ -254,15 +247,17 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     UA_Boolean all_refs = UA_NodeId_isNull(&descr->referenceTypeId);
     if(!all_refs) {
         if(descr->includeSubtypes) {
-            result->statusCode = findsubtypes(ns, &descr->referenceTypeId, &relevant_refs, &relevant_refs_size);
+            result->statusCode = findSubTypes(server->nodestore, &descr->referenceTypeId,
+                                              &relevant_refs, &relevant_refs_size);
             if(result->statusCode != UA_STATUSCODE_GOOD)
                 return;
         } else {
-            const UA_Node *rootRef = UA_NodeStore_get(ns, &descr->referenceTypeId);
+            const UA_Node *rootRef = UA_NodeStore_get(server->nodestore, &descr->referenceTypeId);
             if(!rootRef) {
                 result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
                 return;
-            } else if(rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
+            }
+            if(rootRef->nodeClass != UA_NODECLASS_REFERENCETYPE) {
                 UA_NodeStore_release(rootRef);
                 result->statusCode = UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
                 return;
@@ -274,7 +269,7 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     }
 
     /* get the node */
-    const UA_Node *node = UA_NodeStore_get(ns, &descr->nodeId);
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, &descr->nodeId);
     if(!node) {
         result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
         if(!all_refs && descr->includeSubtypes)
@@ -306,34 +301,32 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
     UA_Boolean isExternal = UA_FALSE;
     for(; referencesIndex < node->referencesSize && referencesCount < real_maxrefs; referencesIndex++) {
     	isExternal = UA_FALSE;
-    	const UA_Node *current = relevant_node(server, ns, descr, all_refs, &node->references[referencesIndex],
-                                               relevant_refs, relevant_refs_size, &isExternal);
+    	const UA_Node *current = isRelevantNode(server, descr, all_refs, &node->references[referencesIndex],
+                                                relevant_refs, relevant_refs_size, &isExternal);
         if(!current)
             continue;
         if(skipped < continuationIndex) {
 #ifdef UA_EXTERNAL_NAMESPACES
-        	if(isExternal == UA_TRUE){
+        	if(isExternal == UA_TRUE)
         		/*	relevant_node returns a node malloced with UA_ObjectNode_new if it is external (there is no UA_Node_new function)	*/
         		UA_ObjectNode_delete((UA_ObjectNode*)current);
-        	} else {
+        	else
         		UA_NodeStore_release(current);
-        	}
 #else
         	UA_NodeStore_release(current);
 #endif
             skipped++;
             continue;
         }
-        UA_StatusCode retval = fillrefdescr(ns, current, &node->references[referencesIndex], descr->resultMask,
-                                            &result->references[referencesCount]);
+        UA_StatusCode retval = fillReferenceDescription(server->nodestore, current, &node->references[referencesIndex],
+                                                        descr->resultMask, &result->references[referencesCount]);
 #ifdef UA_EXTERNAL_NAMESPACES
-        if(isExternal == UA_TRUE){
+        if(isExternal == UA_TRUE)
         	UA_ObjectNode_delete((UA_ObjectNode*)current);
-        } else {
+        else
         	UA_NodeStore_release(current);
-        }
 #else
-        	UA_NodeStore_release(current);
+        UA_NodeStore_release(current);
 #endif
         if(retval != UA_STATUSCODE_GOOD) {
             UA_Array_delete(result->references, &UA_TYPES[UA_TYPES_REFERENCEDESCRIPTION], referencesCount);
@@ -370,7 +363,8 @@ static void browse(UA_Server *server, UA_Session *session, UA_NodeStore *ns, str
         }
     } else if(maxrefs != 0 && referencesCount >= maxrefs) {
         /* create a cp */
-        if(session->availableContinuationPoints <= 0 || !(cp = UA_malloc(sizeof(struct ContinuationPointEntry)))) {
+        if(session->availableContinuationPoints <= 0 ||
+           !(cp = UA_malloc(sizeof(struct ContinuationPointEntry)))) {
             result->statusCode = UA_STATUSCODE_BADNOCONTINUATIONPOINTS;
             return;
         }
@@ -440,8 +434,8 @@ void Service_Browse(UA_Server *server, UA_Session *session, const UA_BrowseReque
 #ifdef UA_EXTERNAL_NAMESPACES
         if(!isExternal[i])
 #endif
-            browse(server, session, server->nodestore, UA_NULL, &request->nodesToBrowse[i],
-                   request->requestedMaxReferencesPerNode, &response->results[i]);
+            Service_Browse_single(server, session, UA_NULL, &request->nodesToBrowse[i],
+                                  request->requestedMaxReferencesPerNode, &response->results[i]);
     }
 }
 
@@ -464,7 +458,7 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
            struct ContinuationPointEntry *cp;
            LIST_FOREACH(cp, &session->continuationPoints, pointers) {
                if(UA_ByteString_equal(&cp->identifier, &request->continuationPoints[i])) {
-                   browse(server, session, server->nodestore, cp, UA_NULL, 0, &response->results[i]);
+                   Service_Browse_single(server, session, cp, UA_NULL, 0, &response->results[i]);
                    break;
                }
            }
@@ -500,8 +494,7 @@ void Service_BrowseNext(UA_Server *server, UA_Session *session, const UA_BrowseN
 static UA_StatusCode
 walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, const UA_RelativePath *path,
                UA_Int32 pathindex, UA_BrowsePathTarget **targets, UA_Int32 *targets_size,
-               UA_Int32 *target_count)
-{
+               UA_Int32 *target_count) {
     const UA_RelativePathElement *elem = &path->elements[pathindex];
     if(elem->targetName.name.length == -1)
         return UA_STATUSCODE_BADBROWSENAMEINVALID;
@@ -515,7 +508,7 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     else if(!elem->includeSubtypes)
         reftypes = (UA_NodeId*)(uintptr_t)&elem->referenceTypeId; // ptr magic due to const cast
     else {
-        retval = findsubtypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
+        retval = findSubTypes(server->nodestore, &elem->referenceTypeId, &reftypes, &reftypes_count);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
     }
@@ -577,8 +570,9 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_Node *node, cons
     return retval;
 }
 
-static void translateBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
-                                UA_BrowsePathResult *result) {
+static void
+translateBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
+                    UA_BrowsePathResult *result) {
     if(path->relativePath.elementsSize <= 0) {
         result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
         return;

+ 136 - 320
src/ua_types.c

@@ -9,43 +9,38 @@
 #include <strsafe.h>
 #endif
 
-/*****************/
 /* Helper Macros */
-/*****************/
-
-#define UA_TYPE_DEFAULT(TYPE)            \
-    UA_TYPE_NEW_DEFAULT(TYPE)            \
-    UA_TYPE_DELETE_DEFAULT(TYPE)
-
-#define UA_TYPE_NEW_DEFAULT(TYPE)                              \
+#define UA_TYPE_DEFAULT(TYPE)                                  \
     TYPE * TYPE##_new() {                                      \
         TYPE *p = UA_malloc(sizeof(TYPE));                     \
         if(p) TYPE##_init(p);                                  \
         return p;                                              \
+    }                                                          \
+    void TYPE##_delete(TYPE *p) {                              \
+        TYPE##_deleteMembers(p);                               \
+        UA_free(p);                                            \
     }
 
-#define UA_TYPE_DELETEMEMBERS_NOACTION(TYPE) \
-    void TYPE##_deleteMembers(TYPE *p) {    \
-    }
-
-#define UA_TYPE_DELETE_DEFAULT(TYPE) \
-    void TYPE##_delete(TYPE *p) {    \
-        TYPE##_deleteMembers(p);     \
-        UA_free(p);                  \
-    }
+/* static variables */
+UA_EXPORT const UA_String UA_STRING_NULL = {.length = -1, .data = (UA_Byte*)0 };
+UA_EXPORT const UA_ByteString UA_BYTESTRING_NULL = {.length = -1, .data = (UA_Byte*)0 };
+UA_EXPORT const UA_NodeId UA_NODEID_NULL = {0, UA_NODEIDTYPE_NUMERIC, {0}};
+UA_EXPORT const UA_ExpandedNodeId UA_EXPANDEDNODEID_NULL = {
+    .nodeId = { .namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0 },
+    .namespaceUri = {.length = -1, .data = (UA_Byte*)0}, .serverIndex = 0 };
 
 /***************************/
 /* Random Number Generator */
 /***************************/
 
-static UA_THREAD_LOCAL pcg32_random_t rng = PCG32_INITIALIZER;
+static UA_THREAD_LOCAL pcg32_random_t UA_rng = PCG32_INITIALIZER;
 
 UA_EXPORT void UA_random_seed(UA_UInt64 seed) {
-    pcg32_srandom_r(&rng, seed, UA_DateTime_now());
+    pcg32_srandom_r(&UA_rng, seed, UA_DateTime_now());
 }
 
 UA_EXPORT UA_UInt32 UA_random(void) {
-    return (UA_UInt32)pcg32_random_r(&rng);
+    return (UA_UInt32)pcg32_random_r(&UA_rng);
 }
 
 /*****************/
@@ -86,8 +81,7 @@ UA_TYPE_DEFAULT(UA_Float)
 UA_TYPE_DEFAULT(UA_Double)
 
 /* String */
-UA_TYPE_NEW_DEFAULT(UA_String)
-UA_TYPE_DELETE_DEFAULT(UA_String)
+UA_TYPE_DEFAULT(UA_String)
 
 void UA_String_init(UA_String *p) {
     p->length = -1;
@@ -255,16 +249,16 @@ UA_Boolean UA_Guid_equal(const UA_Guid *g1, const UA_Guid *g2) {
 
 UA_Guid UA_Guid_random(UA_UInt32 *seed) {
     UA_Guid result;
-    result.data1 = (UA_UInt32)pcg32_random_r(&rng);
-    UA_UInt32 r = (UA_UInt32)pcg32_random_r(&rng);
+    result.data1 = (UA_UInt32)pcg32_random_r(&UA_rng);
+    UA_UInt32 r = (UA_UInt32)pcg32_random_r(&UA_rng);
     result.data2 = (UA_UInt16) r;
     result.data3 = (UA_UInt16) (r >> 16);
-    r = (UA_UInt32)pcg32_random_r(&rng);
+    r = (UA_UInt32)pcg32_random_r(&UA_rng);
     result.data4[0] = (UA_Byte)r;
     result.data4[1] = (UA_Byte)(r >> 4);
     result.data4[2] = (UA_Byte)(r >> 8);
     result.data4[3] = (UA_Byte)(r >> 12);
-    r = (UA_UInt32)pcg32_random_r(&rng);
+    r = (UA_UInt32)pcg32_random_r(&UA_rng);
     result.data4[4] = (UA_Byte)r;
     result.data4[5] = (UA_Byte)(r >> 4);
     result.data4[6] = (UA_Byte)(r >> 8);
@@ -291,8 +285,7 @@ UA_StatusCode UA_ByteString_newMembers(UA_ByteString *p, UA_Int32 length) {
 /* XmlElement */
 
 /* NodeId */
-UA_TYPE_NEW_DEFAULT(UA_NodeId)
-UA_TYPE_DELETE_DEFAULT(UA_NodeId)
+UA_TYPE_DEFAULT(UA_NodeId)
 
 void UA_NodeId_init(UA_NodeId *p) {
     p->identifierType = UA_NODEIDTYPE_NUMERIC;
@@ -391,65 +384,8 @@ UA_Boolean UA_NodeId_isNull(const UA_NodeId *p) {
     return UA_TRUE;
 }
 
-UA_NodeId UA_NodeId_fromInteger(UA_UInt16 nsIndex, UA_Int32 identifier) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_NUMERIC,
-                         .identifier.numeric = identifier };
-}
-
-UA_NodeId UA_NodeId_fromCharString(UA_UInt16 nsIndex, char identifier[]) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
-                         .identifier.string = UA_STRING(identifier) };
-}
-
-UA_NodeId UA_NodeId_fromCharStringCopy(UA_UInt16 nsIndex, char const identifier[]) {
-    return (UA_NodeId) {.namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
-                        .identifier.string = UA_STRING_ALLOC(identifier) };
-}
-
-UA_NodeId UA_NodeId_fromString(UA_UInt16 nsIndex, UA_String identifier) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_STRING,
-                         .identifier.string = identifier };
-}
-
-UA_NodeId UA_NodeId_fromStringCopy(UA_UInt16 nsIndex, UA_String identifier) {
-    UA_NodeId id;
-    id.namespaceIndex = nsIndex;
-    id.identifierType = UA_NODEIDTYPE_STRING;
-    UA_String_copy(&identifier, &id.identifier.string);
-    return id;
-}
-
-UA_NodeId UA_NodeId_fromGuid(UA_UInt16 nsIndex, UA_Guid identifier) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_GUID,
-                         .identifier.guid = identifier };
-}
-
-UA_NodeId UA_NodeId_fromCharByteString(UA_UInt16 nsIndex, char identifier[]) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
-                         .identifier.byteString = UA_STRING(identifier) };
-}
-
-UA_NodeId UA_NodeId_fromCharByteStringCopy(UA_UInt16 nsIndex, char const identifier[]) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
-                         .identifier.byteString = UA_STRING_ALLOC(identifier) };
-}
-
-UA_NodeId UA_NodeId_fromByteString(UA_UInt16 nsIndex, UA_ByteString identifier) {
-    return (UA_NodeId) { .namespaceIndex = nsIndex, .identifierType = UA_NODEIDTYPE_BYTESTRING,
-                         .identifier.byteString = identifier };
-}
-
-UA_NodeId UA_NodeId_fromByteStringCopy(UA_UInt16 nsIndex, UA_ByteString identifier) {
-    UA_NodeId id;
-    id.namespaceIndex = nsIndex;
-    id.identifierType = UA_NODEIDTYPE_BYTESTRING;
-    UA_ByteString_copy(&identifier, &id.identifier.byteString);
-    return id;
-}
-
 /* ExpandedNodeId */
-UA_TYPE_NEW_DEFAULT(UA_ExpandedNodeId)
-UA_TYPE_DELETE_DEFAULT(UA_ExpandedNodeId)
+UA_TYPE_DEFAULT(UA_ExpandedNodeId)
 
 void UA_ExpandedNodeId_deleteMembers(UA_ExpandedNodeId *p) {
     UA_NodeId_deleteMembers(&p->nodeId);
@@ -473,15 +409,10 @@ UA_StatusCode UA_ExpandedNodeId_copy(UA_ExpandedNodeId const *src, UA_ExpandedNo
     return retval;
 }
 
-UA_Boolean UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p) {
-    return UA_NodeId_isNull(&p->nodeId);
-}
-
 /* StatusCode */
 
 /* QualifiedName */
-UA_TYPE_NEW_DEFAULT(UA_QualifiedName)
-UA_TYPE_DELETE_DEFAULT(UA_QualifiedName)
+UA_TYPE_DEFAULT(UA_QualifiedName)
 
 void UA_QualifiedName_deleteMembers(UA_QualifiedName *p) {
     UA_String_deleteMembers(&p->name);
@@ -503,8 +434,7 @@ UA_StatusCode UA_QualifiedName_copy(UA_QualifiedName const *src, UA_QualifiedNam
 }
 
 /* LocalizedText */
-UA_TYPE_NEW_DEFAULT(UA_LocalizedText)
-UA_TYPE_DELETE_DEFAULT(UA_LocalizedText)
+UA_TYPE_DEFAULT(UA_LocalizedText)
 
 void UA_LocalizedText_deleteMembers(UA_LocalizedText *p) {
     UA_String_deleteMembers(&p->locale);
@@ -527,8 +457,7 @@ UA_StatusCode UA_LocalizedText_copy(UA_LocalizedText const *src, UA_LocalizedTex
 }
 
 /* ExtensionObject */
-UA_TYPE_NEW_DEFAULT(UA_ExtensionObject)
-UA_TYPE_DELETE_DEFAULT(UA_ExtensionObject)
+UA_TYPE_DEFAULT(UA_ExtensionObject)
 
 void UA_ExtensionObject_deleteMembers(UA_ExtensionObject *p) {
     UA_NodeId_deleteMembers(&p->typeId);
@@ -553,8 +482,7 @@ UA_StatusCode UA_ExtensionObject_copy(UA_ExtensionObject const *src, UA_Extensio
 }
 
 /* DataValue */
-UA_TYPE_NEW_DEFAULT(UA_DataValue)
-UA_TYPE_DELETE_DEFAULT(UA_DataValue)
+UA_TYPE_DEFAULT(UA_DataValue)
 
 void UA_DataValue_deleteMembers(UA_DataValue *p) {
     UA_Variant_deleteMembers(&p->value);
@@ -577,8 +505,7 @@ UA_StatusCode UA_DataValue_copy(UA_DataValue const *src, UA_DataValue *dst) {
 }
 
 /* Variant */
-UA_TYPE_NEW_DEFAULT(UA_Variant)
-UA_TYPE_DELETE_DEFAULT(UA_Variant)
+UA_TYPE_DEFAULT(UA_Variant)
 
 void UA_Variant_init(UA_Variant *p) {
     p->storageType = UA_VARIANT_DATA;
@@ -846,8 +773,7 @@ UA_StatusCode UA_Variant_setArrayCopy(UA_Variant *v, const void *array, UA_Int32
 }
 
 /* DiagnosticInfo */
-UA_TYPE_NEW_DEFAULT(UA_DiagnosticInfo)
-UA_TYPE_DELETE_DEFAULT(UA_DiagnosticInfo)
+UA_TYPE_DEFAULT(UA_DiagnosticInfo)
 
 void UA_DiagnosticInfo_deleteMembers(UA_DiagnosticInfo *p) {
     UA_String_deleteMembers(&p->additionalInfo);
@@ -889,87 +815,68 @@ UA_StatusCode UA_DiagnosticInfo_copy(UA_DiagnosticInfo const *src, UA_Diagnostic
 /*******************/
 
 void UA_init(void *p, const UA_DataType *dataType) {
-    /* Do not check if the index is a builtin-type here. Builtins will be called
-       with their very own _init functions normally. In the off case, that the
-       generic function is called with the index of a builtin, their layout
-       contains a single member of the builtin type, that will be inited in the
-       for loop. */
+    switch(dataType->typeIndex) {
+    case UA_TYPES_BOOLEAN:
+    case UA_TYPES_SBYTE:
+    case UA_TYPES_BYTE:
+        *(UA_Byte*)p = 0;
+        return;
+    case UA_TYPES_INT16:
+    case UA_TYPES_UINT16:
+        *(UA_Int16*)p = 0;
+        return;
+    case UA_TYPES_INT32:
+    case UA_TYPES_UINT32:
+    case UA_TYPES_STATUSCODE:
+    case UA_TYPES_FLOAT:
+        *(UA_Int32*)p = 0;
+        return;
+    case UA_TYPES_INT64:
+    case UA_TYPES_UINT64:
+    case UA_TYPES_DOUBLE:
+    case UA_TYPES_DATETIME:
+        *(UA_Int64*)p = 0;
+        return;
+    case UA_TYPES_GUID:
+        UA_Guid_init((UA_Guid*)p);
+        return;
+    case UA_TYPES_NODEID:
+        UA_NodeId_init((UA_NodeId*)p);
+        return;
+    case UA_TYPES_EXTENSIONOBJECT:
+        UA_ExtensionObject_init((UA_ExtensionObject*)p);
+        return;
+    case UA_TYPES_DATAVALUE:
+        UA_DataValue_init((UA_DataValue*)p);
+        return;
+    case UA_TYPES_VARIANT:
+        UA_Variant_init((UA_Variant*)p);
+        return;
+    case UA_TYPES_DIAGNOSTICINFO:
+        UA_DiagnosticInfo_init((UA_DiagnosticInfo*)p);
+        return;
+    }
 
     uintptr_t ptr = (uintptr_t)p;
     UA_Byte membersSize = dataType->membersSize;
-    for(size_t i=0;i<membersSize; i++) {
+    for(size_t i = 0; i < membersSize; i++) {
         const UA_DataTypeMember *member = &dataType->members[i];
-        if(member->isArray) {
-            /* Padding contains bit-magic to split into padding before and after
-               the length integer */
+        if(!member->isArray) {
+            const UA_DataType *memberType;
+            if(member->namespaceZero)
+                memberType = &UA_TYPES[member->memberTypeIndex];
+            else
+                memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
+            ptr += member->padding;
+            UA_init((void*)ptr, memberType);
+            ptr += memberType->memSize;
+        } else {
             ptr += (member->padding >> 3);
             *((UA_Int32*)ptr) = -1;
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
             *((void**)ptr) = UA_NULL;
             ptr += sizeof(void*);
-            continue;
-        }
-
-        ptr += member->padding;
-        if(!member->namespaceZero) {
-            // pointer arithmetic
-            const UA_DataType *memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-            UA_init((void*)ptr, memberType);
-            ptr += memberType->memSize;
-            continue;
-        }
-
-        switch(member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-            *(UA_Byte*)ptr = 0;
-            break;
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-            *(UA_Int16*)ptr = 0;
-            break;
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-        case UA_TYPES_FLOAT:
-            *(UA_Int32*)ptr = 0;
-            break;
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DOUBLE:
-        case UA_TYPES_DATETIME:
-            *(UA_Int64*)ptr = 0;
-            break;
-        case UA_TYPES_GUID:
-            UA_Guid_init((UA_Guid*)ptr);
-            break;
-        case UA_TYPES_NODEID:
-            UA_NodeId_init((UA_NodeId*)ptr);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            UA_ExpandedNodeId_init((UA_ExpandedNodeId*)ptr);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            UA_LocalizedText_init((UA_LocalizedText*)ptr);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            UA_ExtensionObject_init((UA_ExtensionObject*)ptr);
-            break;
-        case UA_TYPES_DATAVALUE:
-            UA_DataValue_init((UA_DataValue*)ptr);
-            break;
-        case UA_TYPES_VARIANT:
-            UA_Variant_init((UA_Variant*)ptr);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            UA_DiagnosticInfo_init((UA_DiagnosticInfo*)ptr);
-            break;
-        default:
-            // QualifiedName, LocalizedText and strings are treated as structures, also
-            UA_init((void*)ptr, &UA_TYPES[member->memberTypeIndex]);
         }
-        ptr += UA_TYPES[member->memberTypeIndex].memSize;
     }
 }
 
@@ -985,20 +892,39 @@ UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *dataType) {
         memcpy(dst, src, dataType->memSize);
         return UA_STATUSCODE_GOOD;
     }
+    switch(dataType->typeIndex) {
+    case UA_TYPES_NODEID:
+        return UA_NodeId_copy((const UA_NodeId*)src, (UA_NodeId*)dst);
+    case UA_TYPES_EXTENSIONOBJECT:
+        return UA_ExtensionObject_copy((const UA_ExtensionObject*)src, (UA_ExtensionObject*)dst);
+    case UA_TYPES_DATAVALUE:
+        return UA_DataValue_copy((const UA_DataValue*)src, (UA_DataValue*)dst);
+    case UA_TYPES_VARIANT:
+        return UA_Variant_copy((const UA_Variant*)src, (UA_Variant*)dst);
+    case UA_TYPES_DIAGNOSTICINFO:
+        return UA_DiagnosticInfo_copy((const UA_DiagnosticInfo*)src, (UA_DiagnosticInfo*)dst);
+    }
+
     UA_init(dst, dataType);
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     uintptr_t ptrs = (uintptr_t)src;
     uintptr_t ptrd = (uintptr_t)dst;
     UA_Byte membersSize = dataType->membersSize;
-    for(size_t i=0;i<membersSize; i++) {
+    size_t i;
+    for(i = 0; i < membersSize && retval == UA_STATUSCODE_GOOD; i++) {
         const UA_DataTypeMember *member = &dataType->members[i];
         const UA_DataType *memberType;
         if(member->namespaceZero)
             memberType = &UA_TYPES[member->memberTypeIndex];
         else
             memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-
-        if(member->isArray) {
+        if(!member->isArray) {
+            ptrs += member->padding;
+            ptrd += member->padding;
+            retval = UA_copy((const void*)ptrs, (void*)ptrd, memberType);
+            ptrs += memberType->memSize;
+            ptrd += memberType->memSize;
+        } else {
             ptrs += (member->padding >> 3);
             ptrd += (member->padding >> 3);
             UA_Int32 *dstNoElements = (UA_Int32*)ptrd;
@@ -1006,174 +932,66 @@ UA_StatusCode UA_copy(const void *src, void *dst, const UA_DataType *dataType) {
             ptrs += sizeof(UA_Int32) + (member->padding & 0x07);
             ptrd += sizeof(UA_Int32) + (member->padding & 0x07);
             retval = UA_Array_copy(*(void* const*)ptrs, (void**)ptrd, memberType, elements);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_deleteMembers(dst, dataType);
-                UA_init(dst, dataType);
-                return retval;
-            }
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
             *dstNoElements = elements;
             ptrs += sizeof(void*);
             ptrd += sizeof(void*);
-            continue;
-        }
-
-        ptrs += member->padding;
-        ptrd += member->padding;
-        if(!member->namespaceZero) {
-            retval = UA_copy((const void*)ptrs, (void*)ptrd, memberType);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_deleteMembers(dst, dataType);
-                UA_init(dst, dataType);
-                return retval;
-            }
-            ptrs += memberType->memSize;
-            ptrd += memberType->memSize;
-            continue;
-        }
-
-        switch(member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-            *((UA_Byte*)ptrd) = *((const UA_Byte*)ptrs);
-            break;
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-            *((UA_Int16*)ptrd) = *((const UA_Byte*)ptrs);
-            break;
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-        case UA_TYPES_FLOAT:
-            *((UA_Int32*)ptrd) = *((const UA_Int32*)ptrs);
-            break;
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DOUBLE:
-        case UA_TYPES_DATETIME:
-            *((UA_Int64*)ptrd) = *((const UA_Int64*)ptrs);
-            break;
-        case UA_TYPES_GUID:
-            *((UA_Guid*)ptrd) = *((const UA_Guid*)ptrs);
-            break;
-        case UA_TYPES_NODEID:
-            retval |= UA_NodeId_copy((const UA_NodeId*)ptrs, (UA_NodeId*)ptrd);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            retval |= UA_ExpandedNodeId_copy((const UA_ExpandedNodeId*)ptrs, (UA_ExpandedNodeId*)ptrd);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            retval |= UA_LocalizedText_copy((const UA_LocalizedText*)ptrs, (UA_LocalizedText*)ptrd);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            retval |= UA_ExtensionObject_copy((const UA_ExtensionObject*)ptrs, (UA_ExtensionObject*)ptrd);
-            break;
-        case UA_TYPES_DATAVALUE:
-            retval |= UA_DataValue_copy((const UA_DataValue*)ptrs, (UA_DataValue*)ptrd);
-            break;
-        case UA_TYPES_VARIANT:
-            retval |= UA_Variant_copy((const UA_Variant*)ptrs, (UA_Variant*)ptrd);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            retval |= UA_DiagnosticInfo_copy((const UA_DiagnosticInfo*)ptrs, (UA_DiagnosticInfo*)ptrd);
-            break;
-        default:
-            // QualifiedName, LocalizedText and strings are treated as structures, also
-            retval |= UA_copy((const void *)ptrs, (void*)ptrd, memberType);
         }
-        ptrs += memberType->memSize;
-        ptrd += memberType->memSize;
-    }
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_deleteMembers(dst, dataType);
-        UA_init(dst, dataType);
     }
+    if(retval != UA_STATUSCODE_GOOD)
+        UA_deleteMembersUntil(dst, dataType, i);
     return retval;
 }
 
 void UA_deleteMembers(void *p, const UA_DataType *dataType) {
-    UA_deleteMembersUntil(p, dataType, -1);
+    UA_deleteMembersUntil(p, dataType, UA_UINT16_MIN); // lastMember is bigger than the possible member count
 }
 
-void UA_deleteMembersUntil(void *p, const UA_DataType *dataType, UA_Int32 lastMember) {
-    uintptr_t ptr = (uintptr_t)p;
+void UA_deleteMembersUntil(void *p, const UA_DataType *dataType, size_t lastMember) {
     if(dataType->fixedSize)
         return;
-    UA_Byte membersSize = dataType->membersSize;
-    for(size_t i=0;i<membersSize; i++) {
-        if(lastMember > -1 && (UA_Int32)i > lastMember){
-            return;
-        }
+    switch(dataType->typeIndex) {
+    case UA_TYPES_NODEID:
+        UA_NodeId_deleteMembers((UA_NodeId*)p);
+        return;
+    case UA_TYPES_EXTENSIONOBJECT:
+        UA_ExtensionObject_deleteMembers((UA_ExtensionObject*)p);
+        return;
+    case UA_TYPES_DATAVALUE:
+        UA_DataValue_deleteMembers((UA_DataValue*)p);
+        return;
+    case UA_TYPES_VARIANT:
+        UA_Variant_deleteMembers((UA_Variant*)p);
+        return;
+    case UA_TYPES_DIAGNOSTICINFO:
+        UA_DiagnosticInfo_deleteMembers((UA_DiagnosticInfo*)p);
+        return;
+    }
+
+    uintptr_t ptr = (uintptr_t)p;
+    size_t until = dataType->membersSize;
+    if(lastMember < until)
+        until = lastMember;
+    for(size_t i = 0; i < until; i++) {
         const UA_DataTypeMember *member = &dataType->members[i];
         const UA_DataType *memberType;
         if(member->namespaceZero)
             memberType = &UA_TYPES[member->memberTypeIndex];
         else
             memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-
-        if(member->isArray) {
+        if(!member->isArray) {
+            ptr += member->padding;
+            UA_deleteMembers((void*)ptr, memberType);
+            ptr += memberType->memSize;
+        } else {
             ptr += (member->padding >> 3);
             UA_Int32 elements = *((UA_Int32*)ptr);
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
             UA_Array_delete(*(void**)ptr, memberType, elements);
             *(void**)ptr = UA_NULL;
             ptr += sizeof(void*);
-            continue;
-        }
-
-        ptr += member->padding;
-        if(!member->namespaceZero) {
-            UA_deleteMembers((void*)ptr, memberType);
-            ptr += memberType->memSize;
-            continue;
         }
-
-        switch(member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-        case UA_TYPES_FLOAT:
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DOUBLE:
-        case UA_TYPES_DATETIME:
-        case UA_TYPES_GUID:
-            break;
-        case UA_TYPES_NODEID:
-            UA_NodeId_deleteMembers((UA_NodeId*)ptr);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            UA_ExpandedNodeId_deleteMembers((UA_ExpandedNodeId*)ptr);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            UA_LocalizedText_deleteMembers((UA_LocalizedText*)ptr);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            UA_ExtensionObject_deleteMembers((UA_ExtensionObject*)ptr);
-            break;
-        case UA_TYPES_DATAVALUE:
-            UA_DataValue_deleteMembers((UA_DataValue*)ptr);
-            break;
-        case UA_TYPES_VARIANT:
-            UA_Variant_deleteMembers((UA_Variant*)ptr);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            UA_DiagnosticInfo_deleteMembers((UA_DiagnosticInfo*)ptr);
-            break;
-        default:
-            // QualifiedName, LocalizedText and strings are treated as structures, also
-            if(lastMember > -1){
-                UA_deleteMembersUntil((void*)ptr, memberType, lastMember-i);
-            }
-            else
-                UA_deleteMembers((void*)ptr, memberType);
-        }
-        ptr += memberType->memSize;
     }
 }
 
@@ -1227,10 +1045,8 @@ UA_StatusCode UA_Array_copy(const void *src, void **dst, const UA_DataType *data
         ptrs += dataType->memSize;
         ptrd += dataType->memSize;
     }
-
     if(retval != UA_STATUSCODE_GOOD)
         UA_Array_delete(*dst, dataType, elements);
-
     return retval;
 }
 

+ 142 - 200
src/ua_types_encoding_binary.c

@@ -930,7 +930,47 @@ UA_StatusCode UA_DiagnosticInfo_decodeBinary(UA_ByteString const *src, size_t *U
 
 UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *dataType, UA_ByteString *dst,
                               size_t *UA_RESTRICT offset) {
-    uintptr_t ptr = (uintptr_t) src;
+    /* builtin types */
+    switch (dataType->typeIndex) {
+    case UA_TYPES_BOOLEAN:
+    case UA_TYPES_SBYTE:
+    case UA_TYPES_BYTE:
+        return UA_Byte_encodeBinary((const UA_Byte*)src, dst, offset);
+    case UA_TYPES_INT16:
+    case UA_TYPES_UINT16:
+        return UA_UInt16_encodeBinary((const UA_UInt16*)src, dst, offset);
+    case UA_TYPES_INT32:
+    case UA_TYPES_UINT32:
+    case UA_TYPES_STATUSCODE:
+        return UA_UInt32_encodeBinary((const UA_UInt32*)src, dst, offset);
+    case UA_TYPES_INT64:
+    case UA_TYPES_UINT64:
+    case UA_TYPES_DATETIME:
+        return UA_UInt64_encodeBinary((const UA_UInt64*)src, dst, offset);
+    case UA_TYPES_FLOAT:
+        return UA_Float_encodeBinary((const UA_Float*)src, dst, offset);
+    case UA_TYPES_DOUBLE:
+        return UA_Double_encodeBinary((const UA_Double*)src, dst, offset);
+    case UA_TYPES_GUID:
+        return UA_Guid_encodeBinary((const UA_Guid*)src, dst, offset);
+    case UA_TYPES_NODEID:
+        return UA_NodeId_encodeBinary((const UA_NodeId*)src, dst, offset);
+    case UA_TYPES_EXPANDEDNODEID:
+        return UA_ExpandedNodeId_encodeBinary((const UA_ExpandedNodeId*)src, dst, offset);
+    case UA_TYPES_LOCALIZEDTEXT:
+        return UA_LocalizedText_encodeBinary((const UA_LocalizedText*)src, dst, offset);
+    case UA_TYPES_EXTENSIONOBJECT:
+        return UA_ExtensionObject_encodeBinary((const UA_ExtensionObject*)src, dst, offset);
+    case UA_TYPES_DATAVALUE:
+        return UA_DataValue_encodeBinary((const UA_DataValue*)src, dst, offset);
+    case UA_TYPES_VARIANT:
+        return UA_Variant_encodeBinary((const UA_Variant*)src, dst, offset);
+    case UA_TYPES_DIAGNOSTICINFO:
+        return UA_DiagnosticInfo_encodeBinary((const UA_DiagnosticInfo*)src, dst, offset);
+    }
+
+    /* structured type */
+    uintptr_t ptr = (uintptr_t)src;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Byte membersSize = dataType->membersSize;
     for(size_t i = 0; i < membersSize && retval == UA_STATUSCODE_GOOD; i++) {
@@ -940,84 +980,63 @@ UA_StatusCode UA_encodeBinary(const void *src, const UA_DataType *dataType, UA_B
             memberType = &UA_TYPES[member->memberTypeIndex];
         else
             memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-        if(member->isArray) {
+        if(!member->isArray) {
+            ptr += member->padding;
+            retval = UA_encodeBinary((const void*) ptr, memberType, dst, offset);
+            ptr += memberType->memSize;
+        } else {
             ptr += (member->padding >> 3);
             const UA_Int32 noElements = *((const UA_Int32*) ptr);
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
             retval = UA_Array_encodeBinary(*(void * const *) ptr, noElements, memberType, dst, offset);
             ptr += sizeof(void*);
-            continue;
-        }
-
-        ptr += member->padding;
-        if(!member->namespaceZero) {
-            UA_encodeBinary((const void*) ptr, memberType, dst, offset);
-            ptr += memberType->memSize;
-            continue;
         }
-
-        switch (member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-            retval = UA_Byte_encodeBinary((const UA_Byte*) ptr, dst, offset);
-            break;
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-            retval = UA_UInt16_encodeBinary((const UA_UInt16*) ptr, dst, offset);
-            break;
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-            retval = UA_UInt32_encodeBinary((const UA_UInt32*) ptr, dst, offset);
-            break;
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DATETIME:
-            retval = UA_UInt64_encodeBinary((const UA_UInt64*) ptr, dst, offset);
-            break;
-        case UA_TYPES_FLOAT:
-            retval = UA_Float_encodeBinary((const UA_Float*) ptr, dst, offset);
-            break;
-        case UA_TYPES_DOUBLE:
-            retval = UA_Double_encodeBinary((const UA_Double*) ptr, dst, offset);
-            break;
-        case UA_TYPES_GUID:
-            retval = UA_Guid_encodeBinary((const UA_Guid*) ptr, dst, offset);
-            break;
-        case UA_TYPES_NODEID:
-            retval = UA_NodeId_encodeBinary((const UA_NodeId*) ptr, dst, offset);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            retval = UA_ExpandedNodeId_encodeBinary((const UA_ExpandedNodeId*) ptr, dst, offset);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            retval = UA_LocalizedText_encodeBinary((const UA_LocalizedText*) ptr, dst, offset);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            retval = UA_ExtensionObject_encodeBinary((const UA_ExtensionObject*) ptr, dst, offset);
-            break;
-        case UA_TYPES_DATAVALUE:
-            retval = UA_DataValue_encodeBinary((const UA_DataValue*) ptr, dst, offset);
-            break;
-        case UA_TYPES_VARIANT:
-            retval = UA_Variant_encodeBinary((const UA_Variant*) ptr, dst, offset);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            retval = UA_DiagnosticInfo_encodeBinary((const UA_DiagnosticInfo*) ptr, dst, offset);
-            break;
-        default:
-            // also handles UA_TYPES_QUALIFIEDNAME, UA_TYPES_STRING, UA_TYPES_BYTESTRING,
-            // UA_TYPES_XMLELEMENT
-            retval = UA_encodeBinary((const void*) ptr, memberType, dst, offset);
-        }
-        ptr += memberType->memSize;
     }
     return retval;
 }
 
 UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t *UA_RESTRICT offset,
                               void *dst, const UA_DataType *dataType) {
+    /* builtin types */
+    switch (dataType->typeIndex) {
+    case UA_TYPES_BOOLEAN:
+    case UA_TYPES_SBYTE:
+    case UA_TYPES_BYTE:
+        return UA_Byte_decodeBinary(src, offset, (UA_Byte*)dst);
+    case UA_TYPES_INT16:
+    case UA_TYPES_UINT16:
+        return UA_Int16_decodeBinary(src, offset, (UA_Int16*)dst);
+    case UA_TYPES_INT32:
+    case UA_TYPES_UINT32:
+    case UA_TYPES_STATUSCODE:
+        return UA_UInt32_decodeBinary(src, offset, (UA_UInt32*)dst);
+    case UA_TYPES_INT64:
+    case UA_TYPES_UINT64:
+    case UA_TYPES_DATETIME:
+        return UA_UInt64_decodeBinary(src, offset, (UA_UInt64*)dst);
+    case UA_TYPES_FLOAT:
+        return UA_Float_decodeBinary(src, offset, (UA_Float*)dst);
+    case UA_TYPES_DOUBLE:
+        return UA_Double_decodeBinary(src, offset, (UA_Double*)dst);
+    case UA_TYPES_GUID:
+        return UA_Guid_decodeBinary(src, offset, (UA_Guid*)dst);
+    case UA_TYPES_NODEID:
+        return UA_NodeId_decodeBinary(src, offset, (UA_NodeId*)dst);
+    case UA_TYPES_EXPANDEDNODEID:
+        return UA_ExpandedNodeId_decodeBinary(src, offset, (UA_ExpandedNodeId*)dst);
+    case UA_TYPES_LOCALIZEDTEXT:
+        return UA_LocalizedText_decodeBinary(src, offset, (UA_LocalizedText*)dst);
+    case UA_TYPES_EXTENSIONOBJECT:
+        return UA_ExtensionObject_decodeBinary(src, offset, (UA_ExtensionObject*)dst);
+    case UA_TYPES_DATAVALUE:
+        return UA_DataValue_decodeBinary(src, offset, (UA_DataValue*)dst);
+    case UA_TYPES_VARIANT:
+        return UA_Variant_decodeBinary(src, offset, (UA_Variant*)dst);
+    case UA_TYPES_DIAGNOSTICINFO:
+        return UA_DiagnosticInfo_decodeBinary(src, offset, (UA_DiagnosticInfo*)dst);
+    }
+
+    /* structured types */
     uintptr_t ptr = (uintptr_t) dst;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Byte membersSize = dataType->membersSize;
@@ -1029,84 +1048,24 @@ UA_StatusCode UA_decodeBinary(const UA_ByteString *src, size_t *UA_RESTRICT offs
             memberType = &UA_TYPES[member->memberTypeIndex];
         else
             memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-        if(member->isArray) {
+        if(!member->isArray) {
+            ptr += member->padding;
+            retval = UA_decodeBinary(src, offset, (void*)ptr, memberType);
+            ptr += memberType->memSize;
+        } else {
             ptr += (member->padding >> 3);
             UA_Int32 *noElements = (UA_Int32*) ptr;
             UA_Int32 tempNoElements = 0;
-            retval |= UA_Int32_decodeBinary(src, offset, &tempNoElements);
-            if(retval)
-                continue;
+            retval = UA_Int32_decodeBinary(src, offset, &tempNoElements);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
-            retval = UA_Array_decodeBinary(src, offset, tempNoElements, (void**) ptr, memberType);
-            if(retval == UA_STATUSCODE_GOOD)
-                *noElements = tempNoElements;
+            retval = UA_Array_decodeBinary(src, offset, tempNoElements, (void**)ptr, memberType);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
+            *noElements = tempNoElements;
             ptr += sizeof(void*);
-            continue;
-        }
-
-        ptr += member->padding;
-        if(!member->namespaceZero) {
-            UA_decodeBinary(src, offset, (void*) ptr, memberType);
-            ptr += memberType->memSize;
-            continue;
         }
-
-        switch (member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-            retval = UA_Byte_decodeBinary(src, offset, (UA_Byte*) ptr);
-            break;
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-            retval = UA_Int16_decodeBinary(src, offset, (UA_Int16*) ptr);
-            break;
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-            retval = UA_UInt32_decodeBinary(src, offset, (UA_UInt32*) ptr);
-            break;
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DATETIME:
-            retval = UA_UInt64_decodeBinary(src, offset, (UA_UInt64*) ptr);
-            break;
-        case UA_TYPES_FLOAT:
-            retval = UA_Float_decodeBinary(src, offset, (UA_Float*) ptr);
-            break;
-        case UA_TYPES_DOUBLE:
-            retval = UA_Double_decodeBinary(src, offset, (UA_Double*) ptr);
-            break;
-        case UA_TYPES_GUID:
-            retval = UA_Guid_decodeBinary(src, offset, (UA_Guid*) ptr);
-            break;
-        case UA_TYPES_NODEID:
-            retval = UA_NodeId_decodeBinary(src, offset, (UA_NodeId*) ptr);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            retval = UA_ExpandedNodeId_decodeBinary(src, offset, (UA_ExpandedNodeId*) ptr);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            retval = UA_LocalizedText_decodeBinary(src, offset, (UA_LocalizedText*) ptr);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            retval = UA_ExtensionObject_decodeBinary(src, offset, (UA_ExtensionObject*) ptr);
-            break;
-        case UA_TYPES_DATAVALUE:
-            retval = UA_DataValue_decodeBinary(src, offset, (UA_DataValue*) ptr);
-            break;
-        case UA_TYPES_VARIANT:
-            retval = UA_Variant_decodeBinary(src, offset, (UA_Variant*) ptr);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            retval = UA_DiagnosticInfo_decodeBinary(src, offset, (UA_DiagnosticInfo*) ptr);
-            break;
-        default:
-            // also handles UA_TYPES_QUALIFIEDNAME, UA_TYPES_STRING, UA_TYPES_BYTESTRING,
-            // UA_TYPES_XMLELEMENT
-            retval = UA_decodeBinary(src, offset, (void*) ptr, memberType);
-        }
-        ptr += memberType->memSize;
     }
     if(retval != UA_STATUSCODE_GOOD)
         UA_deleteMembersUntil(dst, dataType, i);
@@ -1271,6 +1230,44 @@ static size_t UA_DiagnosticInfo_calcSizeBinary(UA_DiagnosticInfo const *p) {
 }
 
 size_t UA_calcSizeBinary(const void *p, const UA_DataType *dataType) {
+    /* builtin types */
+    switch (dataType->typeIndex) {
+    case UA_TYPES_BOOLEAN:
+    case UA_TYPES_SBYTE:
+    case UA_TYPES_BYTE:
+        return 1;
+    case UA_TYPES_INT16:
+    case UA_TYPES_UINT16:
+        return 2;
+    case UA_TYPES_INT32:
+    case UA_TYPES_UINT32:
+    case UA_TYPES_STATUSCODE:
+    case UA_TYPES_FLOAT:
+        return 4;
+    case UA_TYPES_INT64:
+    case UA_TYPES_UINT64:
+    case UA_TYPES_DOUBLE:
+    case UA_TYPES_DATETIME:
+        return 8;
+    case UA_TYPES_GUID:
+        return 16;
+    case UA_TYPES_NODEID:
+        return UA_NodeId_calcSizeBinary((const UA_NodeId*)p);
+    case UA_TYPES_EXPANDEDNODEID:
+        return UA_ExpandedNodeId_calcSizeBinary((const UA_ExpandedNodeId*)p);
+    case UA_TYPES_LOCALIZEDTEXT:
+        return UA_LocalizedText_calcSizeBinary((const UA_LocalizedText*)p);
+    case UA_TYPES_EXTENSIONOBJECT:
+        return UA_ExtensionObject_calcSizeBinary((const UA_ExtensionObject*)p);
+    case UA_TYPES_DATAVALUE:
+        return UA_DataValue_calcSizeBinary((const UA_DataValue*)p);
+    case UA_TYPES_VARIANT:
+        return UA_Variant_calcSizeBinary((const UA_Variant*)p);
+    case UA_TYPES_DIAGNOSTICINFO:
+        return UA_DiagnosticInfo_calcSizeBinary((const UA_DiagnosticInfo*)p);
+    }
+
+    /* structured types */
     size_t size = 0;
     uintptr_t ptr = (uintptr_t) p;
     UA_Byte membersSize = dataType->membersSize;
@@ -1281,73 +1278,18 @@ size_t UA_calcSizeBinary(const void *p, const UA_DataType *dataType) {
             memberType = &UA_TYPES[member->memberTypeIndex];
         else
             memberType = &dataType[member->memberTypeIndex - dataType->typeIndex];
-
-        if(member->isArray) {
+        if(!member->isArray) {
+            ptr += member->padding;
+            size += UA_calcSizeBinary((const void*)ptr, memberType);
+            ptr += memberType->memSize;
+        } else {
             ptr += (member->padding >> 3);
             const UA_Int32 elements = *((const UA_Int32*) ptr);
             ptr += sizeof(UA_Int32) + (member->padding & 0x07);
-            size += UA_Array_calcSizeBinary(*(void * const *) ptr, elements, memberType);
+            size += UA_Array_calcSizeBinary(*(void * const *)ptr, elements, memberType);
             ptr += sizeof(void*);
             continue;
         }
-
-        ptr += member->padding;
-        if(!member->namespaceZero) {
-            size += UA_calcSizeBinary((const void*) ptr, memberType);
-            ptr += memberType->memSize;
-            continue;
-        }
-
-        switch (member->memberTypeIndex) {
-        case UA_TYPES_BOOLEAN:
-        case UA_TYPES_SBYTE:
-        case UA_TYPES_BYTE:
-            size += 1;
-            break;
-        case UA_TYPES_INT16:
-        case UA_TYPES_UINT16:
-            size += 2;
-            break;
-        case UA_TYPES_INT32:
-        case UA_TYPES_UINT32:
-        case UA_TYPES_STATUSCODE:
-        case UA_TYPES_FLOAT:
-            size += 4;
-            break;
-        case UA_TYPES_INT64:
-        case UA_TYPES_UINT64:
-        case UA_TYPES_DOUBLE:
-        case UA_TYPES_DATETIME:
-            size += 8;
-            break;
-        case UA_TYPES_GUID:
-            size += 16;
-            break;
-        case UA_TYPES_NODEID:
-            size += UA_NodeId_calcSizeBinary((const UA_NodeId*) ptr);
-            break;
-        case UA_TYPES_EXPANDEDNODEID:
-            size += UA_ExpandedNodeId_calcSizeBinary((const UA_ExpandedNodeId*) ptr);
-            break;
-        case UA_TYPES_LOCALIZEDTEXT:
-            size += UA_LocalizedText_calcSizeBinary((const UA_LocalizedText*) ptr);
-            break;
-        case UA_TYPES_EXTENSIONOBJECT:
-            size += UA_ExtensionObject_calcSizeBinary((const UA_ExtensionObject*) ptr);
-            break;
-        case UA_TYPES_DATAVALUE:
-            size += UA_DataValue_calcSizeBinary((const UA_DataValue*) ptr);
-            break;
-        case UA_TYPES_VARIANT:
-            size += UA_Variant_calcSizeBinary((const UA_Variant*) ptr);
-            break;
-        case UA_TYPES_DIAGNOSTICINFO:
-            size += UA_DiagnosticInfo_calcSizeBinary((const UA_DiagnosticInfo*) ptr);
-            break;
-        default:
-            size += UA_calcSizeBinary((const void*) ptr, memberType);
-        }
-        ptr += memberType->memSize;
     }
     return size;
 }

+ 4 - 2
tests/CMakeLists.txt

@@ -46,12 +46,14 @@ add_executable(check_services_attributes check_services_attributes.c $<TARGET_OB
 target_link_libraries(check_services_attributes ${LIBS})
 add_test(services_attributes ${CMAKE_CURRENT_BINARY_DIR}/check_services_attributes)
 
+add_executable(check_services_nodemanagement check_services_nodemanagement.c $<TARGET_OBJECTS:open62541-object>)
+target_link_libraries(check_services_nodemanagement ${LIBS})
+add_test(services_nodemanagement ${CMAKE_CURRENT_BINARY_DIR}/check_services_nodemanagement)
+
 add_executable(check_nodestore check_nodestore.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_nodestore ${LIBS})
 add_test(nodestore ${CMAKE_CURRENT_BINARY_DIR}/check_nodestore)
 
-
-
 add_executable(check_session check_session.c $<TARGET_OBJECTS:open62541-object>)
 target_link_libraries(check_session ${LIBS})
 add_test(session ${CMAKE_CURRENT_BINARY_DIR}/check_session)

Diferenças do arquivo suprimidas por serem muito extensas
+ 859 - 1015
tests/check_services_attributes.c


+ 60 - 0
tests/check_services_nodemanagement.c

@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "check.h"
+#include "server/ua_nodestore.h"
+#include "server/ua_services.h"
+#include "ua_client.h"
+#include "ua_nodeids.h"
+#include "ua_statuscodes.h"
+#include "ua_types.h"
+#include "ua_util.h"
+#include "server/ua_server_internal.h"
+
+#ifdef UA_MULTITHREADING
+#include <pthread.h>
+#include <urcu.h>
+#endif
+
+START_TEST(AddVariableNode) {
+    UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+
+    /* add a variable node to the address space */
+    UA_VariableAttributes attr;
+    UA_VariableAttributes_init(&attr);
+    UA_Int32 myInteger = 42;
+    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    attr.description = UA_LOCALIZEDTEXT("en_US","the answer");
+    attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer");
+    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
+    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
+    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
+    UA_StatusCode res = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId,
+                                                  myIntegerName, UA_NODEID_NULL, attr, NULL);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, res);
+    UA_Server_delete(server);
+} END_TEST
+
+static Suite * testSuite_services_nodemanagement(void) {
+	Suite *s = suite_create("services_nodemanagement");
+
+	TCase *tc_addnodes = tcase_create("addnodes");
+	tcase_add_test(tc_addnodes, AddVariableNode);
+
+	suite_add_tcase(s, tc_addnodes);
+	return s;
+}
+
+int main(void) {
+	int number_failed = 0;
+	Suite *s;
+	s = testSuite_services_nodemanagement();
+	SRunner *sr = srunner_create(s);
+	// srunner_set_fork_status(sr, CK_NOFORK);
+	srunner_run_all(sr, CK_NORMAL);
+	number_failed += srunner_ntests_failed(sr);
+	srunner_free(sr);
+	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}

+ 38 - 11
tools/generate_datatypes.py

@@ -53,9 +53,10 @@ minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "Applicati
                  "AddNodesItem", "AddNodesResult", "DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse",
                  "AddReferencesItem","DeleteReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode",
                  "ViewNode", "ReferenceTypeNode", "BrowseResultMask", "ServerState", "ServerStatusDataType", "BuildInfo",
-                 "ObjectNode", "DataTypeNode", "ObjectTypeNode", "IdType", "VariableAttributes", "ObjectAttributes",
-                 "NodeAttributes","ReferenceTypeAttributes", "ViewAttributes", "ObjectTypeAttributes",
-                 "NodeAttributesMask","DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
+                 "ObjectNode", "DataTypeNode", "ObjectTypeNode", "IdType", "NodeAttributes",
+                 "VariableAttributes", "ObjectAttributes", "ReferenceTypeAttributes", "ViewAttributes", "MethodAttributes",
+                 "ObjectTypeAttributes", "VariableTypeAttributes", "DataTypeAttributes", "NodeAttributesMask",
+                 "DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
                  "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
                  "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse", 
                  "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault",
@@ -124,28 +125,54 @@ class BuiltinType(object):
                 ".padding = offsetof(UA_String, data) - sizeof(UA_Int32), .isArray = UA_TRUE }}, " + \
                 ".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
 
+        if self.name == "UA_ExpandedNodeId":
+            return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
+                ".memSize = sizeof(UA_ExpandedNodeId), " + \
+                ".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
+                ".membersSize = 3, .members = {" + \
+                "\n\t{.memberTypeIndex = UA_TYPES_NODEID, .namespaceZero = UA_TRUE, " + \
+                (".memberName = \"nodeId\", " if typeintrospection else "") + \
+                ".padding = 0, .isArray = UA_FALSE }," + \
+                "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
+                (".memberName = \"namespaceUri\", " if typeintrospection else "") + \
+                ".padding = offsetof(UA_ExpandedNodeId, namespaceUri) - sizeof(UA_NodeId), .isArray = UA_FALSE }," + \
+                "\n\t{.memberTypeIndex = UA_TYPES_UINT32, .namespaceZero = UA_TRUE, " + \
+                (".memberName = \"serverIndex\", " if typeintrospection else "") + \
+                ".padding = offsetof(UA_ExpandedNodeId, serverIndex) - offsetof(UA_ExpandedNodeId, namespaceUri) - sizeof(UA_String), .isArray = UA_FALSE }},\n" + \
+                ".typeIndex = UA_TYPES_EXPANDEDNODEID }"
+
         if self.name == "UA_QualifiedName":
             return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
                 ".memSize = sizeof(UA_QualifiedName), " + \
                 ".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
                 ".membersSize = 2, .members = {" + \
                 "\n\t{.memberTypeIndex = UA_TYPES_UINT16, .namespaceZero = UA_TRUE, " + \
-                (".memberName = (char*)0, " if typeintrospection else "") + \
+                (".memberName = \"namespaceIndex\", " if typeintrospection else "") + \
                 ".padding = 0, .isArray = UA_FALSE }," + \
                 "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
-                (".memberName = (char*)0, " if typeintrospection else "") + \
-                ".padding = offsetof(UA_QualifiedName, name) - sizeof(UA_UInt16), .isArray = UA_FALSE }},\n" + \
+                (".memberName = \"name\", " if typeintrospection else "") + \
+                ".padding = offsetof(UA_QualifiedName, name)-sizeof(UA_UInt16), .isArray = UA_FALSE }},\n" + \
                 ".typeIndex = UA_TYPES_QUALIFIEDNAME }"
-                
+
+        if self.name == "UA_LocalizedText":
+            return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
+                ".memSize = sizeof(UA_LocalizedText), " + \
+                ".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
+                ".membersSize = 2, .members = {" + \
+                "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
+                (".memberName = \"locale\", " if typeintrospection else "") + \
+                ".padding = 0, .isArray = UA_FALSE }," + \
+                "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
+                (".memberName = \"text\", " if typeintrospection else "") + \
+                ".padding = offsetof(UA_LocalizedText, text)-sizeof(UA_String), .isArray = UA_FALSE }},\n" + \
+                ".typeIndex = UA_TYPES_LOCALIZEDTEXT }"
+
         return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
             ".memSize = sizeof(" + self.name + "), " + \
             ".namespaceZero = UA_TRUE, " + \
             ".fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
             ", .zeroCopyable = " + ("UA_TRUE" if self.zero_copy() else "UA_FALSE") + \
-            ", .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_" + self.name[3:].upper() + "," + \
-            (".memberName = (char*)0, " if typeintrospection else "") + \
-            ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, " + \
-            ".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
+            ", .membersSize = 0, .typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
 
 class EnumerationType(object):
     def __init__(self, name, description = "", elements = OrderedDict()):

+ 44 - 34
tools/pyUANamespace/open62541_MacroHelper.py

@@ -80,48 +80,56 @@ class open62541_MacroHelper():
     #code.append("addOneWayReferenceWithSession(server, (UA_Session *) UA_NULL, &" + refid + ");")
 
     if reference.isForward():
-      code.append("UA_Server_addMonodirectionalReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", UA_TRUE);")
+      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", UA_TRUE);")
     else:
-      code.append("UA_Server_addMonodirectionalReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", UA_FALSE);")
-
+      code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", UA_FALSE);")
     return code
                                
   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, ")
+      nodetype = "Object"
     elif node.nodeClass() == NODE_CLASS_VARIABLE:
-      code.append("UA_Server_addVariableNode(server,")
+      nodetype = "Variable"
     elif node.nodeClass() == NODE_CLASS_METHOD:
-      code.append("#ifdef ENABLE_METHODCALL")
-      code.append("UA_Server_addMethodNode(server,")
+      nodetype = "Method"
     elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
-      code.append("UA_Server_addObjectTypeNode(server,")
+      nodetype = "ObjectType"
     elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
-      code.append("UA_Server_addReferenceTypeNode(server,")
+      nodetype = "ReferenceType"
     elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
-      code.append("UA_Server_addVariableTypeNode(server,")
+      nodetype = "VariableType"
     elif node.nodeClass() == NODE_CLASS_DATATYPE:
-      code.append("UA_Server_addDataTypeNode(server,")
+      nodetype = "DataType"
     elif node.nodeClass() == NODE_CLASS_VIEW:
-      code.append("UA_Server_addViewNode(server,")
-    elif node.nodeClass() == NODE_CLASS_METHODTYPE:
-      code.append("UA_Server_addMethodTypeNode(server,")
+      nodetype = "View"
     else:
-      return []
+      code.append("/* undefined nodeclass */")
+      return code;
+
+    code.append("UA_%sAttributes attr;" % nodetype)
+    code.append("UA_%sAttributes_init(&attr);" %  nodetype); 
+
+    code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
+    code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
     
-    code.append("       " + str(self.getCreateNodeIDMacro(node)) + ",") # NodeId
+    code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
+    if nodetype in ["Object", "Variable"]:
+      code.append("UA_NodeId typeDefinition = UA_NODEID_NULL;") # todo instantiation of object and variable types
+    code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
+    code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
     extrNs = node.browseName().split(":")
     if len(extrNs) > 1:
-      code.append("       UA_QUALIFIEDNAME(" +  str(extrNs[0]) + ", \"" + extrNs[1] + "\"),")  # browseName
+      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(" +  str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
     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(node.writeMask()) + ", " + str(node.userWriteMask()) + ",") # write/userWriteMask
-    code.append("       " + str(self.getCreateNodeIDMacro(parentNode)) + ",") # ParentNode
-    code.append("       " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ",") # ReferenceTypeId
+      code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"" + str(node.browseName()) + "\");")
+
+    code.append("UA_Server_add%sNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName" % nodetype)
+    if nodetype in ["Object", "Variable"]:
+      code.append("       , typeDefinition")
+    code.append("       , attr, NULL);")
       
     return code
     
@@ -130,27 +138,29 @@ class open62541_MacroHelper():
     code = []
 
     code.append("// Node: " + str(node) + ", " + str(node.browseName()))
+    code.append("/* sorry, nodebootstrap needs to be updated */")
 
     if node.nodeClass() == NODE_CLASS_OBJECT:
-      nodetype = "UA_ObjectNode"
+      nodetype = "Object"
     elif node.nodeClass() == NODE_CLASS_VARIABLE:
-      nodetype = "UA_VariableNode"
+      nodetype = "Variable"
     elif node.nodeClass() == NODE_CLASS_METHOD:
-      nodetype = "UA_MethodNode"
+      nodetype = "Method"
     elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
-      nodetype = "UA_ObjectTypeNode"
+      nodetype = "ObjectType"
     elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
-      nodetype = "UA_ReferenceTypeNode"
+      nodetype = "ReferenceType"
     elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
-      nodetype = "UA_VariableTypeNode"
+      nodetype = "VariableType"
     elif node.nodeClass() == NODE_CLASS_DATATYPE:
-      nodetype = "UA_DataTypeNode"
+      nodetype = "DataType"
     elif node.nodeClass() == NODE_CLASS_VIEW:
-      nodetype = "UA_ViewNode"
-    elif node.nodeClass() == NODE_CLASS_METHODTYPE:
-      nodetype = "UA_MethodTypeNode"
+      nodetype = "View"
     else:
-      nodetype = "UA_NodeTypeNotFoundorGeneric"
+      code.append("/* undefined nodeclass */")
+      return;
+
+    code.append("UA_%sAttributes attr;\nUA_%sAttributes_init(&attr);" % (nodetype, nodetype)); 
 
     code.append(nodetype + " *" + node.getCodePrintableID() + " = " + nodetype + "_new();")
     if not "browsename" in self.supressGenerationOfAttribute:

+ 3 - 3
tools/pyUANamespace/ua_node_types.py

@@ -667,10 +667,10 @@ class opcua_node_t:
     (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 + self.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
       code = code + codegen.getCreateNodeNoBootstrap(self, parentNode, parentRef)
-      code = code + self.printOpen62541CCode_Subtype(unPrintedReferences = unPrintedReferences, bootstrapping = False)
-      code.append("       UA_NULL);") # createdNodeId, wraps up the UA_Server_add<XYType>Node() call
+      #code = code + self.printOpen62541CCode_Subtype(unPrintedReferences = unPrintedReferences, bootstrapping = False)
+      #code.append("       UA_NULL);") # createdNodeId, wraps up the UA_Server_add<XYType>Node() call
       if self.nodeClass() == NODE_CLASS_METHOD:
         code.append("#endif //ENABLE_METHODCALL") # ifdef added by codegen when methods are detected
       # Parent to child reference is added by the server, do not reprint that reference

+ 1 - 1
tools/travis_linux_script.sh

@@ -63,7 +63,7 @@ make
 cd .. && rm build -rf && mkdir -p build && cd build
 echo "Debug build and unit tests (64 bit)"
 cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_DEMO_NODESET=ON -DBUILD_UNIT_TESTS=ON -DBUILD_EXAMPLESERVER=ON -DENABLE_COVERAGE=ON ..
-make && make test
+make && make test ARGS="-V"
 echo "Run valgrind to see if the server leaks memory (just starting up and closing..)"
 if [[ ! ( ${TRAVIS_OS_NAME} == "linux" && ${CC} == "clang") ]]; then
   (valgrind --error-exitcode=3 ./server & export pid=$!; sleep 2; kill -INT $pid; wait $pid);