Browse Source

Merge pull request #404 from acplt/callbacks

adding callback structure to couple with callbacks on non-datasource …
Julius Pfrommer 9 years ago
parent
commit
c1f4411d11

+ 1 - 1
examples/server.c

@@ -244,7 +244,7 @@ int main(int argc, char** argv) {
   else if (dataSourceCopy->read != dateDataSource.read)
     UA_LOG_WARNING(logger, UA_LOGCATEGORY_USERLAND, "The returned dataSource is not the same as we set?");
   else
-    UA_Server_setAttribute_DataSource(server, nodeId_currentTime, dataSourceCopy);
+    UA_Server_setAttribute_DataSource(server, nodeId_currentTime, *dataSourceCopy);
   free(dataSourceCopy);
 #ifndef _WIN32
   //cpu temperature monitoring for linux machines

+ 11 - 0
examples/server_variable.c

@@ -22,6 +22,14 @@ static void stopHandler(int sign) {
     running = 0;
 }
 
+static void onRead(void *handle, const UA_NodeId nodeid,  const UA_Variant *data, const UA_NumericRange *range){
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onRead; handle is: %i", (uintptr_t)handle);
+}
+
+static void onWrite(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range){
+    UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "onWrite; handle is: %i", (uintptr_t)handle);
+}
+
 int main(int argc, char** argv) {
     signal(SIGINT, stopHandler); /* catches ctrl-c */
 
@@ -44,6 +52,9 @@ int main(int argc, char** argv) {
     UA_Server_addVariableNode(server, myIntegerNodeId, myIntegerName, myIntegerBrowseName, myIntegerBrowseName, 0, 0,
                               parentNodeId, parentReferenceNodeId, myIntegerVariant, NULL);
 
+    UA_ValueCallback callback = {(void*)7, onRead, onWrite};
+    UA_Server_setAttribute_valueCallback(server, myIntegerNodeId, callback);
+
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
     UA_Server_delete(server);
 

+ 15 - 2
include/ua_server.h

@@ -103,7 +103,8 @@ typedef struct {
      * @return Returns a status code for logging. Error codes intended for the original caller are set
      *         in the value. If an error is returned, then no releasing of the value is done.
      */
-    UA_StatusCode (*read)(void *handle, const UA_NodeId nodeid,  UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range, UA_DataValue *value);
+    UA_StatusCode (*read)(void *handle, const UA_NodeId nodeid, UA_Boolean includeSourceTimeStamp,
+                          const UA_NumericRange *range, UA_DataValue *value);
 
     /**
      * Write into a data source. The write member of UA_DataSource can be empty if the operation
@@ -119,6 +120,14 @@ typedef struct {
     UA_StatusCode (*write)(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range);
 } UA_DataSource;
 
+/* Value Callbacks can be attach to value and value type nodes. If not-null, they are called before
+   reading and after writing respectively */
+typedef struct {
+    void *handle;
+    void (*onRead)(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range);
+    void (*onWrite)(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const UA_NumericRange *range);
+} UA_ValueCallback;
+
 /** @brief Add a new namespace to the server. Returns the index of the new namespace */
 UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
 
@@ -378,7 +387,11 @@ UA_Server_setAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_Meth
 #endif
 
 UA_StatusCode UA_EXPORT
-UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *value);
+UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource value);
+
+/* Succeeds only if the node contains a variant value */
+UA_StatusCode UA_EXPORT
+UA_Server_setAttribute_valueCallback(UA_Server *server, UA_NodeId nodeId, UA_ValueCallback callback);
 
 UA_StatusCode UA_EXPORT
 UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, UA_AttributeId attributeId, void **value);

+ 14 - 10
src/server/ua_nodes.c

@@ -101,7 +101,8 @@ void UA_VariableNode_init(UA_VariableNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_VARIABLE;
     p->valueSource = UA_VALUESOURCE_VARIANT;
-    UA_Variant_init(&p->value.variant);
+    UA_Variant_init(&p->value.variant.value);
+    p->value.variant.callback = (UA_ValueCallback){UA_NULL,UA_NULL,UA_NULL};
     p->valueRank = -2; // scalar or array of any dimension
     p->accessLevel = 0;
     p->userAccessLevel = 0;
@@ -119,7 +120,7 @@ UA_VariableNode * UA_VariableNode_new(void) {
 void UA_VariableNode_deleteMembers(UA_VariableNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
     if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant);
+        UA_Variant_deleteMembers(&p->value.variant.value);
 }
 
 void UA_VariableNode_delete(UA_VariableNode *p) {
@@ -132,9 +133,10 @@ UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT)
-        retval = UA_Variant_copy(&src->value.variant, &dst->value.variant);
-    else
+    if(src->valueSource == UA_VALUESOURCE_VARIANT){
+        retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        dst->value.variant.callback = src->value.variant.callback;
+    }else
         dst->value.dataSource = src->value.dataSource;
     if(retval) {
         UA_VariableNode_deleteMembers(dst);
@@ -152,7 +154,8 @@ void UA_VariableTypeNode_init(UA_VariableTypeNode *p) {
 	UA_Node_init((UA_Node*)p);
     p->nodeClass = UA_NODECLASS_VARIABLETYPE;
     p->valueSource = UA_VALUESOURCE_VARIANT;
-    UA_Variant_init(&p->value.variant);
+    UA_Variant_init(&p->value.variant.value);
+    p->value.variant.callback = (UA_ValueCallback){UA_NULL, UA_NULL, UA_NULL};
     p->valueRank = -2; // scalar or array of any dimension
     p->isAbstract = UA_FALSE;
 }
@@ -167,7 +170,7 @@ UA_VariableTypeNode * UA_VariableTypeNode_new(void) {
 void UA_VariableTypeNode_deleteMembers(UA_VariableTypeNode *p) {
     UA_Node_deleteMembers((UA_Node*)p);
     if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant);
+        UA_Variant_deleteMembers(&p->value.variant.value);
 }
 
 void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
@@ -180,9 +183,10 @@ UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_Variab
 	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
-    if(src->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_copy(&src->value.variant, &dst->value.variant);
-    else
+    if(src->valueSource == UA_VALUESOURCE_VARIANT){
+        UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        dst->value.variant.callback = src->value.variant.callback;
+    }else
         dst->value.dataSource = src->value.dataSource;
     if(retval) {
         UA_VariableTypeNode_deleteMembers(dst);

+ 8 - 2
src/server/ua_nodes.h

@@ -65,7 +65,10 @@ typedef struct {
                              n = -3:  the value can be a scalar or a one dimensional array. */
     UA_ValueSource valueSource;
     union {
-        UA_Variant variant;
+        struct {
+        UA_Variant value;
+        UA_ValueCallback callback;
+        } variant;
         UA_DataSource dataSource;
     } value;
     /* <--- similar to variabletypenodes up to there--->*/
@@ -87,7 +90,10 @@ typedef struct {
     UA_Int32 valueRank;
     UA_ValueSource valueSource;
     union {
-        UA_Variant variant;
+        struct {
+            UA_Variant value;
+            UA_ValueCallback callback;
+        } variant;
         UA_DataSource dataSource;
     } value;
     /* <--- similar to variablenodes up to there--->*/

+ 60 - 71
src/server/ua_server.c

@@ -300,7 +300,7 @@ createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
     variabletype->isAbstract = abstract;
-    variabletype->value.variant.type = &UA_TYPES[UA_TYPES_VARIANT];
+    variabletype->value.variant.value.type = &UA_TYPES[UA_TYPES_VARIANT];
     return variabletype;
 }
 
@@ -834,22 +834,20 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    namespaceArray->value.dataSource = (UA_DataSource) {.handle = server, .read = readNamespaces, .write = UA_NULL};
    namespaceArray->valueRank = 1;
    namespaceArray->minimumSamplingInterval = 1.0;
-   UA_Server_addNode(server, (UA_Node*)namespaceArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                     nodeIdHasProperty);
+   UA_Server_addNode(server, (UA_Node*)namespaceArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), nodeIdHasTypeDefinition,
                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
    UA_VariableNode *serverArray = UA_VariableNode_new();
    copyNames((UA_Node*)serverArray, "ServerArray");
    serverArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERARRAY;
-   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(server->config.Application_applicationURI);
+   serverArray->value.variant.value.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
+   serverArray->value.variant.value.arrayLength = 1;
+   serverArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
+   *(UA_String *)serverArray->value.variant.value.data = UA_STRING_ALLOC(server->config.Application_applicationURI);
    serverArray->valueRank = 1;
    serverArray->minimumSamplingInterval = 1.0;
-   UA_Server_addNode(server, (UA_Node*)serverArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
-                     nodeIdHasProperty);
+   UA_Server_addNode(server, (UA_Node*)serverArray, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasProperty);
    UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY),
                           nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE));
 
@@ -858,17 +856,16 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
    servercapablities->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES;
    UA_Server_addNode(server, (UA_Node*)servercapablities, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER),
                      nodeIdHasComponent);
-   UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES),
-                          nodeIdHasTypeDefinition,
+   UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasTypeDefinition,
                           UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE));
 
    UA_VariableNode *localeIdArray = UA_VariableNode_new();
    copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
    localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
-   localeIdArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
-   localeIdArray->value.variant.arrayLength = 1;
-   localeIdArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
-   *(UA_String *)localeIdArray->value.variant.data = UA_STRING_ALLOC("en");
+   localeIdArray->value.variant.value.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], 1);
+   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");
    localeIdArray->valueRank = 1;
    localeIdArray->minimumSamplingInterval = 1.0;
    UA_Server_addNode(server, (UA_Node*)localeIdArray,
@@ -880,9 +877,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
     maxBrowseContinuationPoints->nodeId.identifier.numeric =
         UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.data = UA_UInt16_new();
-    *((UA_UInt16*)maxBrowseContinuationPoints->value.variant.data) = MAXCONTINUATIONPOINTS;
-    maxBrowseContinuationPoints->value.variant.type = &UA_TYPES[UA_TYPES_UINT16];
+    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_Server_addNode(server, (UA_Node*)maxBrowseContinuationPoints,
                       UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasProperty);
     UA_Server_addReference(server,
@@ -909,11 +906,11 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     UA_VariableNode *serverProfileArray = UA_VariableNode_new();
     copyNames((UA_Node*)serverProfileArray, "ServerProfileArray");
     serverProfileArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY;
-    serverProfileArray->value.variant.arrayLength = profileArraySize;
-    serverProfileArray->value.variant.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], profileArraySize);
-    serverProfileArray->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+    serverProfileArray->value.variant.value.arrayLength = profileArraySize;
+    serverProfileArray->value.variant.value.data = UA_Array_new(&UA_TYPES[UA_TYPES_STRING], profileArraySize);
+    serverProfileArray->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
     for(UA_UInt16 i=0;i<profileArraySize;i++){
-        ((UA_String *)serverProfileArray->value.variant.data)[i] = profileArray[i];
+        ((UA_String *)serverProfileArray->value.variant.value.data)[i] = profileArray[i];
     }
     serverProfileArray->valueRank = 1;
     serverProfileArray->minimumSamplingInterval = 1.0;
@@ -934,8 +931,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
     UA_VariableNode *enabledFlag = UA_VariableNode_new();
      copyNames((UA_Node*)enabledFlag, "EnabledFlag");
      enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
-     enabledFlag->value.variant.data = UA_Boolean_new(); //initialized as false
-     enabledFlag->value.variant.type = &UA_TYPES[UA_TYPES_BOOLEAN];
+     enabledFlag->value.variant.value.data = UA_Boolean_new(); //initialized as false
+     enabledFlag->value.variant.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
      enabledFlag->valueRank = 1;
      enabledFlag->minimumSamplingInterval = 1.0;
      UA_Server_addNode(server, (UA_Node*)enabledFlag,
@@ -956,9 +953,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
      UA_VariableNode *starttime = UA_VariableNode_new();
       copyNames((UA_Node*)starttime, "StartTime");
       starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
-      starttime->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
-      starttime->value.variant.data = &server->startTime;
-      starttime->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+      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_Server_addNode(server, (UA_Node*)starttime, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                         nodeIdHasComponent);
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
@@ -970,8 +967,7 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
       currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
       currenttime->value.dataSource = (UA_DataSource) {.handle = NULL, .read = readCurrentTime, .write = UA_NULL};
       UA_Server_addNode(server, (UA_Node*)currenttime,
-                        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-                        nodeIdHasComponent);
+                        UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
                              nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
@@ -980,9 +976,9 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
       *stateEnum = UA_SERVERSTATE_RUNNING;
       copyNames((UA_Node*)state, "State");
       state->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERSTATUS_STATE;
-      state->value.variant.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
-      state->value.variant.arrayLength = -1;
-      state->value.variant.data = stateEnum; // points into the other object.
+      state->value.variant.value.type = &UA_TYPES[UA_TYPES_SERVERSTATE];
+      state->value.variant.value.arrayLength = -1;
+      state->value.variant.value.data = stateEnum; // points into the other object.
       UA_Server_addNode(server, (UA_Node*)state, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                         nodeIdHasComponent);
       UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
@@ -991,36 +987,33 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
       UA_VariableNode *buildinfo = UA_VariableNode_new();
        copyNames((UA_Node*)buildinfo, "BuildInfo");
        buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
-       buildinfo->value.variant.data = UA_BuildInfo_new();
-       buildinfo->value.variant.type = &UA_TYPES[UA_TYPES_BUILDINFO];
-       getBulidInfo(server, (UA_BuildInfo*)buildinfo->value.variant.data);
+       buildinfo->value.variant.value.data = UA_BuildInfo_new();
+       buildinfo->value.variant.value.type = &UA_TYPES[UA_TYPES_BUILDINFO];
+       getBulidInfo(server, (UA_BuildInfo*)buildinfo->value.variant.value.data);
        UA_Server_addNode(server, (UA_Node*)buildinfo,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
                               nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE));
 
        UA_VariableNode *producturi = UA_VariableNode_new();
        copyNames((UA_Node*)producturi, "ProductUri");
        producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
-       producturi->value.variant.data = UA_String_new();
-       *((UA_String*)producturi->value.variant.data) = UA_STRING_ALLOC(PRODUCT_URI);
-       producturi->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       producturi->value.variant.value.data = UA_String_new();
+       *((UA_String*)producturi->value.variant.value.data) = UA_STRING_ALLOC(PRODUCT_URI);
+       producturi->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
        UA_Server_addNode(server, (UA_Node*)producturi,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
        UA_VariableNode *manufacturername = UA_VariableNode_new();
        copyNames((UA_Node*)manufacturername, "ManufacturererName");
        manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
-       manufacturername->value.variant.data = UA_String_new();
-       *((UA_String*)manufacturername->value.variant.data) = UA_STRING_ALLOC(MANUFACTURER_NAME);
-       manufacturername->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       manufacturername->value.variant.value.data = UA_String_new();
+       *((UA_String*)manufacturername->value.variant.value.data) = UA_STRING_ALLOC(MANUFACTURER_NAME);
+       manufacturername->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
        UA_Server_addNode(server, (UA_Node*)manufacturername,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
@@ -1028,24 +1021,22 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
        UA_VariableNode *productname = UA_VariableNode_new();
        copyNames((UA_Node*)productname, "ProductName");
        productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
-       productname->value.variant.data = UA_String_new();
-       *((UA_String*)productname->value.variant.data) = UA_STRING_ALLOC(PRODUCT_NAME);
-       productname->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       productname->value.variant.value.data = UA_String_new();
+       *((UA_String*)productname->value.variant.value.data) = UA_STRING_ALLOC(PRODUCT_NAME);
+       productname->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
        UA_Server_addNode(server, (UA_Node*)productname,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
        UA_VariableNode *softwareversion = UA_VariableNode_new();
        copyNames((UA_Node*)softwareversion, "SoftwareVersion");
        softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
-       softwareversion->value.variant.data = UA_String_new();
-       *((UA_String*)softwareversion->value.variant.data) = UA_STRING_ALLOC(SOFTWARE_VERSION);
-       softwareversion->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       softwareversion->value.variant.value.data = UA_String_new();
+       *((UA_String*)softwareversion->value.variant.value.data) = UA_STRING_ALLOC(SOFTWARE_VERSION);
+       softwareversion->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
        UA_Server_addNode(server, (UA_Node*)softwareversion,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server,
                               UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
@@ -1053,32 +1044,30 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
        UA_VariableNode *buildnumber = UA_VariableNode_new();
        copyNames((UA_Node*)buildnumber, "BuildNumber");
        buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
-       buildnumber->value.variant.data = UA_String_new();
-       *((UA_String*)buildnumber->value.variant.data) = UA_STRING_ALLOC(BUILD_NUMBER);
-       buildnumber->value.variant.type = &UA_TYPES[UA_TYPES_STRING];
+       buildnumber->value.variant.value.data = UA_String_new();
+       *((UA_String*)buildnumber->value.variant.value.data) = UA_STRING_ALLOC(BUILD_NUMBER);
+       buildnumber->value.variant.value.type = &UA_TYPES[UA_TYPES_STRING];
        UA_Server_addNode(server, (UA_Node*)buildnumber,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
        UA_VariableNode *builddate = UA_VariableNode_new();
        copyNames((UA_Node*)builddate, "BuildDate");
        builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
-       builddate->value.variant.storageType = UA_VARIANT_DATA_NODELETE;
-       builddate->value.variant.data = &server->buildDate;
-       builddate->value.variant.type = &UA_TYPES[UA_TYPES_DATETIME];
+       builddate->value.variant.value.storageType = UA_VARIANT_DATA_NODELETE;
+       builddate->value.variant.value.data = &server->buildDate;
+       builddate->value.variant.value.type = &UA_TYPES[UA_TYPES_DATETIME];
        UA_Server_addNode(server, (UA_Node*)builddate,
-                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
-                         nodeIdHasComponent);
+                         UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
                               nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype);
 
        UA_VariableNode *secondstillshutdown = UA_VariableNode_new();
        copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
        secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
-       secondstillshutdown->value.variant.data = UA_UInt32_new();
-       secondstillshutdown->value.variant.type = &UA_TYPES[UA_TYPES_UINT32];
+       secondstillshutdown->value.variant.value.data = UA_UInt32_new();
+       secondstillshutdown->value.variant.value.type = &UA_TYPES[UA_TYPES_UINT32];
        UA_Server_addNode(server, (UA_Node*)secondstillshutdown,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN),
@@ -1087,8 +1076,8 @@ UA_Server * UA_Server_new(UA_ServerConfig config) {
        UA_VariableNode *shutdownreason = UA_VariableNode_new();
        copyNames((UA_Node*)shutdownreason, "ShutdownReason");
        shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
-       shutdownreason->value.variant.data = UA_LocalizedText_new();
-       shutdownreason->value.variant.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
+       shutdownreason->value.variant.value.data = UA_LocalizedText_new();
+       shutdownreason->value.variant.value.type = &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
        UA_Server_addNode(server, (UA_Node*)shutdownreason,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
        UA_Server_addReference(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON),

+ 68 - 33
src/server/ua_server_addressspace.c

@@ -146,16 +146,13 @@ UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId, UA_Nod
 
 UA_StatusCode
 UA_Server_addVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                          const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
+                          const UA_LocalizedText displayName, const UA_LocalizedText description,
+                          const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
                           const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                          UA_Variant *value,
-
-                          UA_NodeId *createdNodeId) {
+                          UA_Variant *value, UA_NodeId *createdNodeId) {
     UA_VariableNode *node = UA_VariableNode_new();
     UA_StatusCode retval;
-    node->value.variant = *value; // copy content
+    node->value.variant.value = *value; // copy content
     UA_NodeId_copy(&nodeId, &node->nodeId);
     UA_QualifiedName_copy(&browseName, &node->browseName);
     UA_LocalizedText_copy(&displayName, &node->displayName);
@@ -171,7 +168,7 @@ UA_Server_addVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_Qu
                            UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric));
     
     if(res.statusCode != UA_STATUSCODE_GOOD) {
-        UA_Variant_init(&node->value.variant);
+        UA_Variant_init(&node->value.variant.value);
         UA_VariableNode_delete(node);
     } else 
         UA_free(value);
@@ -185,13 +182,10 @@ UA_Server_addVariableNode(UA_Server *server, const UA_NodeId nodeId, const UA_Qu
 
 UA_StatusCode
 UA_Server_addObjectNode(UA_Server *server, const UA_NodeId nodeId, const UA_QualifiedName browseName,
-                        const UA_LocalizedText displayName, const UA_LocalizedText description, const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
-
+                        const UA_LocalizedText displayName, const UA_LocalizedText description,
+                        const UA_UInt32 userWriteMask, const UA_UInt32 writeMask,
                         const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
-
-                        const UA_ExpandedNodeId typeDefinition,
-
-                        UA_NodeId *createdNodeId){
+                        const UA_ExpandedNodeId typeDefinition, UA_NodeId *createdNodeId){
     UA_ObjectNode *node = UA_ObjectNode_new();
     UA_StatusCode retval;
     
@@ -271,7 +265,7 @@ UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId nodeId, const U
         UA_NodeId *createdNodeId) {
     UA_VariableTypeNode *node = UA_VariableTypeNode_new();
     UA_StatusCode retval;
-    node->value.variant = *value; // copy content
+    node->value.variant.value = *value; // copy content
     UA_NodeId_copy(&nodeId, &node->nodeId);
     UA_QualifiedName_copy(&browseName, &node->browseName);
     UA_LocalizedText_copy(&displayName, &node->displayName);
@@ -289,7 +283,7 @@ UA_Server_addVariableTypeNode(UA_Server *server, const UA_NodeId nodeId, const U
                            UA_EXPANDEDNODEID_NUMERIC(0, value->type->typeId.identifier.numeric));
     
     if(res.statusCode != UA_STATUSCODE_GOOD) {
-        UA_Variant_init(&node->value.variant);
+        UA_Variant_init(&node->value.variant.value);
         UA_VariableTypeNode_delete(node);
     } else 
         UA_free(value);
@@ -857,7 +851,7 @@ UA_Server_addMethodNode(UA_Server* server, const UA_NodeId nodeId, const UA_Qual
     inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
     inputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
     inputArgumentsVariableNode->valueRank = 1;
-    UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant, inputArguments,
+    UA_Variant_setArrayCopy(&inputArgumentsVariableNode->value.variant.value, inputArguments,
                             inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
     addRes = UA_Server_addNode(server, (UA_Node*)inputArgumentsVariableNode, methodExpandedNodeId,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
@@ -878,7 +872,7 @@ UA_Server_addMethodNode(UA_Server* server, const UA_NodeId nodeId, const UA_Qual
     outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
     outputArgumentsVariableNode->description = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
     outputArgumentsVariableNode->valueRank = 1;
-    UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant, outputArguments,
+    UA_Variant_setArrayCopy(&outputArgumentsVariableNode->value.variant.value, outputArguments,
                             outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
     addRes = UA_Server_addNode(server, (UA_Node*)outputArgumentsVariableNode, methodExpandedNodeId,
                                UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY));
@@ -1025,8 +1019,8 @@ UA_StatusCode UA_Server_setAttributeValue(UA_Server *server, UA_NodeId nodeId, U
         return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
       }
       nVariant = value;
-      UA_Variant_deleteMembers(&anyTypeNode.vObj->value.variant);
-      UA_Variant_copy(nVariant, &anyTypeNode.vObj->value.variant);
+      UA_Variant_deleteMembers(&anyTypeNode.vObj->value.variant.value);
+      UA_Variant_copy(nVariant, &anyTypeNode.vObj->value.variant.value);
       break;
     case UA_ATTRIBUTEID_DATATYPE:
       UA_Server_deleteNodeCopy(server, (void **) &anyTypeNode.node);
@@ -1123,7 +1117,7 @@ UA_Server_setAttribute_method(UA_Server *server, UA_NodeId methodNodeId, UA_Meth
 #endif
 
 UA_StatusCode
-UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource *value) {
+UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSource value) {
   union {
     UA_Node *anyNode;
     UA_VariableNode *varNode;
@@ -1142,21 +1136,18 @@ UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSo
   
   if (node.anyNode->nodeClass == UA_NODECLASS_VARIABLE) {
     if (node.varNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      UA_Variant_deleteMembers(&node.varNode->value.variant);
+      UA_Variant_deleteMembers(&node.varNode->value.variant.value);
     }
     node.varNode->valueSource = UA_VALUESOURCE_DATASOURCE;
-    node.varNode->value.dataSource.handle = value->handle;
-    node.varNode->value.dataSource.read   = value->read;
-    node.varNode->value.dataSource.write  = value->write;
+    node.varNode->value.dataSource = value;
   }
+  //UA_NODECLASS_VARIABLE_TYPE
   else {
     if (node.varTNode->valueSource == UA_VALUESOURCE_VARIANT) {
-      UA_Variant_deleteMembers(&node.varTNode->value.variant);
+      UA_Variant_deleteMembers(&node.varTNode->value.variant.value);
     }
     node.varTNode->valueSource = UA_VALUESOURCE_DATASOURCE;
-    node.varTNode->value.dataSource.handle = value->handle;
-    node.varTNode->value.dataSource.read   = value->read;
-    node.varTNode->value.dataSource.write  = value->write;
+    node.varTNode->value.dataSource = value;
   }
   
   const UA_Node **inserted = UA_NULL;
@@ -1167,6 +1158,44 @@ UA_Server_setAttribute_DataSource(UA_Server *server, UA_NodeId nodeId, UA_DataSo
   return retval;
 }
 
+UA_StatusCode
+UA_Server_setAttribute_valueCallback(UA_Server *server, UA_NodeId nodeId, UA_ValueCallback callback) {
+  union {
+    UA_Node *anyNode;
+    UA_VariableNode *varNode;
+    UA_VariableTypeNode *varTNode;
+  } node;
+  UA_StatusCode retval;
+  retval = UA_Server_getNodeCopy(server, nodeId, (void **) &node.anyNode);
+
+  if (retval != UA_STATUSCODE_GOOD || node.anyNode == UA_NULL)
+    return retval;
+
+  if (node.anyNode->nodeClass != UA_NODECLASS_VARIABLE && node.anyNode->nodeClass != UA_NODECLASS_VARIABLETYPE) {
+    UA_Server_deleteNodeCopy(server, (void **) &node);
+    return UA_STATUSCODE_BADNODECLASSINVALID;
+  }
+
+  if(node.varNode->valueSource == UA_VALUESOURCE_DATASOURCE){
+    return UA_STATUSCODE_BADNODECLASSINVALID;
+  }
+
+  if (node.anyNode->nodeClass == UA_NODECLASS_VARIABLE) {
+    node.varNode->value.variant.callback = callback;
+  }
+  //UA_NODECLASS_VARIABLE_TYPE
+  else {
+      node.varTNode->value.variant.callback = callback;
+  }
+
+  const UA_Node **inserted = UA_NULL;
+  const UA_Node *oldNode = UA_NodeStore_get(server->nodestore, &node.anyNode->nodeId);
+  retval |= UA_NodeStore_replace(server->nodestore, oldNode, node.anyNode, inserted);
+  UA_NodeStore_release(oldNode);
+
+  return retval;
+}
+
 #define SERVER_GETATTRIBUTE_COPYTYPEVALUE(TYPE, SRC) \
   *value = (void *) UA_##TYPE##_new();               \
   UA_##TYPE##_copy( SRC, (UA_##TYPE *) *value  );    \
@@ -1264,7 +1293,9 @@ UA_StatusCode UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, U
         case UA_NODECLASS_VARIABLE:
           *value = UA_Variant_new();
           if(anyTypeNode.vObj->valueSource == UA_VALUESOURCE_VARIANT) {
-            retval |= UA_Variant_copy(&anyTypeNode.vObj->value.variant, *value);
+            retval |= UA_Variant_copy(&anyTypeNode.vObj->value.variant.value, *value);
+            if(anyTypeNode.vObj->value.variant.callback.onRead)
+                anyTypeNode.vObj->value.variant.callback.onRead(anyTypeNode.vObj->value.variant.callback.handle, nodeId, *value, UA_NULL);
           } else {
             UA_DataValue ds;
             UA_DataValue_init(&ds);
@@ -1276,7 +1307,9 @@ UA_StatusCode UA_Server_getAttributeValue(UA_Server *server, UA_NodeId nodeId, U
           // Note: Precicely the same as variableNode above!
           *value = UA_Variant_new();
           if(anyTypeNode.vtObj->valueSource == UA_VALUESOURCE_VARIANT) {
-            retval |= UA_Variant_copy(&anyTypeNode.vtObj->value.variant, *value);
+            retval |= UA_Variant_copy(&anyTypeNode.vtObj->value.variant.value, *value);
+            if(anyTypeNode.vtObj->value.variant.callback.onRead)
+                anyTypeNode.vtObj->value.variant.callback.onRead(anyTypeNode.vtObj->value.variant.callback.handle, nodeId, *value, UA_NULL);
           } else {
             UA_DataValue ds;
             UA_DataValue_init(&ds);
@@ -1558,8 +1591,10 @@ void UA_Server_addInstanceOf_instatiateChildNode(UA_Server *server,
         newVarNode->valueSource = varTypeNode->valueSource;
         if (varTypeNode->valueSource == UA_VALUESOURCE_DATASOURCE)
           newVarNode->value.dataSource = varTypeNode->value.dataSource;
-        else
-          UA_Variant_copy(&varTypeNode->value.variant, &newVarNode->value.variant);
+        else{
+          newVarNode->value.variant.callback = varTypeNode->value.variant.callback;
+          UA_Variant_copy(&varTypeNode->value.variant.value, &newVarNode->value.variant.value);
+        }
         
         adres = UA_Server_addNode(server, (UA_Node *) newVarNode, *objectRootExpanded, ref.referenceTypeId);
         if (callback != UA_NULL)

+ 25 - 10
src/server/ua_services_attribute.c

@@ -210,15 +210,18 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_Rea
             }
 
             const UA_VariableNode *vn = (const UA_VariableNode*)node;
+
             if(vn->valueSource == UA_VALUESOURCE_VARIANT) {
                 if(rangeptr)
-                    retval |= UA_Variant_copyRange(&vn->value.variant, &v->value, range);
+                    retval |= UA_Variant_copyRange(&vn->value.variant.value, &v->value, range);
                 else
-                    retval |= UA_Variant_copy(&vn->value.variant, &v->value);
+                    retval |= UA_Variant_copy(&vn->value.variant.value, &v->value);
                 if(retval == UA_STATUSCODE_GOOD) {
                     v->hasValue = UA_TRUE;
                     handleSourceTimestamps(timestamps, v);
                 }
+                if(vn->value.variant.callback.onRead)
+                    vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId, &v->value, rangeptr);
             } else {
                 UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE || timestamps == UA_TIMESTAMPSTORETURN_BOTH);
                 retval |= vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, sourceTimeStamp, rangeptr, v);
@@ -232,9 +235,11 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_Rea
     case UA_ATTRIBUTEID_DATATYPE: {
 		CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         const UA_VariableNode *vn = (const UA_VariableNode*)node;
-        if(vn->valueSource == UA_VALUESOURCE_VARIANT)
-            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
-        else {
+        if(vn->valueSource == UA_VALUESOURCE_VARIANT){
+            retval = UA_Variant_setScalarCopy(&v->value, &vn->value.variant.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
+            if(vn->value.variant.callback.onRead)
+                vn->value.variant.callback.onRead(vn->value.variant.callback.handle, vn->nodeId, &vn->value.variant.value, UA_NULL);
+        } else {
             UA_DataValue val;
             UA_DataValue_init(&val);
             retval = vn->value.dataSource.read(vn->value.dataSource.handle, vn->nodeId, UA_FALSE, UA_NULL, &val);
@@ -260,7 +265,7 @@ void readValue(UA_Server *server, UA_TimestampsToReturn timestamps, const UA_Rea
         {
             const UA_VariableNode *vn = (const UA_VariableNode *)node;
             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]);
+                retval = UA_Variant_setArrayCopy(&v->value, vn->value.variant.value.arrayDimensions, vn->value.variant.value.arrayDimensionsSize, &UA_TYPES[UA_TYPES_INT32]);
                 if(retval == UA_STATUSCODE_GOOD)
                     v->hasValue = UA_TRUE;
             } else {
@@ -557,7 +562,7 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                 done = UA_TRUE;
                 goto clean_up_range;
             }
-            const UA_Variant *oldV = &vn->value.variant;
+            const UA_Variant *oldV = &vn->value.variant.value;
 
             /* the nodeid on the wire may be != the nodeid in the node: opaque types, enums and bytestrings */
             if(!UA_NodeId_equal(&oldV->type->typeId, &wvalue->value.value.type->typeId)) {
@@ -596,15 +601,25 @@ UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                 
             /* insert the new value */
             if(hasRange)
-                retval = UA_Variant_setRangeCopy(&newVn->value.variant, wvalue->value.value.data,
+                retval = UA_Variant_setRangeCopy(&newVn->value.variant.value, wvalue->value.value.data,
                                                  wvalue->value.value.arrayLength, range);
             else {
-                UA_Variant_deleteMembers(&newVn->value.variant);
-                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant);
+                UA_Variant_deleteMembers(&newVn->value.variant.value);
+                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value.variant.value);
             }
 
             if(retval == UA_STATUSCODE_GOOD && UA_NodeStore_replace(server->nodestore, node,
                                                    (UA_Node*)newVn, UA_NULL) == UA_STATUSCODE_GOOD) {
+                //trigger onWrite
+                if(vn->value.variant.callback.onWrite){
+                    if(hasRange)
+                        vn->value.variant.callback.onWrite(vn->value.variant.callback.handle,
+                                                           wvalue->nodeId, &wvalue->value.value, &range);
+                    else
+                        vn->value.variant.callback.onWrite(vn->value.variant.callback.handle,
+                                                           wvalue->nodeId, &wvalue->value.value, UA_NULL);
+                }
+
                 done = UA_TRUE;
                 goto clean_up_range;
             }

+ 10 - 10
src/server/ua_services_call.c

@@ -73,12 +73,12 @@ static UA_StatusCode statisfySignature(UA_Variant *var, UA_Argument arg) {
 }
 
 static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_VariableNode *argDefinition) {
-    if(argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
-        argDefinition->value.variant.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
+    if(argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_ARGUMENT] &&
+        argDefinition->value.variant.value.type != &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
         return UA_STATUSCODE_BADINTERNALERROR;
-    if(rs->inputArgumentsSize < argDefinition->value.variant.arrayLength) 
+    if(rs->inputArgumentsSize < argDefinition->value.variant.value.arrayLength)
         return UA_STATUSCODE_BADARGUMENTSMISSING;
-    if(rs->inputArgumentsSize > argDefinition->value.variant.arrayLength)
+    if(rs->inputArgumentsSize > argDefinition->value.variant.value.arrayLength)
         return UA_STATUSCODE_BADINVALIDARGUMENT;
     
     const UA_ExtensionObject *thisArgDefExtObj;
@@ -89,16 +89,16 @@ static UA_StatusCode argConformsToDefinition(UA_CallMethodRequest *rs, const UA_
     UA_NodeId ArgumentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ARGUMENT + UA_ENCODINGOFFSET_BINARY);
     for(int i = 0; i<rs->inputArgumentsSize; i++) {
         var = &rs->inputArguments[i];
-        if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) {
-            thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.data))[i];
+        if(argDefinition->value.variant.value.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]) {
+            thisArgDefExtObj = &((const UA_ExtensionObject *) (argDefinition->value.variant.value.data))[i];
             decodingOffset = 0;
             
             if(!UA_NodeId_equal(&ArgumentNodeId, &thisArgDefExtObj->typeId))
                 return UA_STATUSCODE_BADINTERNALERROR;
                 
             UA_decodeBinary(&thisArgDefExtObj->body, &decodingOffset, &arg, &UA_TYPES[UA_TYPES_ARGUMENT]);
-        } else if(argDefinition->value.variant.type == &UA_TYPES[UA_TYPES_ARGUMENT])
-            arg = ((UA_Argument *) argDefinition->value.variant.data)[i];
+        } else if(argDefinition->value.variant.value.type == &UA_TYPES[UA_TYPES_ARGUMENT])
+            arg = ((UA_Argument *) argDefinition->value.variant.value.data)[i];
         retval |= statisfySignature(var, arg);
     }
     return retval;
@@ -178,8 +178,8 @@ static void callMethod(UA_Server *server, UA_Session *session, UA_CallMethodRequ
     // Call method if available
     if(methodCalled->attachedMethod) {
         result->outputArguments = UA_Array_new(&UA_TYPES[UA_TYPES_VARIANT],
-                                               outputArguments->value.variant.arrayLength);
-        result->outputArgumentsSize = outputArguments->value.variant.arrayLength;
+                                               outputArguments->value.variant.value.arrayLength);
+        result->outputArgumentsSize = outputArguments->value.variant.value.arrayLength;
         result->statusCode = methodCalled->attachedMethod(withObject->nodeId, request->inputArguments,
                                                           result->outputArguments, methodCalled->methodHandle);
     }

+ 1 - 1
src/server/ua_services_nodemanagement.c

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

+ 2 - 1
src/server/ua_subscription.c

@@ -428,7 +428,8 @@ UA_Boolean MonitoredItem_CopyMonitoredValueToVariant(UA_UInt32 attributeID, cons
         if(src->nodeClass == UA_NODECLASS_VARIABLE) {
             const UA_VariableNode *vsrc = (const UA_VariableNode*)src;
             if(srcAsVariableNode->valueSource == UA_VALUESOURCE_VARIANT) {
-                UA_Variant_copy(&vsrc->value.variant, dst);
+                UA_Variant_copy(&vsrc->value.variant.value, dst);
+                //no onRead callback here since triggered by a subscription
                 samplingError = UA_FALSE;
             } else {
                 if(srcAsVariableNode->valueSource != UA_VALUESOURCE_DATASOURCE)

+ 2 - 2
tests/check_services_attributes.c

@@ -88,7 +88,7 @@ static UA_VariableNode* makeCompareSequence(void) {
         const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
 	UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
 	//UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
-	node->value.variant=*myIntegerVariant;
+	node->value.variant.value = *myIntegerVariant;
 	UA_NodeId_copy(&myIntegerNodeId,&node->nodeId);
 	UA_QualifiedName_copy(&myIntegerName,&node->browseName);
         UA_LocalizedText_copy(&myIntegerDisplName, &node->displayName);
@@ -851,7 +851,7 @@ START_TEST(WriteSingleAttributeValue)
 		const UA_Node *node = UA_NodeStore_get(server->nodestore, &wValue.nodeId);
 		ck_assert_int_eq(node->nodeClass, UA_NODECLASS_VARIABLE);
 		const UA_VariableNode *vn = (const UA_VariableNode*)node;
-		const UA_Variant *oldV = &vn->value.variant;
+		const UA_Variant *oldV = &vn->value.variant.value;
 		ck_assert_ptr_eq(&oldV->type->typeId, &wValue.value.value.type->typeId);
 
 		ck_assert_int_eq(20, *(UA_Int32* )resp.value.data);

+ 3 - 3
tools/pyUANamespace/ua_builtin_types.py

@@ -367,7 +367,7 @@ class opcua_value_t():
         code.append("UA_Variant_setArrayCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", (UA_Int32) " + str(len(self.value)) + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
         # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
         #if (bootstrapping == True):
-        #  code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+        #  code.append(self.parent.getCodePrintableID() + "->value.variant.value = *" + self.parent.getCodePrintableID() + "_variant;")
     else:
       # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
       if self.value[0].__binTypeId__ == BUILTINTYPE_TYPEID_GUID:
@@ -391,14 +391,14 @@ class opcua_value_t():
           code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(" + valueName + ");")
           # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
           #if (bootstrapping == True):
-          #  code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+          #  code.append(self.parent.getCodePrintableID() + "->value.variant.value = *" + self.parent.getCodePrintableID() + "_variant;")
         else:
           code.append("UA_" + self.value[0].stringRepresentation + " " + valueName + " = " + self.value[0].printOpen62541CCode_SubType() + ";")
           code.append("UA_Variant_setScalarCopy(" + self.parent.getCodePrintableID() + "_variant, &" + valueName + ", &UA_TYPES[UA_TYPES_" + self.value[0].stringRepresentation.upper() + "]);")
           code.append("UA_" + self.value[0].stringRepresentation + "_deleteMembers(&" + valueName + ");")
           # Variant creation is now part of printSubtypeEarly, the node does not exist when the type is initialized!
           #if (bootstrapping == True):
-          #  code.append(self.parent.getCodePrintableID() + "->value.variant = *" + self.parent.getCodePrintableID() + "_variant;")
+          #  code.append(self.parent.getCodePrintableID() + "->value.variant.value = *" + self.parent.getCodePrintableID() + "_variant;")
     return code
 
 

+ 2 - 2
tools/pyUANamespace/ua_node_types.py

@@ -1096,7 +1096,7 @@ class opcua_node_variable_t(opcua_node_t):
     code.append(self.getCodePrintableID() + "->accessLevel = (UA_Int32) " + str(self.accessLevel()) + ";")
     code.append(self.getCodePrintableID() + "->valueRank = (UA_Int32) " + str(self.valueRank()) + ";")
     # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(self.getCodePrintableID() + "->value.variant = *" + self.getCodePrintableID() + "_variant;")
+    code.append(self.getCodePrintableID() + "->value.variant.value = *" + self.getCodePrintableID() + "_variant;")
     return code
 
 class opcua_node_method_t(opcua_node_t):
@@ -1348,7 +1348,7 @@ class opcua_node_variableType_t(opcua_node_t):
       code.append(self.getCodePrintableID() + "->isAbstract = UA_FALSE;")
     
     # The variant is guaranteed to exist by SubtypeEarly()
-    code.append(self.getCodePrintableID() + "->value.variant = *" + self.getCodePrintableID() + "_variant;")
+    code.append(self.getCodePrintableID() + "->value.variant.value = *" + self.getCodePrintableID() + "_variant;")
     return code
 
 class opcua_node_dataType_t(opcua_node_t):