Kaynağa Gözat

compiles with datavalue in node

Julius Pfrommer 8 yıl önce
ebeveyn
işleme
bd57e8867a

+ 61 - 37
src/server/ua_nodes.c

@@ -7,7 +7,8 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     UA_QualifiedName_deleteMembers(&node->browseName);
     UA_LocalizedText_deleteMembers(&node->displayName);
     UA_LocalizedText_deleteMembers(&node->description);
-    UA_Array_delete(node->references, node->referencesSize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
+    UA_Array_delete(node->references, node->referencesSize,
+                    &UA_TYPES[UA_TYPES_REFERENCENODE]);
     node->references = NULL;
     node->referencesSize = 0;
 
@@ -22,9 +23,13 @@ void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
     case UA_NODECLASS_VARIABLE:
     case UA_NODECLASS_VARIABLETYPE: {
         UA_VariableNode *p = (UA_VariableNode*)node;
-        if(p->valueSource == UA_VALUESOURCE_CONTAINED &&
-           p->value.contained.value.hasValue)
-            UA_Variant_deleteMembers(&p->value.contained.value.value);
+        UA_NodeId_deleteMembers(&p->dataType);
+        UA_Array_delete(p->arrayDimensions, p->arrayDimensionsSize,
+                        &UA_TYPES[UA_TYPES_INT32]);
+        p->arrayDimensions = NULL;
+        p->arrayDimensionsSize = 0;
+        if(p->valueSource == UA_VALUESOURCE_DATA)
+            UA_DataValue_deleteMembers(&p->value.data.value);
         break;
     }
     case UA_NODECLASS_REFERENCETYPE: {
@@ -49,21 +54,43 @@ UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
 }
 
 static UA_StatusCode
-UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+UA_CommonVariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+    UA_StatusCode retval = UA_Array_copy(src->arrayDimensions,
+                                         src->arrayDimensionsSize,
+                                         (void**)&dst->arrayDimensions,
+                                         &UA_TYPES[UA_TYPES_INT32]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    dst->arrayDimensionsSize = src->arrayDimensionsSize;
+    retval = UA_NodeId_copy(&src->dataType, &dst->dataType);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_CONTAINED) {
-        UA_StatusCode retval = UA_DataValue_copy(&src->value.contained.value, &dst->value.contained.value);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        dst->value.variant.callback = src->value.variant.callback;
+    if(src->valueSource == UA_VALUESOURCE_DATA) {
+        retval |= UA_DataValue_copy(&src->value.data.value,
+                                    &dst->value.data.value);
+        dst->value.data.callback = src->value.data.callback;
     } else
         dst->value.dataSource = src->value.dataSource;
+    return retval;
+}
+
+static UA_StatusCode
+UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
+    UA_StatusCode retval = UA_CommonVariableNode_copy(src, dst);
     dst->accessLevel = src->accessLevel;
-    dst->userAccessLevel = src->accessLevel;
+    dst->userAccessLevel = src->userAccessLevel;
     dst->minimumSamplingInterval = src->minimumSamplingInterval;
     dst->historizing = src->historizing;
-    return UA_STATUSCODE_GOOD;
+    return retval;
+}
+
+static UA_StatusCode
+UA_VariableTypeNode_copy(const UA_VariableTypeNode *src,
+                         UA_VariableTypeNode *dst) {
+    UA_StatusCode retval = UA_CommonVariableNode_copy((const UA_VariableNode*)src,
+                                                      (UA_VariableNode*)dst);
+    dst->isAbstract = src->isAbstract;
+    return retval;
 }
 
 static UA_StatusCode
@@ -83,23 +110,10 @@ UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
 }
 
 static UA_StatusCode
-UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
-    dst->valueRank = src->valueRank;
-    dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_CONTAINED) {
-        UA_StatusCode retval = UA_DataValue_copy(&src->value.contained.value, &dst->value.contained.value);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        dst->value.variant.callback = src->value.variant.callback;
-    } else
-        dst->value.dataSource = src->value.dataSource;
-    dst->isAbstract = src->isAbstract;
-    return UA_STATUSCODE_GOOD;
-}
-
-static UA_StatusCode
-UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
-    UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
+UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src,
+                          UA_ReferenceTypeNode *dst) {
+    UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName,
+                                                 &dst->inverseName);
     dst->isAbstract = src->isAbstract;
     dst->symmetric = src->symmetric;
     return retval;
@@ -134,7 +148,8 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
         UA_Node_deleteMembersAnyNodeClass(dst);
         return retval;
     }
-    retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
+    retval |= UA_Array_copy(src->references, src->referencesSize,
+                            (void**)&dst->references,
                             &UA_TYPES[UA_TYPES_REFERENCENODE]);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_Node_deleteMembersAnyNodeClass(dst);
@@ -145,25 +160,32 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     /* copy unique content of the nodeclass */
     switch(src->nodeClass) {
     case UA_NODECLASS_OBJECT:
-        retval = UA_ObjectNode_copy((const UA_ObjectNode*)src, (UA_ObjectNode*)dst);
+        retval = UA_ObjectNode_copy((const UA_ObjectNode*)src,
+                                    (UA_ObjectNode*)dst);
         break;
     case UA_NODECLASS_VARIABLE:
-        retval = UA_VariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst);
+        retval = UA_VariableNode_copy((const UA_VariableNode*)src,
+                                      (UA_VariableNode*)dst);
         break;
     case UA_NODECLASS_METHOD:
-        retval = UA_MethodNode_copy((const UA_MethodNode*)src, (UA_MethodNode*)dst);
+        retval = UA_MethodNode_copy((const UA_MethodNode*)src,
+                                    (UA_MethodNode*)dst);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src, (UA_ObjectTypeNode*)dst);
+        retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src,
+                                        (UA_ObjectTypeNode*)dst);
         break;
     case UA_NODECLASS_VARIABLETYPE:
-        retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src, (UA_VariableTypeNode*)dst);
+        retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src,
+                                          (UA_VariableTypeNode*)dst);
         break;
     case UA_NODECLASS_REFERENCETYPE:
-        retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src, (UA_ReferenceTypeNode*)dst);
+        retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src,
+                                           (UA_ReferenceTypeNode*)dst);
         break;
     case UA_NODECLASS_DATATYPE:
-        retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src, (UA_DataTypeNode*)dst);
+        retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src,
+                                      (UA_DataTypeNode*)dst);
         break;
     case UA_NODECLASS_VIEW:
         retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst);
@@ -171,7 +193,9 @@ UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
     default:
         break;
     }
+
     if(retval != UA_STATUSCODE_GOOD)
         UA_Node_deleteMembersAnyNodeClass(dst);
+
     return retval;
 }

+ 32 - 37
src/server/ua_nodes.h

@@ -2,14 +2,6 @@
 #define UA_NODES_H_
 
 #include "ua_server.h"
-#include "ua_types_generated.h"
-#include "ua_types_encoding_binary.h"
-
-/*
- * Most APIs take and return UA_EditNode and UA_ConstNode. By looking up the
- * nodeclass, nodes can be cast to their "true" class, i.e. UA_VariableNode,
- * UA_ObjectNode, and so on.
- */
 
 #define UA_STANDARD_NODEMEMBERS                 \
     UA_NodeId nodeId;                           \
@@ -22,6 +14,36 @@
     size_t referencesSize;                      \
     UA_ReferenceNode *references;
 
+typedef enum {
+    UA_VALUESOURCE_DATA,
+    UA_VALUESOURCE_DATASOURCE
+} UA_ValueSource;
+
+#define UA_VARIABLE_NODEMEMBERS                                         \
+    /* Constraints on possible values */                                \
+    UA_NodeId dataType;                                                 \
+    UA_Int32 valueRank; /* n >= 1: the value is an array with the specified number of dimensions. */       \
+                        /* n =  0: the value is an array with one or more dimensions. */                   \
+                        /* n = -1: the value is a scalar. */                                               \
+                        /* n = -2: the value can be a scalar or an array with any number of dimensions. */ \
+                        /* n = -3:  the value can be a scalar or a one dimensional array. */               \
+    size_t arrayDimensionsSize;                                         \
+    UA_Int32 *arrayDimensions;                                          \
+                                                                        \
+    /* The current value */                                             \
+    UA_ValueSource valueSource;                                         \
+    union {                                                             \
+        struct {                                                        \
+            UA_DataValue value;                                         \
+            UA_ValueCallback callback;                                  \
+        } data;                                                         \
+        UA_DataSource dataSource;                                       \
+    } value;
+
+/****************/
+/* Generic Node */
+/****************/
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
 } UA_Node;
@@ -49,31 +71,13 @@ typedef struct {
     UA_ObjectLifecycleManagement lifecycleManagement;
 } UA_ObjectTypeNode;
 
-typedef enum {
-    UA_VALUESOURCE_CONTAINED,
-    UA_VALUESOURCE_DATASOURCE
-} UA_ValueSource;
-
 /****************/
 /* VariableNode */
 /****************/
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
-    UA_Int32 valueRank; /**< n >= 1: the value is an array with the specified number of dimensions.
-                             n = 0: the value is an array with one or more dimensions.
-                             n = -1: the value is a scalar.
-                             n = -2: the value can be a scalar or an array with any number of dimensions.
-                             n = -3:  the value can be a scalar or a one dimensional array. */
-    UA_ValueSource valueSource;
-    union {
-        struct {
-            UA_DataValue value;
-            UA_ValueCallback callback;
-        } contained;
-        UA_DataSource dataSource;
-    } value;
-    /* <--- similar to variabletypenodes up to there--->*/
+    UA_VARIABLE_NODEMEMBERS
     UA_Byte accessLevel;
     UA_Byte userAccessLevel;
     UA_Double minimumSamplingInterval;
@@ -86,16 +90,7 @@ typedef struct {
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
-    UA_Int32 valueRank;
-    UA_ValueSource valueSource;
-    union {
-        struct {
-            UA_DataValue value;
-            UA_ValueCallback callback;
-        } contained;
-        UA_DataSource dataSource;
-    } value;
-    /* <--- similar to variablenodes up to there--->*/
+    UA_VARIABLE_NODEMEMBERS
     UA_Boolean isAbstract;
 } UA_VariableTypeNode;
 

+ 84 - 77
src/server/ua_server.c

@@ -326,7 +326,8 @@ readCurrentTime(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp
         return UA_STATUSCODE_GOOD;
     }
     UA_DateTime currentTime = UA_DateTime_now();
-    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &currentTime, &UA_TYPES[UA_TYPES_DATETIME]);
+    UA_StatusCode retval = UA_Variant_setScalarCopy(&value->value, &currentTime,
+                                                    &UA_TYPES[UA_TYPES_DATETIME]);
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     value->hasValue = true;
@@ -348,7 +349,8 @@ addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_UInt32 p
     UA_DataTypeNode *datatype = UA_NodeStore_newDataTypeNode();
     copyNames((UA_Node*)datatype, name);
     datatype->nodeId.identifier.numeric = datatypeid;
-    addNodeInternal(server, (UA_Node*)datatype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
+    addNodeInternal(server, (UA_Node*)datatype,
+                    UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
 }
 
 static void
@@ -363,34 +365,18 @@ addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttypeid,
 
 static UA_VariableTypeNode*
 createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                       UA_UInt32 parent, UA_Boolean abstract) {
+                       UA_Boolean abstract) {
     UA_VariableTypeNode *variabletype = UA_NodeStore_newVariableTypeNode();
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
     variabletype->isAbstract = abstract;
-    variabletype->value.variant.value.type = &UA_TYPES[UA_TYPES_VARIANT];
     return variabletype;
 }
 
-static void
-addVariableTypeNode_organized(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                              UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype = createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-    addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
-}
-
-static void
-addVariableTypeNode_subtype(UA_Server *server, char* name, UA_UInt32 variabletypeid,
-                            UA_UInt32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype =
-        createVariableTypeNode(server, name, variabletypeid, parent, abstract);
-    addNodeInternal(server, (UA_Node*)variabletype, UA_NODEID_NUMERIC(0, parent), nodeIdHasSubType);
-}
-
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
 static UA_StatusCode
 GetMonitoredItems(void *handle, const UA_NodeId objectId, size_t inputSize,
-                          const UA_Variant *input, size_t outputSize, UA_Variant *output) {
+                  const UA_Variant *input, size_t outputSize, UA_Variant *output) {
     UA_UInt32 subscriptionId = *((UA_UInt32*)(input[0].data));
     UA_Session* session = methodCallSession;
     UA_Subscription* subscription = UA_Session_getSubscriptionByID(session, subscriptionId);
@@ -821,12 +807,28 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
     addNodeInternalWithType(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
                             nodeIdOrganizes, nodeIdFolderType);
-    addVariableTypeNode_organized(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE,
-                                  UA_NS0ID_VARIABLETYPESFOLDER, true);
-    addVariableTypeNode_subtype(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE,
-                                UA_NS0ID_BASEVARIABLETYPE, false);
-    addVariableTypeNode_subtype(server, "PropertyType", UA_NS0ID_PROPERTYTYPE,
-                                UA_NS0ID_BASEVARIABLETYPE, false);
+
+
+    UA_VariableTypeNode *basevartype =
+        createVariableTypeNode(server, "BaseVariableType", UA_NS0ID_BASEVARIABLETYPE, true);
+    basevartype->valueRank = -2;
+    basevartype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    addNodeInternal(server, (UA_Node*)basevartype,
+                    UA_NODEID_NUMERIC(0, UA_NS0ID_VARIABLETYPESFOLDER), nodeIdOrganizes);
+
+    UA_VariableTypeNode *basedatavartype =
+        createVariableTypeNode(server, "BaseDataVariableType", UA_NS0ID_BASEDATAVARIABLETYPE, false);
+    basedatavartype->valueRank = -2;
+    basedatavartype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    addNodeInternal(server, (UA_Node*)basedatavartype,
+                    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), nodeIdHasSubType);
+
+    UA_VariableTypeNode *propertytype =
+        createVariableTypeNode(server, "PropertyType", UA_NS0ID_PROPERTYTYPE, false);
+    propertytype->valueRank = -2;
+    propertytype->dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    addNodeInternal(server, (UA_Node*)propertytype,
+                    UA_NODEID_NUMERIC(0, UA_NS0ID_BASEVARIABLETYPE), nodeIdHasSubType);
 
     /***************/
     /* Event Types */
@@ -869,9 +871,10 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *serverArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverArray, "ServerArray");
     serverArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERARRAY;
-    UA_Variant_setArrayCopy(&serverArray->value.variant.value,
+    UA_Variant_setArrayCopy(&serverArray->value.data.value.value,
                             &server->config.applicationDescription.applicationUri, 1,
                             &UA_TYPES[UA_TYPES_STRING]);
+    serverArray->value.data.value.hasValue = true;
     serverArray->valueRank = 1;
     serverArray->minimumSamplingInterval = 1.0;
     addNodeInternal(server, (UA_Node*)serverArray, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
@@ -889,10 +892,10 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *localeIdArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
     localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
-    localeIdArray->value.variant.value.data = UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
-    localeIdArray->value.variant.value.arrayLength = 1;
-    localeIdArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
-    *(UA_String *)localeIdArray->value.variant.value.data = UA_STRING_ALLOC("en");
+    UA_String enLocale = UA_STRING("en");
+    UA_Variant_setArrayCopy(&localeIdArray->value.data.value.value,
+                            &enLocale, 1, &UA_TYPES[UA_TYPES_STRING]);
+    localeIdArray->value.data.value.hasValue = true;
     localeIdArray->valueRank = 1;
     localeIdArray->minimumSamplingInterval = 1.0;
     addNodeInternal(server, (UA_Node*)localeIdArray,
@@ -904,9 +907,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
     maxBrowseContinuationPoints->nodeId.identifier.numeric =
         UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.value.data) = MAXCONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
+    UA_Variant_setScalar(&maxBrowseContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
+    maxBrowseContinuationPoints->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)maxBrowseContinuationPoints,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS),
@@ -932,11 +935,12 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *serverProfileArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverProfileArray, "ServerProfileArray");
     serverProfileArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY;
-    serverProfileArray->value.variant.value.arrayLength = profileArraySize;
-    serverProfileArray->value.variant.value.data = UA_Array_new(profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
-    serverProfileArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
+    UA_Variant_setArray(&serverProfileArray->value.data.value.value,
+                        UA_Array_new(profileArraySize, &UA_TYPES[UA_TYPES_STRING]),
+                        profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
     for(UA_UInt16 i=0;i<profileArraySize;i++)
-        ((UA_String *)serverProfileArray->value.variant.value.data)[i] = profileArray[i];
+        ((UA_String *)serverProfileArray->value.data.value.value.data)[i] = profileArray[i];
+    serverProfileArray->value.data.value.hasValue = true;
     serverProfileArray->valueRank = 1;
     serverProfileArray->minimumSamplingInterval = 1.0;
     addNodeInternal(server, (UA_Node*)serverProfileArray,
@@ -947,7 +951,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *softwareCertificates = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)softwareCertificates, "SoftwareCertificates");
     softwareCertificates->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SOFTWARECERTIFICATES;
-    softwareCertificates->value.variant.value.type = &UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE];
+    softwareCertificates->dataType = UA_TYPES[UA_TYPES_SIGNEDSOFTWARECERTIFICATE].typeId;
     addNodeInternal(server, (UA_Node*)softwareCertificates,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_SOFTWARECERTIFICATES),
@@ -956,10 +960,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *maxQueryContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxQueryContinuationPoints, "MaxQueryContinuationPoints");
     maxQueryContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS;
-    maxQueryContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
-    maxQueryContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    //FIXME
-    *((UA_UInt16*)maxQueryContinuationPoints->value.variant.value.data) = 0;
+    UA_Variant_setScalar(&maxQueryContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
+    maxQueryContinuationPoints->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)maxQueryContinuationPoints,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXQUERYCONTINUATIONPOINTS),
@@ -968,10 +971,8 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *maxHistoryContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxHistoryContinuationPoints, "MaxHistoryContinuationPoints");
     maxHistoryContinuationPoints->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS;
-    maxHistoryContinuationPoints->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT16];
-    maxHistoryContinuationPoints->value.variant.value.data = UA_UInt16_new();
-    //FIXME
-    *((UA_UInt16*)maxHistoryContinuationPoints->value.variant.value.data) = 0;
+    UA_Variant_setScalar(&maxHistoryContinuationPoints->value.data.value.value,
+                         UA_UInt16_new(), &UA_TYPES[UA_TYPES_UINT16]);
     addNodeInternal(server, (UA_Node*)maxHistoryContinuationPoints,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXHISTORYCONTINUATIONPOINTS),
@@ -980,10 +981,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *minSupportedSampleRate = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)minSupportedSampleRate, "MinSupportedSampleRate");
     minSupportedSampleRate->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE;
-    minSupportedSampleRate->value.variant.value.type = &UA_TYPES[UA_TYPES_DOUBLE];
-    minSupportedSampleRate->value.variant.value.data = UA_Double_new();
-    //FIXME
-    *((UA_Double*)minSupportedSampleRate->value.variant.value.data) = 0.0;
+    UA_Variant_setScalar(&minSupportedSampleRate->value.data.value.value,
+                         UA_Double_new(), &UA_TYPES[UA_TYPES_DOUBLE]);
+    minSupportedSampleRate->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)minSupportedSampleRate,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_MINSUPPORTEDSAMPLERATE),
@@ -1016,8 +1016,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *enabledFlag = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
-    enabledFlag->value.variant.value.data = UA_Boolean_new(); //initialized as false
-    enabledFlag->value.variant.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+    UA_Variant_setScalar(&enabledFlag->value.data.value.value, UA_Boolean_new(),
+                         &UA_TYPES[UA_TYPES_BOOLEAN]);
+    enabledFlag->value.data.value.hasValue = true;
     enabledFlag->valueRank = 1;
     enabledFlag->minimumSamplingInterval = 1.0;
     addNodeInternal(server, (UA_Node*)enabledFlag,
@@ -1037,9 +1038,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *starttime = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)starttime, "StartTime");
     starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
-    starttime->value.variant.value.storageType = UA_VARIANT_DATA_NODELETE;
-    starttime->value.variant.value.data = &server->startTime;
-    starttime->value.variant.value.type = &UA_TYPES[UA_TYPES_DATETIME];
+    UA_Variant_setScalarCopy(&starttime->value.data.value.value,
+                             &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
+    starttime->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)starttime, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                     nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
@@ -1057,13 +1058,11 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, true);
 
     UA_VariableNode *state = UA_NodeStore_newVariableNode();
-    UA_ServerState *stateEnum = UA_ServerState_new();
-    *stateEnum = UA_SERVERSTATE_RUNNING;
     copyNames((UA_Node*)state, "State");
     state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-    state->value.variant.value.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-    state->value.variant.value.arrayLength = 0;
-    state->value.variant.value.data = stateEnum; // points into the other object.
+    UA_Variant_setScalar(&state->value.data.value.value, UA_ServerState_new(),
+                         &UA_TYPES[UA_TYPES_SERVERSTATE]);
+    state->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)state, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                     nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
@@ -1072,9 +1071,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *buildinfo = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildinfo, "BuildInfo");
     buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
-    UA_Variant_setScalarCopy(&buildinfo->value.variant.value,
-                             &server->config.buildInfo,
-                             &UA_TYPES[UA_TYPES_BUILDINFO]);
+    UA_Variant_setScalarCopy(&buildinfo->value.data.value.value,
+                             &server->config.buildInfo, &UA_TYPES[UA_TYPES_BUILDINFO]);
+    buildinfo->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)buildinfo,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
@@ -1083,9 +1082,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *producturi = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)producturi, "ProductUri");
     producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
-    UA_Variant_setScalarCopy(&producturi->value.variant.value, &server->config.buildInfo.productUri,
+    UA_Variant_setScalarCopy(&producturi->value.data.value.value, &server->config.buildInfo.productUri,
                              &UA_TYPES[UA_TYPES_STRING]);
-    producturi->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
+    producturi->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)producturi,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI),
@@ -1094,9 +1093,10 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *manufacturername = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)manufacturername, "ManufacturerName");
     manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
-    UA_Variant_setScalarCopy(&manufacturername->value.variant.value,
+    UA_Variant_setScalarCopy(&manufacturername->value.data.value.value,
                              &server->config.buildInfo.manufacturerName,
                              &UA_TYPES[UA_TYPES_STRING]);
+    manufacturername->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)manufacturername,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME),
@@ -1105,8 +1105,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *productname = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)productname, "ProductName");
     productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
-    UA_Variant_setScalarCopy(&productname->value.variant.value, &server->config.buildInfo.productName,
+    UA_Variant_setScalarCopy(&productname->value.data.value.value, &server->config.buildInfo.productName,
                              &UA_TYPES[UA_TYPES_STRING]);
+    productname->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)productname,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME),
@@ -1115,8 +1116,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *softwareversion = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)softwareversion, "SoftwareVersion");
     softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
-    UA_Variant_setScalarCopy(&softwareversion->value.variant.value, &server->config.buildInfo.softwareVersion,
+    UA_Variant_setScalarCopy(&softwareversion->value.data.value.value, &server->config.buildInfo.softwareVersion,
                              &UA_TYPES[UA_TYPES_STRING]);
+    softwareversion->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)softwareversion,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION),
@@ -1125,8 +1127,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *buildnumber = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildnumber, "BuildNumber");
     buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
-    UA_Variant_setScalarCopy(&buildnumber->value.variant.value, &server->config.buildInfo.buildNumber,
+    UA_Variant_setScalarCopy(&buildnumber->value.data.value.value, &server->config.buildInfo.buildNumber,
                              &UA_TYPES[UA_TYPES_STRING]);
+    buildnumber->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)buildnumber,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
@@ -1135,8 +1138,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *builddate = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)builddate, "BuildDate");
     builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
-    UA_Variant_setScalarCopy(&builddate->value.variant.value, &server->config.buildInfo.buildDate,
+    UA_Variant_setScalarCopy(&builddate->value.data.value.value, &server->config.buildInfo.buildDate,
                              &UA_TYPES[UA_TYPES_DATETIME]);
+    builddate->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)builddate,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
@@ -1145,8 +1149,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *secondstillshutdown = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
     secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
-    secondstillshutdown->value.variant.value.data = UA_UInt32_new();
-    secondstillshutdown->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT32];
+    UA_Variant_setScalar(&secondstillshutdown->value.data.value.value, UA_UInt32_new(),
+                         &UA_TYPES[UA_TYPES_UINT32]);
+    secondstillshutdown->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)secondstillshutdown,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN),
@@ -1155,8 +1160,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     UA_VariableNode *shutdownreason = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)shutdownreason, "ShutdownReason");
     shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
-    shutdownreason->value.variant.value.data = UA_LocalizedText_new();
-    shutdownreason->value.variant.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+    UA_Variant_setScalar(&shutdownreason->value.data.value.value, UA_LocalizedText_new(),
+                         &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
+    shutdownreason->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)shutdownreason,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON),
@@ -1207,8 +1213,9 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     copyNames((UA_Node*)redundancySupport, "RedundancySupport");
     redundancySupport->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT);
     //FIXME: enum is needed for type letting it uninitialized for now
-    redundancySupport->value.variant.value.data = UA_Int32_new();
-    redundancySupport->value.variant.value.type = &UA_TYPES[UA_TYPES_INT32];
+    UA_Variant_setScalar(&redundancySupport->value.data.value.value, UA_Int32_new(),
+                         &UA_TYPES[UA_TYPES_INT32]);
+    redundancySupport->value.data.value.hasValue = true;
     addNodeInternal(server, (UA_Node*)redundancySupport, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY),
             nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERREDUNDANCY_REDUNDANCYSUPPORT), nodeIdHasTypeDefinition,

+ 8 - 0
src/server/ua_server_internal.h

@@ -103,4 +103,12 @@ isNodeInTree(UA_NodeStore *ns, const UA_NodeId *rootNode, const UA_NodeId *nodeT
              const UA_NodeId *referenceTypeIds, size_t referenceTypeIdsSize,
              size_t maxDepth, UA_Boolean *found);
 
+/* find a datatype from the nodeid */
+const UA_DataType * findDataType(const UA_NodeId *typeId);
+
+/* Set the value attribute inside a node */
+UA_StatusCode
+CopyIntoValueAttribute(UA_Server *server, UA_VariableNode *node,
+                       const UA_DataValue *value, const UA_String *indexRange);
+
 #endif /* UA_SERVER_INTERNAL_H_ */

+ 162 - 146
src/server/ua_services_attribute.c

@@ -80,30 +80,6 @@ UA_StatusCode parse_numericrange(const UA_String *str, UA_NumericRange *range) {
     return retval;
 }
 
-#define CHECK_NODECLASS(CLASS)                                  \
-    if(!(node->nodeClass & (CLASS))) {                          \
-        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
-        break;                                                  \
-    }
-
-static void
-handleServerTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
-    if(v && (timestamps == UA_TIMESTAMPSTORETURN_SERVER ||
-             timestamps == UA_TIMESTAMPSTORETURN_BOTH)) {
-        v->hasServerTimestamp = true;
-        v->serverTimestamp = UA_DateTime_now();
-    }
-}
-
-static void
-handleSourceTimestamps(UA_TimestampsToReturn timestamps, UA_DataValue* v) {
-    if(timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
-       timestamps == UA_TIMESTAMPSTORETURN_BOTH) {
-        v->hasSourceTimestamp = true;
-        v->sourceTimestamp = UA_DateTime_now();
-    }
-}
-
 /* force cast for zero-copy reading. ensure that the variant is never written into. */
 static void forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *t) {
     UA_Variant_init(v);
@@ -127,17 +103,15 @@ getVariableNodeValue(UA_Server *server, UA_Session *session,
         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(vn->valueSource == UA_VALUESOURCE_DATA) {
+        if(vn->value.data.callback.onRead)
+            vn->value.data.callback.onRead(vn->value.data.callback.handle, vn->nodeId,
+                                           &v->value, rangeptr);
         if(!rangeptr) {
-            v->value = vn->value.variant.value;
+            *v = vn->value.data.value;
             v->value.storageType = UA_VARIANT_DATA_NODELETE;
         } else
-            retval = UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
-        if(retval == UA_STATUSCODE_GOOD)
-            handleSourceTimestamps(timestamps, v);
+            retval = UA_Variant_copyRange(&vn->value.data.value.value, &v->value, range);
     } else {
         if(!vn->value.dataSource.read) {
             UA_LOG_DEBUG_SESSION(server->config.logger, session,
@@ -157,74 +131,47 @@ getVariableNodeValue(UA_Server *server, UA_Session *session,
 }
 
 static UA_StatusCode
-getVariableNodeDataType(UA_Server *server, UA_Session *session,
-                        const UA_VariableNode *vn, UA_DataValue *v) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-        if(vn->value.variant.value.type) {
-            forceVariantSetScalar(&v->value, &vn->value.variant.value.type->typeId,
-                                  &UA_TYPES[UA_TYPES_NODEID]);
-        } else {
-            UA_NodeId nullid;
-            UA_NodeId_init(&nullid);
-            UA_Variant_setScalarCopy(&v->value, &nullid, &UA_TYPES[UA_TYPES_NODEID]);
-        }
-    } else {
-        /* Read from the datasource to see the data type */
-        if(!vn->value.dataSource.read) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "DataSource cannot be read in ReadRequest");
-            return UA_STATUSCODE_BADINTERNALERROR;
-        }
-        UA_DataValue val;
-        UA_DataValue_init(&val);
-        val.hasValue = false; // always assume we are not given a value by userspace
-        retval = vn->value.dataSource.read(vn->value.dataSource.handle,
-                                           vn->nodeId, false, NULL, &val);
-        if(retval != UA_STATUSCODE_GOOD)
-            return retval;
-        if(val.hasValue && val.value.type)
-          retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId,
-                                            &UA_TYPES[UA_TYPES_NODEID]);
-        UA_DataValue_deleteMembers(&val);
-    }
-    return retval;
+getVariableNodeArrayDimensions(const UA_VariableNode *vn, UA_DataValue *v) {
+    UA_Variant_setArray(&v->value, vn->arrayDimensions, vn->arrayDimensionsSize,
+                        &UA_TYPES[UA_TYPES_INT32]);
+    v->value.storageType = UA_VARIANT_DATA_NODELETE;
+    v->hasValue = true;
+    return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
-getVariableNodeArrayDimensions(UA_Server *server, UA_Session *session,
-                               const UA_VariableNode *vn, UA_DataValue *v) {
-    UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
-        UA_Variant_setArray(&v->value, vn->value.variant.value.arrayDimensions,
-                            vn->value.variant.value.arrayDimensionsSize,
-                            &UA_TYPES[UA_TYPES_INT32]);
-        v->value.storageType = UA_VARIANT_DATA_NODELETE;
-    } else {
-        if(!vn->value.dataSource.read) {
-            UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                 "DataSource cannot be read in ReadRequest");
-            return UA_STATUSCODE_BADINTERNALERROR;
-        }
-        /* 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, false, 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);
+getIsAbstractAttribute(const UA_Node *node, UA_Variant *v) {
+    const UA_Boolean *isAbstract;
+    switch(node->nodeClass) {
+    case UA_NODECLASS_REFERENCETYPE:
+        isAbstract = &((const UA_ReferenceTypeNode*)node)->isAbstract;
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        isAbstract = &((const UA_ObjectTypeNode*)node)->isAbstract;
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        isAbstract = &((const UA_VariableTypeNode*)node)->isAbstract;
+        break;
+    case UA_NODECLASS_DATATYPE:
+        isAbstract = &((const UA_DataTypeNode*)node)->isAbstract;
+        break;
+    default:
+        return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
     }
-    return retval;
+    forceVariantSetScalar(v, isAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
+    return UA_STATUSCODE_GOOD;
 }
 
 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"};
 
+#define CHECK_NODECLASS(CLASS)                                  \
+    if(!(node->nodeClass & (CLASS))) {                          \
+        retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;           \
+        break;                                                  \
+    }
+
 /* Reads a single attribute from a node in the nodestore */
 void Service_Read_single(UA_Server *server, UA_Session *session,
                          const UA_TimestampsToReturn timestamps,
@@ -279,10 +226,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         forceVariantSetScalar(&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);
-        forceVariantSetScalar(&v->value, &((const UA_ReferenceTypeNode*)node)->isAbstract,
-                              &UA_TYPES[UA_TYPES_BOOLEAN]);
+        retval = getIsAbstractAttribute(node, &v->value);
         break;
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS(UA_NODECLASS_REFERENCETYPE);
@@ -310,7 +254,8 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_DATATYPE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        retval = getVariableNodeDataType(server, session, (const UA_VariableNode*)node, v);
+        forceVariantSetScalar(&v->value, &((const UA_VariableTypeNode*)node)->dataType,
+                              &UA_TYPES[UA_TYPES_NODEID]);
         break;
     case UA_ATTRIBUTEID_VALUERANK:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
@@ -319,7 +264,7 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        retval = getVariableNodeArrayDimensions(server, session, (const UA_VariableNode*)node, v);
+        retval = getVariableNodeArrayDimensions((const UA_VariableNode*)node, v);
         break;
     case UA_ATTRIBUTEID_ACCESSLEVEL:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE);
@@ -361,9 +306,6 @@ void Service_Read_single(UA_Server *server, UA_Session *session,
         v->hasStatus = 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,
@@ -601,6 +543,15 @@ enum type_equivalence {
     TYPE_EQUIVALENCE_OPAQUE
 };
 
+const UA_DataType *
+findDataType(const UA_NodeId *typeId) {
+    for(size_t i = 0; i < UA_TYPES_COUNT; i++) {
+        if (UA_TYPES[i].typeId.identifier.numeric == typeId->identifier.numeric)
+            return &UA_TYPES[i];
+    }
+    return NULL;
+}
+
 static enum type_equivalence typeEquivalence(const UA_DataType *t) {
     if(t->membersSize != 1 || !t->members[0].namespaceZero)
         return TYPE_EQUIVALENCE_NONE;
@@ -611,71 +562,137 @@ static enum type_equivalence typeEquivalence(const UA_DataType *t) {
     return TYPE_EQUIVALENCE_NONE;
 }
 
-static UA_StatusCode
-CopyIntoValueAttribute(UA_VariableNode *node, const 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);
-
+UA_StatusCode
+CopyIntoValueAttribute(UA_Server *server, UA_VariableNode *node,
+                       const UA_DataValue *value, const UA_String *indexRange) {
     /* Parse the range */
     UA_NumericRange range;
     UA_NumericRange *rangeptr = NULL;
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
-    if(wvalue->indexRange.length > 0) {
-        retval = parse_numericrange(&wvalue->indexRange, &range);
+    if(indexRange && indexRange->length > 0) {
+        if(!value->hasValue || !node->value.data.value.hasValue)
+            return UA_STATUSCODE_BADINDEXRANGENODATA;
+        retval = parse_numericrange(indexRange, &range);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
         rangeptr = &range;
     }
 
-    /* The nodeid on the wire may be != the nodeid in the node: opaque types,
-       enums and bytestrings. newV contains the correct type definition after
-       the following paragraph */
-    UA_Variant *oldV = &node->value.variant.value;
-    const UA_Variant *newV = &wvalue->value.value;
-    UA_Variant cast_v;
-    if(oldV->type && !UA_NodeId_equal(&oldV->type->typeId, &newV->type->typeId)) {
-        cast_v = wvalue->value.value;
-        newV = &cast_v;
-        enum type_equivalence te1 = typeEquivalence(oldV->type);
-        enum type_equivalence te2 = typeEquivalence(newV->type);
+    if(!value->hasValue)
+        goto write_value;
+
+    /* See if the types match. The nodeid on the wire may be != the nodeid in
+     * the node for opaque types, enums and bytestrings. value contains the
+     * correct type definition after the following paragraph */
+    UA_DataValue cast_v;
+    if(!UA_NodeId_isNull(&node->dataType) &&
+       !UA_NodeId_equal(&node->dataType, &value->value.type->typeId)) {
+        cast_v = *value;
+        value = &cast_v;
+        const UA_DataType *nodeDataType = findDataType(&node->dataType);
+
+        /* is this a subtype? */
+        UA_NodeId subtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
+        UA_Boolean found = false;
+        retval = isNodeInTree(server->nodestore, &value->value.type->typeId,
+                              &node->dataType, &subtypeId, 1, 10, &found);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+        if(found)
+            goto check_array;
+
+        /* a string is written to a byte array */
+        if(nodeDataType == &UA_TYPES[UA_TYPES_BYTE] &&
+           value->value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
+           (!node->value.data.value.hasValue ||
+            !UA_Variant_isScalar(&node->value.data.value.value)) &&
+           UA_Variant_isScalar(&value->value)) {
+            UA_ByteString *str = (UA_ByteString*)value->value.data;
+            cast_v.value.type = &UA_TYPES[UA_TYPES_BYTE];
+            cast_v.value.arrayLength = str->length;
+            cast_v.value.data = str->data;
+            goto check_array;
+        }
+
+        /* An enum was sent as an int32, or an opaque type as a bytestring. This
+         * is detected with the typeIndex indicated the "true" datatype. */
+        enum type_equivalence te1 = typeEquivalence(nodeDataType);
+        enum type_equivalence te2 = typeEquivalence(value->value.type);
         if(te1 != TYPE_EQUIVALENCE_NONE && te1 == te2) {
-            /* An enum was sent as an int32, or an opaque type as a bytestring. This is
-               detected with the typeIndex indicated the "true" datatype. */
-            cast_v.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;
-            cast_v.type = &UA_TYPES[UA_TYPES_BYTE];
-            cast_v.arrayLength = str->length;
-            cast_v.data = str->data;
-        } else {
+            cast_v.value.type = nodeDataType;
+            goto check_array;
+        }
+
+        retval = UA_STATUSCODE_BADTYPEMISMATCH;
+        goto cleanup;
+    }
+
+ check_array:
+    /* See if the array dimensions match */
+    if(!rangeptr && node->arrayDimensions) {
+        if(value->value.arrayDimensionsSize != node->arrayDimensionsSize) {
             retval = UA_STATUSCODE_BADTYPEMISMATCH;
             goto cleanup;
         }
+        for(size_t i = 0; i < node->arrayDimensionsSize; i++) {
+            if(node->arrayDimensions[i] == value->value.arrayDimensions[i] ||
+               node->arrayDimensions[i] == 0)
+                continue;
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID; /* TODO check this error code */
+            goto cleanup;
+        }
     }
 
+ write_value:
     /* write the value */
     if(!rangeptr) {
-        UA_Variant_deleteMembers(&node->value.variant.value);
-        UA_Variant_copy(newV, &node->value.variant.value);
-    } else
-        retval = UA_Variant_setRangeCopy(&node->value.variant.value, newV->data,
-                                         newV->arrayLength, range);
+        UA_DataValue save = node->value.data.value;
+        UA_DataValue_init(&node->value.data.value);
+        retval = UA_DataValue_copy(value, &node->value.data.value);
+        if(retval == UA_STATUSCODE_GOOD)
+            UA_DataValue_deleteMembers(&node->value.data.value);
+        else
+            node->value.data.value = save;
+    } else {
+        UA_DataValue save = node->value.data.value;
+        node->value.data.value = *value;
+        node->value.data.value.value = save.value;
+        retval = UA_Variant_setRangeCopy(&node->value.data.value.value,
+                                         value->value.data, value->value.arrayLength, range);
+    }
 
     /* post-write callback */
-    if(node->value.variant.callback.onWrite)
-        node->value.variant.callback.onWrite(node->value.variant.callback.handle,
-                                             node->nodeId, &node->value.variant.value,
-                                             rangeptr);
+    if(node->value.data.callback.onWrite)
+        node->value.data.callback.onWrite(node->value.data.callback.handle,
+                                          node->nodeId, &node->value.data.value.value,
+                                          rangeptr);
  cleanup:
     if(rangeptr)
         UA_free(range.dimensions);
     return retval;
 }
 
+static UA_StatusCode
+CopyIsAbstractIntoNode(UA_Node *node, UA_Boolean value) {
+    switch(node->nodeClass) {
+    case UA_NODECLASS_OBJECTTYPE:
+        ((UA_ObjectTypeNode*)node)->isAbstract = value;
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        ((UA_ReferenceTypeNode*)node)->isAbstract = value;
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        ((UA_VariableTypeNode*)node)->isAbstract = value;
+        break;
+    case UA_NODECLASS_DATATYPE:
+        ((UA_DataTypeNode*)node)->isAbstract = value;
+        break;
+    default:
+        return UA_STATUSCODE_BADNODECLASSINVALID;
+    }
+    return UA_STATUSCODE_GOOD;
+}
+
 /* this function implements the main part of the write service */
 static UA_StatusCode
 CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
@@ -715,10 +732,8 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
         node->userWriteMask = *(const 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 = *(const UA_Boolean*)value;
+        retval = CopyIsAbstractIntoNode(node, *(const UA_Boolean*)value);
         break;
     case UA_ATTRIBUTEID_SYMMETRIC:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_REFERENCETYPE);
@@ -743,8 +758,9 @@ CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
         break;
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS_WRITE(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-        if(((const UA_VariableNode*)node)->valueSource == UA_VALUESOURCE_VARIANT)
-            retval = CopyIntoValueAttribute((UA_VariableNode*)node, wvalue);
+        if(((const UA_VariableNode*)node)->valueSource == UA_VALUESOURCE_DATA)
+            retval = CopyIntoValueAttribute(server, (UA_VariableNode*)node,
+                                            &wvalue->value, &wvalue->indexRange);
         else
             /* TODO: Don't make a copy of the node in the multithreaded case */
             retval = WriteToDataSource(server, session,
@@ -796,8 +812,8 @@ UA_StatusCode Service_Write_single(UA_Server *server, UA_Session *session,
                               (UA_EditNodeCallback)CopyAttributeIntoNode, wvalue);
 }
 
-void Service_Write(UA_Server *server, UA_Session *session, const UA_WriteRequest *request,
-                   UA_WriteResponse *response) {
+void Service_Write(UA_Server *server, UA_Session *session,
+                   const UA_WriteRequest *request, UA_WriteResponse *response) {
     UA_LOG_DEBUG_SESSION(server->config.logger, session, "Processing WriteRequest");
     if(request->nodesToWriteSize <= 0) {
         response->responseHeader.serviceResult = UA_STATUSCODE_BADNOTHINGTODO;

+ 7 - 7
src/server/ua_services_call.c

@@ -102,13 +102,13 @@ satisfySignature(UA_Server *server, const UA_Variant *var, const UA_Argument *ar
 
 static UA_StatusCode
 argConformsToDefinition(UA_Server *server, const UA_VariableNode *argRequirements, size_t argsSize, const UA_Variant *args) {
-    if(argRequirements->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
+    if(argRequirements->value.data.value.value.type != &UA_TYPES[UA_TYPES_ARGUMENT])
         return UA_STATUSCODE_BADINTERNALERROR;
-    UA_Argument *argReqs = (UA_Argument*)argRequirements->value.variant.value.data;
-    size_t argReqsSize = argRequirements->value.variant.value.arrayLength;
-    if(argRequirements->valueSource != UA_VALUESOURCE_VARIANT)
+    UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
+    size_t argReqsSize = argRequirements->value.data.value.value.arrayLength;
+    if(argRequirements->valueSource != UA_VALUESOURCE_DATA)
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(UA_Variant_isScalar(&argRequirements->value.variant.value))
+    if(UA_Variant_isScalar(&argRequirements->value.data.value.value))
         argReqsSize = 1;
     if(argReqsSize > argsSize)
         return UA_STATUSCODE_BADARGUMENTSMISSING;
@@ -193,12 +193,12 @@ Service_Call_single(UA_Server *server, UA_Session *session, const UA_CallMethodR
     if(!outputArguments) {
         result->outputArgumentsSize=0;
     }else{
-        result->outputArguments = UA_Array_new(outputArguments->value.variant.value.arrayLength, &UA_TYPES[UA_TYPES_VARIANT]);
+        result->outputArguments = UA_Array_new(outputArguments->value.data.value.value.arrayLength, &UA_TYPES[UA_TYPES_VARIANT]);
         if(!result->outputArguments) {
             result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
             return;
         }
-        result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
+        result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
     }
 
 #if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)

+ 63 - 33
src/server/ua_services_nodemanagement.c

@@ -244,7 +244,7 @@ Service_AddNodes_existing(UA_Server *server, UA_Session *session, UA_Node *node,
                 result->statusCode = UA_STATUSCODE_BADTYPEDEFINITIONINVALID;
             if(result->statusCode != UA_STATUSCODE_GOOD) {
                 UA_LOG_DEBUG_SESSION(server->config.logger, session,
-                                     "AddNodes: The variable if not derived from BaseVariableType");
+                                     "AddNodes: The variable is not derived from BaseVariableType");
                 goto remove_node;
             }
         }
@@ -287,7 +287,7 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
     attr.writeMask = node->writeMask;
     attr.userWriteMask = node->userWriteMask;
     // todo: handle data sources!!!!
-    UA_Variant_copy(&node->value.variant.value, &attr.value);
+    UA_Variant_copy(&node->value.data.value.value, &attr.value);
     // datatype is taken from the value
     // valuerank is taken from the value
     // array dimensions are taken from the value
@@ -543,21 +543,51 @@ copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item,
     return retval;
 }
 
+static UA_StatusCode
+copyCommonVariableAttributes(UA_Server *server, UA_VariableNode *node,
+                             const UA_AddNodesItem *item,
+                             const UA_VariableAttributes *attr) {
+
+    /* Use datatype of the typedefiniton or BaseDataType */
+    UA_NodeId dataType = attr->dataType;
+    if(UA_NodeId_isNull(&dataType)) {
+        const UA_DataType *found = findDataType(&item->typeDefinition.nodeId);
+        if(found)
+            dataType = found->typeId;
+        dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATATYPE);
+    }
+    
+    // todo: test if the type / valueRank / value attributes are consistent
+    UA_StatusCode retval = UA_Array_copy(attr->arrayDimensions, attr->arrayDimensionsSize,
+                                         (void**)&node->arrayDimensions, &UA_TYPES[UA_TYPES_INT32]);
+    if(retval != UA_STATUSCODE_GOOD)
+        return retval;
+    node->arrayDimensionsSize = attr->arrayDimensionsSize;
+    retval = UA_NodeId_copy(&dataType, &node->dataType);
+    node->valueRank = attr->valueRank;
+
+    /* Set the value */
+    UA_DataValue value;
+    UA_DataValue_init(&value);
+    value.value = attr->value;
+    value.hasValue = true;
+    retval = CopyIntoValueAttribute(server, node, &value, NULL);
+    return retval;
+}
+
 static UA_Node *
-variableNodeFromAttributes(const UA_AddNodesItem *item,
+variableNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item,
                            const UA_VariableAttributes *attr) {
     UA_VariableNode *vnode = UA_NodeStore_newVariableNode();
     if(!vnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)vnode, item,
                                                   (const UA_NodeAttributes*)attr);
-    // todo: test if the type / valueRank / value attributes are consistent
+    retval |= copyCommonVariableAttributes(server, vnode, item, attr);
     vnode->accessLevel = attr->accessLevel;
     vnode->userAccessLevel = attr->userAccessLevel;
     vnode->historizing = attr->historizing;
     vnode->minimumSamplingInterval = attr->minimumSamplingInterval;
-    vnode->valueRank = attr->valueRank;
-    retval |= UA_Variant_copy(&attr->value, &vnode->value.variant.value);
     if(retval != UA_STATUSCODE_GOOD) {
         UA_NodeStore_deleteNode((UA_Node*)vnode);
         return NULL;
@@ -565,6 +595,24 @@ variableNodeFromAttributes(const UA_AddNodesItem *item,
     return (UA_Node*)vnode;
 }
 
+static UA_Node *
+variableTypeNodeFromAttributes(UA_Server *server, const UA_AddNodesItem *item,
+                               const UA_VariableTypeAttributes *attr) {
+    UA_VariableTypeNode *vtnode = UA_NodeStore_newVariableTypeNode();
+    if(!vtnode)
+        return NULL;
+    UA_StatusCode retval = copyStandardAttributes((UA_Node*)vtnode, item,
+                                                  (const UA_NodeAttributes*)attr);
+    retval |= copyCommonVariableAttributes(server, (UA_VariableNode*)vtnode, item,
+                                           (const UA_VariableAttributes*)attr);
+    vtnode->isAbstract = attr->isAbstract;
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_NodeStore_deleteNode((UA_Node*)vtnode);
+        return NULL;
+    }
+    return (UA_Node*)vtnode;
+}
+
 static UA_Node *
 objectNodeFromAttributes(const UA_AddNodesItem *item,
                          const UA_ObjectAttributes *attr) {
@@ -615,26 +663,6 @@ objectTypeNodeFromAttributes(const UA_AddNodesItem *item,
     return (UA_Node*)otnode;
 }
 
-static UA_Node *
-variableTypeNodeFromAttributes(const UA_AddNodesItem *item,
-                               const UA_VariableTypeAttributes *attr) {
-    UA_VariableTypeNode *vtnode = UA_NodeStore_newVariableTypeNode();
-    if(!vtnode)
-        return NULL;
-    UA_StatusCode retval = copyStandardAttributes((UA_Node*)vtnode, item,
-                                                  (const UA_NodeAttributes*)attr);
-    UA_Variant_copy(&attr->value, &vtnode->value.variant.value);
-    // datatype is taken from the value
-    vtnode->valueRank = attr->valueRank;
-    // array dimensions are taken from the value
-    vtnode->isAbstract = attr->isAbstract;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_NodeStore_deleteNode((UA_Node*)vtnode);
-        return NULL;
-    }
-    return (UA_Node*)vtnode;
-}
-
 static UA_Node *
 viewNodeFromAttributes(const UA_AddNodesItem *item,
                        const UA_ViewAttributes *attr) {
@@ -693,7 +721,7 @@ Service_AddNodes_single(UA_Server *server, UA_Session *session,
             result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
             return;
         }
-        node = variableNodeFromAttributes(item, item->nodeAttributes.content.decoded.data);
+        node = variableNodeFromAttributes(server, item, item->nodeAttributes.content.decoded.data);
         break;
     case UA_NODECLASS_OBJECTTYPE:
         if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_OBJECTTYPEATTRIBUTES]) {
@@ -707,7 +735,7 @@ Service_AddNodes_single(UA_Server *server, UA_Session *session,
             result->statusCode = UA_STATUSCODE_BADNODEATTRIBUTESINVALID;
             return;
         }
-        node = variableTypeNodeFromAttributes(item, item->nodeAttributes.content.decoded.data);
+        node = variableTypeNodeFromAttributes(server, item, item->nodeAttributes.content.decoded.data);
         break;
     case UA_NODECLASS_REFERENCETYPE:
         if(item->nodeAttributes.content.decoded.type != &UA_TYPES[UA_TYPES_REFERENCETYPEATTRIBUTES]) {
@@ -971,8 +999,9 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
            result.addedNodeId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS){
             inputArgumentsVariableNode->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_INPUTARGUMENTS);
         }
-        UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant.value, inputArguments,
+        UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.data.value.value, inputArguments,
                                 inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
+        inputArgumentsVariableNode->value.data.value.hasValue = true;
         UA_AddNodesResult inputAddRes;
         UA_RCU_LOCK();
         Service_AddNodes_existing(server, &adminSession, (UA_Node*)inputArgumentsVariableNode,
@@ -996,8 +1025,9 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
            result.addedNodeId.identifier.numeric == UA_NS0ID_SERVER_GETMONITOREDITEMS){
             outputArgumentsVariableNode->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS_OUTPUTARGUMENTS);
         }
-        UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant.value, outputArguments,
+        UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.data.value.value, outputArguments,
                                 outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
+        outputArgumentsVariableNode->value.data.value.hasValue = true;
         UA_AddNodesResult outputAddRes;
         UA_RCU_LOCK();
         Service_AddNodes_existing(server, &adminSession, (UA_Node*)outputArgumentsVariableNode,
@@ -1356,7 +1386,7 @@ setValueCallback(UA_Server *server, UA_Session *session,
                  UA_VariableNode *node, UA_ValueCallback *callback) {
     if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
-    node->value.variant.callback = *callback;
+    node->value.data.callback = *callback;
     return UA_STATUSCODE_GOOD;
 }
 
@@ -1375,8 +1405,8 @@ setDataSource(UA_Server *server, UA_Session *session,
               UA_VariableNode* node, UA_DataSource *dataSource) {
     if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
-    if(node->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&node->value.variant.value);
+    if(node->valueSource == UA_VALUESOURCE_DATA)
+        UA_DataValue_deleteMembers(&node->value.data.value);
     node->value.dataSource = *dataSource;
     node->valueSource = UA_VALUESOURCE_DATASOURCE;
     return UA_STATUSCODE_GOOD;

+ 1 - 0
src/server/ua_subscription.c

@@ -1,5 +1,6 @@
 #include "ua_subscription.h"
 #include "ua_server_internal.h"
+#include "ua_types_encoding_binary.h"
 #include "ua_services.h"
 #include "ua_nodestore.h"
 

+ 48 - 35
tests/check_services_attributes.c

@@ -25,6 +25,7 @@ readCPUTemperature_broken(void *handle, const UA_NodeId nodeid, UA_Boolean sourc
 
 static UA_Server* makeTestSequence(void) {
     UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
     /* VariableNode */
     UA_VariableAttributes vattr;
@@ -38,9 +39,10 @@ static UA_Server* makeTestSequence(void) {
     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, vattr, NULL, NULL);
+    retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                       parentReferenceNodeId, myIntegerName,
+                                       UA_NODEID_NULL, vattr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* DataSource VariableNode */
     UA_VariableAttributes_init(&vattr);
@@ -48,22 +50,25 @@ static UA_Server* makeTestSequence(void) {
                                            .handle = NULL, .read = NULL, .write = NULL};
     vattr.description = UA_LOCALIZEDTEXT("en_US","temperature");
     vattr.displayName = UA_LOCALIZEDTEXT("en_US","temperature");
-    UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature"),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                        UA_QUALIFIEDNAME(1, "cpu temperature"),
-                                        UA_NODEID_NULL, vattr, temperatureDataSource, NULL);
+    retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature"),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                                 UA_QUALIFIEDNAME(1, "cpu temperature"),
+                                                 UA_NODEID_NULL, vattr, temperatureDataSource, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* DataSource Variable returning no value */
     UA_DataSource temperatureDataSource1 = (UA_DataSource) {
                                             .handle = NULL, .read = readCPUTemperature_broken, .write = NULL};
     vattr.description = UA_LOCALIZEDTEXT("en_US","temperature1");
     vattr.displayName = UA_LOCALIZEDTEXT("en_US","temperature1");
-    UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature1"),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
-                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                                        UA_QUALIFIEDNAME(1, "cpu temperature bogus"),
-                                        UA_NODEID_NULL, vattr, temperatureDataSource1, NULL);
+    retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, "cpu.temperature1"),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
+                                                 UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                                 UA_QUALIFIEDNAME(1, "cpu temperature bogus"),
+                                                 UA_NODEID_NULL, vattr, temperatureDataSource1, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
     /* VariableNode with array */
     UA_VariableAttributes_init(&vattr);
     UA_Int32 myIntegerArray[9] = {1,2,3,4,5,6,7,8,9};
@@ -76,31 +81,34 @@ static UA_Server* makeTestSequence(void) {
     myIntegerNodeId = UA_NODEID_STRING(1, "myarray");
     parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
     parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
-                              parentReferenceNodeId, myIntegerName,
-                              UA_NODEID_NULL, vattr, NULL, NULL);
+    retval = UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
+                                       parentReferenceNodeId, myIntegerName,
+                                       UA_NODEID_NULL, vattr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* ObjectNode */
     UA_ObjectAttributes obj_attr;
     UA_ObjectAttributes_init(&obj_attr);
     obj_attr.description = UA_LOCALIZEDTEXT("en_US","Demo");
     obj_attr.displayName = UA_LOCALIZEDTEXT("en_US","Demo");
-    UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50),
-                            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),
-                            obj_attr, NULL, NULL);
+    retval = UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 50),
+                                     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),
+                                     obj_attr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     /* ViewNode */
     UA_ViewAttributes view_attr;
     UA_ViewAttributes_init(&view_attr);
     view_attr.description = UA_LOCALIZEDTEXT("en_US", "Viewtest");
     view_attr.displayName = UA_LOCALIZEDTEXT("en_US", "Viewtest");
-    UA_Server_addViewNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWNODE),
-                          UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
-                          UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
-                          UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
+    retval = UA_Server_addViewNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWNODE),
+                                   UA_NODEID_NUMERIC(0, UA_NS0ID_VIEWSFOLDER),
+                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
+                                   UA_QUALIFIEDNAME(0, "Viewtest"), view_attr, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
 #ifdef UA_ENABLE_METHODCALLS
     /* MethodNode */
@@ -108,11 +116,12 @@ static UA_Server* makeTestSequence(void) {
     UA_MethodAttributes_init(&ma);
     ma.description = UA_LOCALIZEDTEXT("en_US", "Methodtest");
     ma.displayName = UA_LOCALIZEDTEXT("en_US", "Methodtest");
-    UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
-                            UA_NODEID_NUMERIC(0, 3),
-                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
-                            UA_QUALIFIEDNAME_ALLOC(0, "Methodtest"), ma,
-                            NULL, NULL, 0, NULL, 0, NULL, NULL);
+    retval = UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(0, UA_NS0ID_METHODNODE),
+                                     UA_NODEID_NUMERIC(0, 3),
+                                     UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                                     UA_QUALIFIEDNAME_ALLOC(0, "Methodtest"), ma,
+                                     NULL, NULL, 0, NULL, 0, NULL, NULL);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 #endif
 
     return server;
@@ -122,7 +131,8 @@ static UA_VariableNode* makeCompareSequence(void) {
     UA_VariableNode *node = UA_NodeStore_newVariableNode();
 
     UA_Int32 myInteger = 42;
-    UA_Variant_setScalarCopy(&node->value.variant.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    UA_Variant_setScalarCopy(&node->value.data.value.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
+    node->value.data.value.hasValue = true;
 
     const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
     UA_QualifiedName_copy(&myIntegerName,&node->browseName);
@@ -435,11 +445,13 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) {
     rReq.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
     rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_DATATYPE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &rReq.nodesToRead[0], &resp);
-    UA_NodeId* respval = (UA_NodeId*) resp.value.data;
     ck_assert_int_eq(0, resp.value.arrayLength);
+    ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
+    ck_assert_int_eq(true, resp.hasValue);
     ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
+    UA_NodeId* respval = (UA_NodeId*)resp.value.data;
     ck_assert_int_eq(respval->namespaceIndex,0);
-    ck_assert_int_eq(respval->identifier.numeric,UA_NS0ID_INT32);
+    ck_assert_int_eq(respval->identifier.numeric, UA_NS0ID_BASEDATATYPE);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);
@@ -860,6 +872,7 @@ START_TEST(WriteSingleAttributeValue) {
     wValue.nodeId = UA_NODEID_STRING(1, "the.answer");
     wValue.attributeId = UA_ATTRIBUTEID_VALUE;
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wValue);
+    ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
 
     UA_DataValue resp;
     UA_DataValue_init(&resp);
@@ -869,7 +882,7 @@ START_TEST(WriteSingleAttributeValue) {
     id.attributeId = UA_ATTRIBUTEID_VALUE;
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER, &id, &resp);
     ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
-    ck_assert(wValue.value.hasValue);
+    ck_assert(resp.hasValue);
     ck_assert_int_eq(20, *(UA_Int32*)resp.value.data);
     UA_DataValue_deleteMembers(&resp);
     UA_Server_delete(server);