Sfoglia il codice sorgente

support writing of numeric ranges (required for nano). no test cases in the ctt

Julius Pfrommer 10 anni fa
parent
commit
efbf011556

+ 1 - 1
examples/client.c

@@ -22,7 +22,7 @@ int main(int argc, char *argv[]) {
     UA_ReadRequest_init(&req);
     req.nodesToRead = UA_ReadValueId_new();
     req.nodesToReadSize = 1;
-    req.nodesToRead[0].nodeId = UA_NODEID_STRING(1, "the.answer"); /* assume this node exists */
+    req.nodesToRead[0].nodeId = UA_NODEID_STRING_ALLOC(1, "the.answer"); /* assume this node exists */
     req.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;
 
     UA_ReadResponse resp = UA_Client_read(client, &req);

+ 3 - 3
examples/server.c

@@ -180,7 +180,7 @@ int main(int argc, char** argv) {
 		.read = readTimeData,
 		.release = releaseTimeData,
 		.write = NULL};
-	const UA_QualifiedName dateName = UA_QUALIFIEDNAME_STATIC(0, "current time");
+	const UA_QualifiedName dateName = UA_QUALIFIEDNAME(0, "current time");
 	UA_Server_addDataSourceVariableNode(server, dateDataSource, dateName, UA_NODEID_NULL,
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
@@ -220,7 +220,7 @@ int main(int argc, char** argv) {
 		.read = readLedStatus,
 		.release = releaseLedStatus,
 		.write = writeLedStatus};
-	const UA_QualifiedName statusName = UA_QUALIFIEDNAME_STATIC(0, "status LED");
+	const UA_QualifiedName statusName = UA_QUALIFIEDNAME(0, "status LED");
 	UA_Server_addDataSourceVariableNode(server, ledStatusDataSource, statusName, UA_NODEID_NULL,
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                         UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
@@ -229,7 +229,7 @@ int main(int argc, char** argv) {
     UA_Variant *myIntegerVariant = UA_Variant_new();
     UA_Int32 myInteger = 42;
     UA_Variant_setScalarCopy(myIntegerVariant, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
-    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_STATIC(0, "the answer");
+    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(0, "the answer");
     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);

+ 4 - 6
examples/server_simple.c

@@ -87,11 +87,10 @@ int main(int argc, char** argv) {
         void *data = UA_new(&UA_TYPES[i]);
         UA_Variant *variant = UA_Variant_new();
         UA_Variant_setScalar(variant, data, &UA_TYPES[i]);
-        UA_QualifiedName *nodeName = UA_QualifiedName_new();
         sprintf(str,"%d",i);
-        *nodeName = UA_QUALIFIEDNAME(1, str);
+        UA_QualifiedName nodeName = UA_QUALIFIEDNAME(1, str);
         UA_NodeId id = UA_NODEID_NUMERIC(1, 100 + i);
-        UA_Server_addVariableNode(server, variant, *nodeName, id,
+        UA_Server_addVariableNode(server, variant, nodeName, id,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
 
@@ -99,11 +98,10 @@ int main(int argc, char** argv) {
         data = UA_Array_new(&UA_TYPES[i], 10);
         variant = UA_Variant_new();
         UA_Variant_setArray(variant, data, 10, &UA_TYPES[i]);
-        nodeName = UA_QualifiedName_new();
         sprintf(str,"array of %d",i);
-        *nodeName = UA_QUALIFIEDNAME(1, str);
+        nodeName = UA_QUALIFIEDNAME(1, str);
         id = UA_NODEID_NUMERIC(1, 200 + i);
-        UA_Server_addVariableNode(server, variant, *nodeName, id,
+        UA_Server_addVariableNode(server, variant, nodeName, id,
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES));
     }

+ 22 - 10
include/ua_types.h

@@ -323,9 +323,9 @@ UA_TYPE_HANDLING_FUNCTIONS(UA_DiagnosticInfo)
 /** Copy a (zero terminated) char-array into a UA_String. Memory for the string data is
     allocated. If the memory cannot be allocated, a null-string is returned. */
 UA_String UA_EXPORT UA_String_fromChars(char const *src);
-#define UA_STRING(CHARS) UA_String_fromChars(CHARS)
+#define UA_STRING_ALLOC(CHARS) UA_String_fromChars(CHARS)
+#define UA_STRING(CHARS) (const UA_String) {sizeof(CHARS), (UA_Byte*)CHARS }
 #define UA_STRING_NULL (UA_String) {-1, (UA_Byte*)0 }
-#define UA_STRING_STATIC(CHARS) (const UA_String) {sizeof(CHARS), (UA_Byte*)CHARS }
 
 /** Printf a char-array into a UA_String. Memory for the string data is allocated. */
 UA_StatusCode UA_EXPORT UA_String_copyprintf(char const *fmt, UA_String *dst, ...);
@@ -378,21 +378,31 @@ UA_Boolean UA_EXPORT UA_NodeId_isNull(const UA_NodeId *p);
         .identifierType = UA_NODEIDTYPE_NUMERIC,                       \
         .identifier.numeric = NUMERICID }
 
-#define UA_NODEID_STRING(NS_INDEX, CHARS) (UA_NodeId) {                \
+#define UA_NODEID_STRING(NS_INDEX, CHARS) (const UA_NodeId) {          \
         .namespaceIndex = NS_INDEX,                                    \
         .identifierType = UA_NODEIDTYPE_STRING,                        \
         .identifier.string = UA_STRING(CHARS) }
+    
+#define UA_NODEID_STRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) {    \
+        .namespaceIndex = NS_INDEX,                                    \
+        .identifierType = UA_NODEIDTYPE_STRING,                        \
+        .identifier.string = UA_STRING_ALLOC(CHARS) }
 
 #define UA_NODEID_GUID(NS_INDEX, GUID) (UA_NodeId) {                   \
         .namespaceIndex = NS_INDEX,                                    \
         .identifierType = UA_NODEIDTYPE_GUID,                          \
         .identifier.guid = GUID }
 
-#define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) (UA_NodeId) {            \
+#define UA_NODEID_BYTESTRING(NS_INDEX, CHARS) (const UA_NodeId) {      \
         .namespaceIndex = NS_INDEX,                                    \
         .identifierType = UA_NODEIDTYPE_BYTESTRING,                    \
         .identifier.byteString = UA_STRING(CHARS) }
 
+#define UA_NODEID_BYTESTRING_ALLOC(NS_INDEX, CHARS) (const UA_NodeId) {\
+        .namespaceIndex = NS_INDEX,                                    \
+        .identifierType = UA_NODEIDTYPE_BYTESTRING,                    \
+        .identifier.byteString = UA_STRING_ALLOC(CHARS) }
+
 #define UA_NODEID_NULL UA_NODEID_NUMERIC(0,0)
 
 /* ExpandedNodeId */
@@ -404,14 +414,16 @@ UA_Boolean UA_EXPORT UA_ExpandedNodeId_isNull(const UA_ExpandedNodeId *p);
         .serverIndex = 0, .namespaceUri = {.data = (UA_Byte*)0, .length = -1} }
     
 /* QualifiedName */
-#define UA_QUALIFIEDNAME(NS_INDEX, NAME) (UA_QualifiedName) {   \
-        .namespaceIndex = NS_INDEX, .name = UA_STRING(NAME) }
-#define UA_QUALIFIEDNAME_STATIC(NS_INDEX, NAME) (const UA_QualifiedName) {    \
-        .namespaceIndex = NS_INDEX, .name = UA_STRING_STATIC(NAME) }
+#define UA_QUALIFIEDNAME(NS_INDEX, CHARS) (const UA_QualifiedName) {    \
+        .namespaceIndex = NS_INDEX, .name = UA_STRING(CHARS) }
+#define UA_QUALIFIEDNAME_ALLOC(NS_INDEX, CHARS) (const UA_QualifiedName) {    \
+        .namespaceIndex = NS_INDEX, .name = UA_STRING_ALLOC(CHARS) }
 
 /* LocalizedText */
-#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (UA_LocalizedText) {             \
-        .locale = UA_String_fromChars(LOCALE), .text = UA_STRING(TEXT) }
+#define UA_LOCALIZEDTEXT(LOCALE, TEXT) (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) }
 
 /* Variant */
 

+ 20 - 12
src/server/ua_nodes.c

@@ -100,8 +100,8 @@ UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectType
 void UA_VariableNode_init(UA_VariableNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_VARIABLE;
-    p->variableType = UA_VARIABLENODETYPE_VARIANT;
-    UA_Variant_init(&p->variable.variant);
+    p->valueSource = UA_VALUESOURCE_VARIANT;
+    UA_Variant_init(&p->value.variant);
     p->valueRank = -2; // scalar or array of any dimension
     p->accessLevel = 0;
     p->userAccessLevel = 0;
@@ -118,8 +118,8 @@ UA_VariableNode * UA_VariableNode_new(void) {
 
 void UA_VariableNode_deleteMembers(UA_VariableNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
-    if(p->variableType == UA_VARIABLENODETYPE_VARIANT)
-        UA_Variant_deleteMembers(&p->variable.variant);
+    if(p->valueSource == UA_VALUESOURCE_VARIANT)
+        UA_Variant_deleteMembers(&p->value.variant);
 }
 
 void UA_VariableNode_delete(UA_VariableNode *p) {
@@ -130,11 +130,12 @@ void UA_VariableNode_delete(UA_VariableNode *p) {
 UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
     UA_VariableNode_init(dst);
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-    dst->variableType = src->variableType;
-    if(src->variableType == UA_VARIABLENODETYPE_VARIANT)
-        retval = UA_Variant_copy(&src->variable.variant, &dst->variable.variant);
+    dst->valueRank = src->valueRank;
+    dst->valueSource = src->valueSource;
+    if(src->valueSource == UA_VALUESOURCE_VARIANT)
+        retval = UA_Variant_copy(&src->value.variant, &dst->value.variant);
     else
-        dst->variable.dataSource = src->variable.dataSource;
+        dst->value.dataSource = src->value.dataSource;
     if(retval) {
         UA_VariableNode_deleteMembers(dst);
         return retval;
@@ -150,8 +151,9 @@ UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *
 void UA_VariableTypeNode_init(UA_VariableTypeNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_VARIABLETYPE;
-    UA_Variant_init(&p->value);
-    p->valueRank = 0;
+    p->valueSource = UA_VALUESOURCE_VARIANT;
+    UA_Variant_init(&p->value.variant);
+    p->valueRank = -2; // scalar or array of any dimension
     p->isAbstract = UA_FALSE;
 }
 
@@ -164,7 +166,8 @@ UA_VariableTypeNode * UA_VariableTypeNode_new(void) {
 
 void UA_VariableTypeNode_deleteMembers(UA_VariableTypeNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
-    UA_Variant_deleteMembers(&p->value);
+    if(p->valueSource == UA_VALUESOURCE_VARIANT)
+        UA_Variant_deleteMembers(&p->value.variant);
 }
 
 void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
@@ -175,7 +178,12 @@ void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
 UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
     UA_VariableTypeNode_init(dst);
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-    retval |= UA_Variant_copy(&src->value, &dst->value);
+    dst->valueRank = src->valueRank;
+    dst->valueSource = src->valueSource;
+    if(src->valueSource == UA_VALUESOURCE_VARIANT)
+        UA_Variant_copy(&src->value.variant, &dst->value.variant);
+    else
+        dst->value.dataSource = src->value.dataSource;
     if(retval) {
         UA_VariableTypeNode_deleteMembers(dst);
         return retval;

+ 15 - 7
src/server/ua_nodes.h

@@ -32,6 +32,11 @@ typedef struct {
 } UA_ObjectTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectTypeNode)
 
+typedef enum {
+    UA_VALUESOURCE_VARIANT,
+    UA_VALUESOURCE_DATASOURCE
+} UA_ValueSource;
+
 typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Int32 valueRank; /**< n >= 1: the value is an array with the specified number of dimensions.
@@ -39,14 +44,12 @@ typedef struct {
                              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. */
-    enum {
-        UA_VARIABLENODETYPE_VARIANT,
-        UA_VARIABLENODETYPE_DATASOURCE
-    } variableType;
+    UA_ValueSource valueSource;
     union {
         UA_Variant variant;
         UA_DataSource dataSource;
-    } variable;
+    } value;
+    /* <--- similar to variabletypenodes up to there--->*/
     UA_Byte accessLevel;
     UA_Byte userAccessLevel;
     UA_Double minimumSamplingInterval;
@@ -58,9 +61,14 @@ UA_StatusCode UA_VariableNode_copyWithoutRefsAndVariable(const UA_VariableNode *
 
 typedef struct {
     UA_STANDARD_NODEMEMBERS
-    UA_Boolean isAbstract;
     UA_Int32 valueRank;
-    UA_Variant value;
+    UA_ValueSource valueSource;
+    union {
+        UA_Variant variant;
+        UA_DataSource dataSource;
+    } value;
+    /* <--- similar to variablenodes up to there--->*/
+    UA_Boolean isAbstract;
 } UA_VariableTypeNode;
 UA_TYPE_HANDLING_FUNCTIONS(UA_VariableTypeNode)
 

+ 53 - 58
src/server/ua_server.c

@@ -94,13 +94,13 @@ static UA_StatusCode readStatus(const void *handle, UA_Boolean sourceTimeStamp,
     status->startTime   = ((const UA_Server*)handle)->startTime;
     status->currentTime = UA_DateTime_now();
     status->state       = UA_SERVERSTATE_RUNNING;
-    status->buildInfo.productUri = UA_STRING("http://www.open62541.org");
-    status->buildInfo.manufacturerName = UA_STRING("open62541");
-    status->buildInfo.productName = UA_STRING("open62541 OPC UA Server");
+    status->buildInfo.productUri = UA_STRING_ALLOC("http://www.open62541.org");
+    status->buildInfo.manufacturerName = UA_STRING_ALLOC("open62541");
+    status->buildInfo.productName = UA_STRING_ALLOC("open62541 OPC UA Server");
 #define STRINGIFY(x) #x //some magic
 #define TOSTRING(x) STRINGIFY(x) //some magic
-    status->buildInfo.softwareVersion = UA_STRING(TOSTRING(OPEN62541_VERSION_MAJOR) "." TOSTRING(OPEN62541_VERSION_MINOR) "." TOSTRING(OPEN62541_VERSION_PATCH));
-    status->buildInfo.buildNumber = UA_STRING("0");
+    status->buildInfo.softwareVersion = UA_STRING_ALLOC(TOSTRING(OPEN62541_VERSION_MAJOR) "." TOSTRING(OPEN62541_VERSION_MINOR) "." TOSTRING(OPEN62541_VERSION_PATCH));
+    status->buildInfo.buildNumber = UA_STRING_ALLOC("0");
     status->buildInfo.buildDate = ((const UA_Server*)handle)->buildDate;
     status->secondsTillShutdown = 0;
     value->value.type = &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE];
@@ -143,9 +143,9 @@ static void releaseCurrentTime(const void *handle, UA_DataValue *value) {
 }
 
 static void copyNames(UA_Node *node, char *name) {
-    node->browseName = UA_QUALIFIEDNAME(0, name);
-    node->displayName = UA_LOCALIZEDTEXT("", name);
-    node->description = UA_LOCALIZEDTEXT("", name);
+    node->browseName = UA_QUALIFIEDNAME_ALLOC(0, name);
+    node->displayName = UA_LOCALIZEDTEXT_ALLOC("", name);
+    node->description = UA_LOCALIZEDTEXT_ALLOC("", name);
 }
 
 static void addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_Int32 parent) {
@@ -162,8 +162,7 @@ static UA_VariableTypeNode* createVariableTypeNode(UA_Server *server, char* name
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
     variabletype->isAbstract = abstract;
-    variabletype->value.type = &UA_TYPES[UA_TYPES_VARIANT];
-
+    variabletype->value.variant.type = &UA_TYPES[UA_TYPES_VARIANT];
     return variabletype;
 }
 
@@ -211,11 +210,11 @@ UA_Server * UA_Server_new(void) {
 #define APPLICATION_URI "urn:unconfigured:open62541:open62541Server"
     // mockup application description
     UA_ApplicationDescription_init(&server->description);
-    server->description.productUri = UA_STRING(PRODUCT_URI);
-    server->description.applicationUri = UA_STRING(APPLICATION_URI);
+    server->description.productUri = UA_STRING_ALLOC(PRODUCT_URI);
+    server->description.applicationUri = UA_STRING_ALLOC(APPLICATION_URI);
     server->description.discoveryUrlsSize = 0;
 
-    server->description.applicationName = UA_LOCALIZEDTEXT("", "Unconfigured open62541 application");
+    server->description.applicationName = UA_LOCALIZEDTEXT_ALLOC("", "Unconfigured open62541 application");
     server->description.applicationType = UA_APPLICATIONTYPE_SERVER;
     server->externalNamespacesSize = 0;
     server->externalNamespaces = UA_NULL;
@@ -223,15 +222,15 @@ UA_Server * UA_Server_new(void) {
     UA_EndpointDescription *endpoint = UA_EndpointDescription_new(); // todo: check return code
     if(endpoint) {
         endpoint->securityMode = UA_MESSAGESECURITYMODE_NONE;
-        endpoint->securityPolicyUri = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
-        endpoint->transportProfileUri = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
+        endpoint->securityPolicyUri = UA_STRING_ALLOC("http://opcfoundation.org/UA/SecurityPolicy#None");
+        endpoint->transportProfileUri = UA_STRING_ALLOC("http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary");
         endpoint->userIdentityTokens = UA_malloc(sizeof(UA_UserTokenPolicy));
         if(!endpoint->userIdentityTokens) {
             UA_EndpointDescription_delete(endpoint);
         } else {
             UA_UserTokenPolicy_init(endpoint->userIdentityTokens);
             endpoint->userIdentityTokens->tokenType = UA_USERTOKENTYPE_ANONYMOUS;
-            endpoint->userIdentityTokens->policyId = UA_STRING("my-anonymous-policy"); // defined per server
+            endpoint->userIdentityTokens->policyId = UA_STRING_ALLOC("my-anonymous-policy"); // defined per server
 
             /* UA_String_copy(endpointUrl, &endpoint->endpointUrl); */
             /* /\* The standard says "the HostName specified in the Server Certificate is the */
@@ -275,7 +274,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hassubtype = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hassubtype, "HasSubtype");
-    hassubtype->inverseName = UA_LOCALIZEDTEXT("", "HasSupertype");
+    hassubtype->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "HasSupertype");
     hassubtype->nodeId.identifier.numeric = UA_NS0ID_HASSUBTYPE;
     hassubtype->isAbstract = UA_FALSE;
     hassubtype->symmetric  = UA_FALSE;
@@ -311,7 +310,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)organizes, "Organizes");
-    organizes->inverseName = UA_LOCALIZEDTEXT("", "OrganizedBy");
+    organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrganizedBy");
     organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
     organizes->isAbstract = UA_FALSE;
     organizes->symmetric  = UA_FALSE;
@@ -321,7 +320,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseventsource, "HasEventSource");
-    haseventsource->inverseName = UA_LOCALIZEDTEXT("", "EventSourceOf");
+    haseventsource->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "EventSourceOf");
     haseventsource->nodeId.identifier.numeric = UA_NS0ID_HASEVENTSOURCE;
     haseventsource->isAbstract = UA_FALSE;
     haseventsource->symmetric  = UA_FALSE;
@@ -331,7 +330,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodellingrule, "HasModellingRule");
-    hasmodellingrule->inverseName = UA_LOCALIZEDTEXT("", "ModellingRuleOf");
+    hasmodellingrule->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ModellingRuleOf");
     hasmodellingrule->nodeId.identifier.numeric = UA_NS0ID_HASMODELLINGRULE;
     hasmodellingrule->isAbstract = UA_FALSE;
     hasmodellingrule->symmetric  = UA_FALSE;
@@ -341,7 +340,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasencoding, "HasEncoding");
-    hasencoding->inverseName = UA_LOCALIZEDTEXT("", "EncodingOf");
+    hasencoding->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "EncodingOf");
     hasencoding->nodeId.identifier.numeric = UA_NS0ID_HASENCODING;
     hasencoding->isAbstract = UA_FALSE;
     hasencoding->symmetric  = UA_FALSE;
@@ -351,7 +350,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasdescription, "HasDescription");
-    hasdescription->inverseName = UA_LOCALIZEDTEXT("", "DescriptionOf");
+    hasdescription->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "DescriptionOf");
     hasdescription->nodeId.identifier.numeric = UA_NS0ID_HASDESCRIPTION;
     hasdescription->isAbstract = UA_FALSE;
     hasdescription->symmetric  = UA_FALSE;
@@ -361,7 +360,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hastypedefinition, "HasTypeDefinition");
-    hastypedefinition->inverseName = UA_LOCALIZEDTEXT("", "TypeDefinitionOf");
+    hastypedefinition->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "TypeDefinitionOf");
     hastypedefinition->nodeId.identifier.numeric = UA_NS0ID_HASTYPEDEFINITION;
     hastypedefinition->isAbstract = UA_FALSE;
     hastypedefinition->symmetric  = UA_FALSE;
@@ -371,7 +370,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)generatesevent, "GeneratesEvent");
-    generatesevent->inverseName = UA_LOCALIZEDTEXT("", "GeneratedBy");
+    generatesevent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "GeneratedBy");
     generatesevent->nodeId.identifier.numeric = UA_NS0ID_GENERATESEVENT;
     generatesevent->isAbstract = UA_FALSE;
     generatesevent->symmetric  = UA_FALSE;
@@ -395,7 +394,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasproperty = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasproperty, "HasProperty");
-    hasproperty->inverseName = UA_LOCALIZEDTEXT("", "PropertyOf");
+    hasproperty->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "PropertyOf");
     hasproperty->nodeId.identifier.numeric = UA_NS0ID_HASPROPERTY;
     hasproperty->isAbstract = UA_FALSE;
     hasproperty->symmetric  = UA_FALSE;
@@ -405,7 +404,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascomponent, "HasComponent");
-    hascomponent->inverseName = UA_LOCALIZEDTEXT("", "ComponentOf");
+    hascomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ComponentOf");
     hascomponent->nodeId.identifier.numeric = UA_NS0ID_HASCOMPONENT;
     hascomponent->isAbstract = UA_FALSE;
     hascomponent->symmetric  = UA_FALSE;
@@ -415,7 +414,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasnotifier, "HasNotifier");
-    hasnotifier->inverseName = UA_LOCALIZEDTEXT("", "NotifierOf");
+    hasnotifier->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "NotifierOf");
     hasnotifier->nodeId.identifier.numeric = UA_NS0ID_HASNOTIFIER;
     hasnotifier->isAbstract = UA_FALSE;
     hasnotifier->symmetric  = UA_FALSE;
@@ -425,7 +424,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasorderedcomponent, "HasOrderedComponent");
-    hasorderedcomponent->inverseName = UA_LOCALIZEDTEXT("", "OrderedComponentOf");
+    hasorderedcomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "OrderedComponentOf");
     hasorderedcomponent->nodeId.identifier.numeric = UA_NS0ID_HASORDEREDCOMPONENT;
     hasorderedcomponent->isAbstract = UA_FALSE;
     hasorderedcomponent->symmetric  = UA_FALSE;
@@ -435,7 +434,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hasmodelparent, "HasModelParent");
-    hasmodelparent->inverseName = UA_LOCALIZEDTEXT("", "ModelParentOf");
+    hasmodelparent->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ModelParentOf");
     hasmodelparent->nodeId.identifier.numeric = UA_NS0ID_HASMODELPARENT;
     hasmodelparent->isAbstract = UA_FALSE;
     hasmodelparent->symmetric  = UA_FALSE;
@@ -445,7 +444,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)fromstate, "FromState");
-    fromstate->inverseName = UA_LOCALIZEDTEXT("", "ToTransition");
+    fromstate->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "ToTransition");
     fromstate->nodeId.identifier.numeric = UA_NS0ID_FROMSTATE;
     fromstate->isAbstract = UA_FALSE;
     fromstate->symmetric  = UA_FALSE;
@@ -455,7 +454,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)tostate, "ToState");
-    tostate->inverseName = UA_LOCALIZEDTEXT("", "FromTransition");
+    tostate->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "FromTransition");
     tostate->nodeId.identifier.numeric = UA_NS0ID_TOSTATE;
     tostate->isAbstract = UA_FALSE;
     tostate->symmetric  = UA_FALSE;
@@ -465,7 +464,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hascause, "HasCause");
-    hascause->inverseName = UA_LOCALIZEDTEXT("", "MayBeCausedBy");
+    hascause->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "MayBeCausedBy");
     hascause->nodeId.identifier.numeric = UA_NS0ID_HASCAUSE;
     hascause->isAbstract = UA_FALSE;
     hascause->symmetric  = UA_FALSE;
@@ -475,7 +474,7 @@ UA_Server * UA_Server_new(void) {
     
     UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)haseffect, "HasEffect");
-    haseffect->inverseName = UA_LOCALIZEDTEXT("", "MayBeEffectedBy");
+    haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "MayBeEffectedBy");
     haseffect->nodeId.identifier.numeric = UA_NS0ID_HASEFFECT;
     haseffect->isAbstract = UA_FALSE;
     haseffect->symmetric  = UA_FALSE;
@@ -485,7 +484,7 @@ UA_Server * UA_Server_new(void) {
 
     UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
     copyNames((UA_Node*)hashistoricalconfiguration, "HasHistoricalConfiguration");
-    hashistoricalconfiguration->inverseName = UA_LOCALIZEDTEXT("", "HistoricalConfigurationOf");
+    hashistoricalconfiguration->inverseName = UA_LOCALIZEDTEXT_ALLOC("", "HistoricalConfigurationOf");
     hashistoricalconfiguration->nodeId.identifier.numeric = UA_NS0ID_HASHISTORICALCONFIGURATION;
     hashistoricalconfiguration->isAbstract = UA_FALSE;
     hashistoricalconfiguration->symmetric  = UA_FALSE;
@@ -621,13 +620,12 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *namespaceArray = UA_VariableNode_new();
    copyNames((UA_Node*)namespaceArray, "NamespaceArray");
    namespaceArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_NAMESPACEARRAY;
-   namespaceArray->variableType = UA_VARIABLENODETYPE_VARIANT;
-   namespaceArray->variable.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
-   namespaceArray->variable.variant.arrayLength = 2;
-   namespaceArray->variable.variant.type = &UA_TYPES[UA_TYPES_STRING];
+   namespaceArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
+   namespaceArray->value.variant.arrayLength = 2;
+   namespaceArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
    // Fixme: Insert the external namespaces
-   ((UA_String *)namespaceArray->variable.variant.data)[0] = UA_STRING("http://opcfoundation.org/UA/");
-   ((UA_String *)namespaceArray->variable.variant.data)[1] = UA_STRING(APPLICATION_URI);
+   ((UA_String *)namespaceArray->value.variant.data)[0] = UA_STRING_ALLOC("http://opcfoundation.org/UA/");
+   ((UA_String *)namespaceArray->value.variant.data)[1] = UA_STRING_ALLOC(APPLICATION_URI);
    namespaceArray->valueRank = 1;
    namespaceArray->minimumSamplingInterval = 1.0;
    namespaceArray->historizing = UA_FALSE;
@@ -641,11 +639,10 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *serverArray = UA_VariableNode_new();
    copyNames((UA_Node*)serverArray, "ServerArray");
    serverArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERARRAY;
-   serverArray->variableType = UA_VARIABLENODETYPE_VARIANT;
-   serverArray->variable.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
-   serverArray->variable.variant.arrayLength = 1;
-   serverArray->variable.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   *(UA_String *)serverArray->variable.variant.data = UA_STRING(APPLICATION_URI);
+   serverArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
+   serverArray->value.variant.arrayLength = 1;
+   serverArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+   *(UA_String *)serverArray->value.variant.data = UA_STRING_ALLOC(APPLICATION_URI);
    serverArray->valueRank = 1;
    serverArray->minimumSamplingInterval = 1.0;
    serverArray->historizing = UA_FALSE;
@@ -666,11 +663,10 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *localeIdArray = UA_VariableNode_new();
    copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
    localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
-   localeIdArray->variableType = UA_VARIABLENODETYPE_VARIANT;
-   localeIdArray->variable.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
-   localeIdArray->variable.variant.arrayLength = 2;
-   localeIdArray->variable.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   *(UA_String *)localeIdArray->variable.variant.data = UA_STRING("en");
+   localeIdArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 2);
+   localeIdArray->value.variant.arrayLength = 2;
+   localeIdArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+   *(UA_String *)localeIdArray->value.variant.data = UA_STRING_ALLOC("en");
    localeIdArray->valueRank = 1;
    localeIdArray->minimumSamplingInterval = 1.0;
    localeIdArray->historizing = UA_FALSE;
@@ -683,8 +679,8 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *serverstatus = UA_VariableNode_new();
    copyNames((UA_Node*)serverstatus, "ServerStatus");
    serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
-   serverstatus->variableType = UA_VARIABLENODETYPE_DATASOURCE;
-   serverstatus->variable.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
+   serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
+   serverstatus->value.dataSource = (UA_DataSource) {.handle = server, .read = readStatus,
 	   .release = releaseStatus, .write = UA_NULL};
    UA_Server_addNode(server, (UA_Node*)serverstatus, &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
 		   &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));
@@ -694,10 +690,9 @@ UA_Server * UA_Server_new(void) {
    *stateEnum = UA_SERVERSTATE_RUNNING;
    copyNames((UA_Node*)state, "State");
    state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-   state->variableType = UA_VARIABLENODETYPE_VARIANT;
-   state->variable.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-   state->variable.variant.arrayLength = -1;
-   state->variable.variant.data = stateEnum; // points into the other object.
+   state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
+   state->value.variant.arrayLength = -1;
+   state->value.variant.data = stateEnum; // points into the other object.
    UA_NodeStore_insert(server->nodestore, (UA_Node*)state, UA_NULL);
    ADDREFERENCE(UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
 		   UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE));
@@ -705,8 +700,8 @@ UA_Server * UA_Server_new(void) {
    UA_VariableNode *currenttime = UA_VariableNode_new();
    copyNames((UA_Node*)currenttime, "CurrentTime");
    currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
-   currenttime->variableType = UA_VARIABLENODETYPE_DATASOURCE;
-   currenttime->variable.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
+   currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
+   currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime,
 	   .release = releaseCurrentTime, .write = UA_NULL};
    UA_Server_addNode(server, (UA_Node*)currenttime, &UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
 		   &UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT));

+ 6 - 4
src/server/ua_server_addressspace.c

@@ -6,7 +6,7 @@ UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_Qualifi
                           UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId)
 {
     UA_VariableNode *node = UA_VariableNode_new();
-    node->variable.variant = *value; // copy content
+    node->value.variant = *value; // copy content
     UA_NodeId_copy(&nodeId, &node->nodeId);
     UA_QualifiedName_copy(&browseName, &node->browseName);
     UA_String_copy(&browseName.name, &node->displayName.text);
@@ -18,10 +18,11 @@ UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_Qualifi
     ADDREFERENCE(res.addedNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION),
                  UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric));
     if(res.statusCode != UA_STATUSCODE_GOOD) {
-        UA_Variant_init(&node->variable.variant);
+        UA_Variant_init(&node->value.variant);
         UA_VariableNode_delete(node);
     } else
         UA_free(value);
+    UA_AddNodesResult_deleteMembers(&res);
     return res.statusCode;
 }
 
@@ -31,8 +32,8 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                                     const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId)
 {
     UA_VariableNode *node = UA_VariableNode_new();
-    node->variableType = UA_VARIABLENODETYPE_DATASOURCE;
-    node->variable.dataSource = dataSource;
+    node->valueSource = UA_VALUESOURCE_DATASOURCE;
+    node->value.dataSource = dataSource;
     UA_NodeId_copy(&nodeId, &node->nodeId);
     UA_QualifiedName_copy(&browseName, &node->browseName);
     UA_String_copy(&browseName.name, &node->displayName.text);
@@ -45,6 +46,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                  UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE));
     if(res.statusCode != UA_STATUSCODE_GOOD)
         UA_VariableNode_delete(node);
+    UA_AddNodesResult_deleteMembers(&res);
     return res.statusCode;
 }
 

+ 114 - 132
src/server/ua_services_attribute.c

@@ -195,19 +195,18 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
             UA_Boolean hasRange = UA_FALSE;
             UA_NumericRange range;
             if(id->indexRange.length > 0) {
-                hasRange = UA_TRUE;
                 retval = parse_numericrange(id->indexRange, &range);
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
+                hasRange = UA_TRUE;
             }
 
             const UA_VariableNode *vn = (const UA_VariableNode*)node;
-
-            if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
+            if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
                 if(hasRange)
-                    retval |= UA_Variant_copyRange(&vn->variable.variant, &v->value, range);
+                    retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range);
                 else
-                    retval |= UA_Variant_copy(&vn->variable.variant, &v->value);
+                    retval |= UA_Variant_copy(&vn->value.variant, &v->value);
                 if(retval == UA_STATUSCODE_GOOD) {
                     v->hasValue = UA_TRUE;
                     handleSourceTimestamps(timestamps, v);
@@ -217,11 +216,11 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                 UA_DataValue_init(&val);
                 UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
                                               timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-                retval |= vn->variable.dataSource.read(vn->variable.dataSource.handle, sourceTimeStamp, &val);
+                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val);
                 if(retval == UA_STATUSCODE_GOOD) {
                     retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges
                 }
-                vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
+                vn->value.dataSource.release(vn->value.dataSource.handle, &val);
             }
 
             if(hasRange)
@@ -229,31 +228,24 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
         }
         break;
 
-    case UA_ATTRIBUTEID_DATATYPE:
+    case UA_ATTRIBUTEID_DATATYPE: {
 		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
-		v->hasValue = UA_TRUE;
-		if(node->nodeClass == UA_NODECLASS_VARIABLETYPE){
-			retval |= UA_Variant_setScalarCopy(&v->value,
-											  &((const UA_VariableTypeNode *)node)->value.type->typeId,
-											  &UA_TYPES[UA_TYPES_NODEID]);
-		} else {
-			const UA_VariableNode *vn = (const UA_VariableNode*)node;
-			if(vn->variableType == UA_VARIABLENODETYPE_VARIANT)
-				retval |= UA_Variant_setScalarCopy(&v->value, &vn->variable.variant.type->typeId,
-												  &UA_TYPES[UA_TYPES_NODEID]);
-			else {
-				UA_DataValue val;
-				UA_DataValue_init(&val);
-				retval |= vn->variable.dataSource.read(vn->variable.dataSource.handle, UA_FALSE, &val);
-				if(retval != UA_STATUSCODE_GOOD)
-					break;
-				retval |= UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId,
-												  &UA_TYPES[UA_TYPES_NODEID]);
-				vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
-				if(retval != UA_STATUSCODE_GOOD)
-					break;
-			}
-		}
+        const UA_VariableNode *vn = (const UA_VariableNode*)node;
+        if(vn->valueSource == UA_VALUESOURCE_VARIANT)
+            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId,
+                                              &UA_TYPES[UA_TYPES_NODEID]);
+        else {
+            UA_DataValue val;
+            UA_DataValue_init(&val);
+            retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
+            if(retval != UA_STATUSCODE_GOOD)
+                break;
+            retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+            vn->value.dataSource.release(vn->value.dataSource.handle, &val);
+        }
+        if(retval == UA_STATUSCODE_GOOD)
+            v->hasValue = UA_TRUE;
+        }
         break;
 
     case UA_ATTRIBUTEID_VALUERANK:
@@ -266,21 +258,17 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         {
-        	//TODO: handle indexRange
-            if(node->nodeClass == UA_NODECLASS_VARIABLETYPE)
-                break;
-                
             const UA_VariableNode *vn = (const UA_VariableNode *)node;
-            if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
-                retval = UA_Variant_setArrayCopy(&v->value, vn->variable.variant.arrayDimensions,
-                                                 vn->variable.variant.arrayDimensionsSize,
+            if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
+                retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.arrayDimensions,
+                                                 vn->value.variant.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->variable.dataSource.read(vn->variable.dataSource.handle, UA_FALSE, &val);
+                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
                 if(!val.hasValue)
@@ -288,7 +276,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                 else
                     retval = UA_Variant_setArrayCopy(&v->value, val.value.arrayDimensions,
                                                      val.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
-                vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
+                vn->value.dataSource.release(vn->value.dataSource.handle, &val);
             }
         }
         break;
@@ -440,6 +428,10 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 static 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;
+
     // we might repeat writing, e.g. when the node got replaced mid-work
     UA_Boolean done = UA_FALSE;
     while(!done) {
@@ -462,104 +454,95 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
         case UA_ATTRIBUTEID_EVENTNOTIFIER:
             retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
             break;
-        case UA_ATTRIBUTEID_VALUE:
-            if(node->nodeClass == UA_NODECLASS_VARIABLE) {
-                const UA_VariableNode *vn = (const UA_VariableNode*)node;
-                if(vn->variableType == UA_VARIABLENODETYPE_DATASOURCE) {
-                    if(!vn->variable.dataSource.write) {
-                        retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-                        break;
-                    }
-                    retval = vn->variable.dataSource.write(vn->variable.dataSource.handle, &wvalue->value.value);
-                    done = UA_TRUE;
-                    break;
-                }
-
-                // array sizes are not compared
+        case UA_ATTRIBUTEID_VALUE: {
+            if(node->nodeClass != UA_NODECLASS_VARIABLE &&
+               node->nodeClass != UA_NODECLASS_VARIABLETYPE) {
+                retval = UA_STATUSCODE_BADTYPEMISMATCH;
+                break;
+            }
 
-                if(!wvalue->value.hasValue) {
-                    retval = UA_STATUSCODE_BADTYPEMISMATCH;
+            /* 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;
-                }
-
-                if(!UA_NodeId_equal(&vn->variable.variant.type->typeId, &wvalue->value.value.type->typeId)) {
-                    if(vn->variable.variant.type->namespaceZero && wvalue->value.value.type->namespaceZero &&
-                       vn->variable.variant.type->typeIndex == wvalue->value.value.type->typeIndex)
-                        // an enum was sent as an int32, or an opaque type as a bytestring
-                        wvalue->value.value.type = vn->variable.variant.type;
-                    else if(vn->variable.variant.type == &UA_TYPES[UA_TYPES_BYTE] &&
-                            (!vn->variable.variant.data || vn->variable.variant.arrayLength > -1) /* isArray */ &&
-                            wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
-                            wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) {
-                        // 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;
-                        break;
-                    }
-                }
+                hasRange = UA_TRUE;
+            }
 
-                UA_VariableNode *newVn = UA_VariableNode_new();
-                if(!newVn) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                retval = UA_VariableNode_copy(vn, newVn);
-                if(retval != UA_STATUSCODE_GOOD) {
-                    UA_VariableNode_delete(newVn);
-                    break;
-                }
-                retval = UA_Variant_copy(&wvalue->value.value, &newVn->variable.variant);
-                if(retval != UA_STATUSCODE_GOOD) {
-                    UA_VariableNode_delete(newVn);
-                    break;
-                }
-                if(UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn,
-                                        UA_NULL) == UA_STATUSCODE_GOOD)
-                    done = UA_TRUE;
-                else
-                    UA_VariableNode_delete(newVn);
-            } else if(node->nodeClass == UA_NODECLASS_VARIABLETYPE) {
-                const UA_VariableTypeNode *vtn = (const UA_VariableTypeNode*)node;
-                if(!wvalue->value.hasValue) {
+            /* 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;
                     break;
                 }
-                if(!UA_NodeId_equal(&vtn->value.type->typeId, &wvalue->value.value.type->typeId)) {
-                    if(!vtn->value.type->namespaceZero || wvalue->value.value.type->namespaceZero ||
-                       vtn->value.type->typeIndex != wvalue->value.value.type->typeIndex) {
-                        retval = UA_STATUSCODE_BADTYPEMISMATCH;
-                        break;
-                    }
-                    wvalue->value.value.type = vtn->value.type;
-                }
-
-                UA_VariableTypeNode *newVtn = UA_VariableTypeNode_new();
-                if(!newVtn) {
-                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
-                    break;
-                }
-                retval = UA_VariableTypeNode_copy(vtn, newVtn);
-                if(retval != UA_STATUSCODE_GOOD) {
-                    UA_VariableTypeNode_delete(newVtn);
-                    break;
-                }
-                retval = UA_Variant_copy(&wvalue->value.value, &newVtn->value);
-                if(retval != UA_STATUSCODE_GOOD) {
-                    UA_VariableTypeNode_delete(newVtn);
+                // todo: writing ranges
+                retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value);
+                done = UA_TRUE;
+                break;
+            }
+            const UA_Variant *oldV = &vn->value.variant;
+
+            /* 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] &&
+                        (!oldV->data || vn->value.variant.arrayLength > -1) /* isArray */ &&
+                        wvalue->value.value.type == &UA_TYPES[UA_TYPES_BYTESTRING] &&
+                        wvalue->value.value.data && wvalue->value.value.arrayLength == -1 /* isScalar */) {
+                    /* 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;
                     break;
                 }
-                if(UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVtn,
-                                        UA_NULL) == UA_STATUSCODE_GOOD)
-                    done = UA_TRUE;
-                else
-                    UA_VariableTypeNode_delete(newVtn);
-            } else {
-                retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+            }
+
+            /* 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;
+                break;
+            }
+            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_setRange(&newVn->value.variant, wvalue->value.value.data, range);
+            else {
+                UA_Variant_deleteMembers(&newVn->value.variant);
+                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
+            }
+            if(retval != UA_STATUSCODE_GOOD ||
+               UA_NodeStore_replace(server->nodestore, node, (UA_Node*)newVn,
+                                    UA_NULL) != UA_STATUSCODE_GOOD)
+                goto clean_up;
+            if(hasRange)
+                UA_free(range.dimensions);
+            done = UA_TRUE;
+            break;
+
+            clean_up:
+            if(node->nodeClass == UA_NODECLASS_VARIABLE)
+                UA_VariableNode_delete(newVn);
+            else
+                UA_VariableTypeNode_delete((UA_VariableTypeNode*)newVn);
+            if(hasRange)
+                UA_free(range.dimensions);
             }
             break;
         case UA_ATTRIBUTEID_DATATYPE:
@@ -578,10 +561,9 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
             break;
         }
 
+        UA_NodeStore_release(node);
         if(retval != UA_STATUSCODE_GOOD)
             break;
-
-        UA_NodeStore_release(node);
     }
 
     return retval;

+ 1 - 1
src/server/ua_services_nodemanagement.c

@@ -66,7 +66,7 @@ static UA_StatusCode parseVariableNode(UA_ExtensionObject *attributes, UA_Node *
     /* } */
 
     if(attr.specifiedAttributes & UA_NODEATTRIBUTESMASK_VALUE) {
-        vnode->variable.variant = attr.value;
+        vnode->value.variant = attr.value;
         UA_Variant_init(&attr.value);
     }