Selaa lähdekoodia

support writing into data sources

Julius Pfrommer 9 vuotta sitten
vanhempi
commit
1a54a765a6
3 muutettua tiedostoa jossa 153 lisäystä ja 173 poistoa
  1. 80 13
      examples/server_datasource.c
  2. 2 3
      include/ua_server.h
  3. 71 157
      src/server/ua_services_attribute.c

+ 80 - 13
examples/server_datasource.c

@@ -8,6 +8,8 @@
 #include <stdio.h>
 #include <stdlib.h> 
 #include <signal.h>
+#define __USE_XOPEN2K
+#include <pthread.h>
 
 // provided by the open62541 lib
 #include "ua_server.h"
@@ -16,8 +18,10 @@
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
-// data source
-static UA_StatusCode readTimeData(const void *handle, UA_VariantData* data) {
+/*************************/
+/* Read-only data source */
+/*************************/
+static UA_StatusCode readTimeData(const void *handle, UA_VariantData *data) {
     UA_DateTime *currentTime = UA_DateTime_new();
     if(!currentTime)
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -29,10 +33,51 @@ static UA_StatusCode readTimeData(const void *handle, UA_VariantData* data) {
     return UA_STATUSCODE_GOOD;
 }
 
-static void releaseTimeData(const void *handle, UA_VariantData* data) {
+static void releaseTimeData(const void *handle, UA_VariantData *data) {
     UA_DateTime_delete((UA_DateTime*)data->dataPtr);
 }
 
+static UA_StatusCode writeTimeData(const void *handle, const UA_VariantData *data) {
+    return UA_STATUSCODE_BADINTERNALERROR;
+}
+
+/**************************/
+/* Read/write data source */
+/**************************/
+UA_Int32 deviceStatus = 0;
+pthread_rwlock_t deviceStatusLock;
+
+static void printDeviceStatus(UA_Server *server, void *data) {
+    printf("Device Status: %i\n", deviceStatus);
+}
+
+static UA_StatusCode readDeviceStatus(const void *handle, UA_VariantData *data) {
+    /* In order to reduce blocking time, we could alloc memory for every read
+       and return a copy of the data. */
+    pthread_rwlock_rdlock(&deviceStatusLock);
+    data->arrayLength = 1;
+    data->dataPtr = &deviceStatus;
+    data->arrayDimensionsSize = -1;
+    data->arrayDimensions = UA_NULL;
+    return UA_STATUSCODE_GOOD;
+}
+
+static void releaseDeviceStatus(const void *handle, UA_VariantData *data) {
+    /* If we allocated memory for a specific read, free the content of the
+       variantdata. */
+    data->dataPtr = UA_NULL;
+    data->arrayLength = -1;
+    pthread_rwlock_unlock(&deviceStatusLock);
+}
+
+static UA_StatusCode writeDeviceStatus(const void *handle, const UA_VariantData *data) {
+    pthread_rwlock_wrlock(&deviceStatusLock);
+    if(data->dataPtr != UA_NULL)
+        deviceStatus = *(UA_Int32*)data->dataPtr;
+    pthread_rwlock_unlock(&deviceStatusLock);
+    return UA_STATUSCODE_GOOD;
+}
+
 UA_Boolean running = 1;
 
 static void stopHandler(int sign) {
@@ -42,28 +87,50 @@ static void stopHandler(int sign) {
 
 int main(int argc, char** argv) {
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
+    pthread_rwlock_init(&deviceStatusLock, 0);
 
 	UA_Server *server = UA_Server_new();
     UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
 
-    // add node with a callback to the userspace
-    UA_Variant *myDateTimeVariant = UA_Variant_new();
-    myDateTimeVariant->storageType = UA_VARIANT_DATASOURCE;
-    myDateTimeVariant->storage.datasource = (UA_VariantDataSource)
+    // add node with the datetime data source
+    UA_Variant *dateVariant = UA_Variant_new();
+    dateVariant->storageType = UA_VARIANT_DATASOURCE;
+    dateVariant->storage.datasource = (UA_VariantDataSource)
         {.handle = UA_NULL,
          .read = readTimeData,
          .release = releaseTimeData,
-         .write = (UA_StatusCode (*)(const void*, const UA_VariantData*))UA_NULL};
-    myDateTimeVariant->type = &UA_TYPES[UA_TYPES_DATETIME];
-    myDateTimeVariant->typeId = UA_NODEID_STATIC(UA_TYPES_IDS[UA_TYPES_DATETIME],0);
-    UA_QualifiedName myDateTimeName;
-    UA_QUALIFIEDNAME_ASSIGN(myDateTimeName, "the time");
-    UA_Server_addVariableNode(server, myDateTimeVariant, &UA_NODEID_NULL, &myDateTimeName,
+         .write = writeTimeData};
+    dateVariant->type = &UA_TYPES[UA_TYPES_DATETIME];
+    dateVariant->typeId = UA_NODEID_STATIC(UA_TYPES_IDS[UA_TYPES_DATETIME],0);
+    UA_QualifiedName dateName;
+    UA_QUALIFIEDNAME_ASSIGN(dateName, "the time");
+    UA_Server_addVariableNode(server, dateVariant, &UA_NODEID_NULL, &dateName,
+                              &UA_NODEID_STATIC(UA_NS0ID_OBJECTSFOLDER,0),
+                              &UA_NODEID_STATIC(UA_NS0ID_ORGANIZES,0));
+
+    // print the status every 2 sec
+    UA_WorkItem work = {.type = UA_WORKITEMTYPE_METHODCALL, .work.methodCall = {.method = printDeviceStatus, .data = UA_NULL} };
+    UA_Server_addRepeatedWorkItem(server, &work, 20000000, UA_NULL);
+
+    // add node with the device status data source
+    UA_Variant *statusVariant = UA_Variant_new();
+    statusVariant->storageType = UA_VARIANT_DATASOURCE;
+    statusVariant->storage.datasource = (UA_VariantDataSource)
+        {.handle = UA_NULL,
+         .read = readDeviceStatus,
+         .release = releaseDeviceStatus,
+         .write = writeDeviceStatus};
+    statusVariant->type = &UA_TYPES[UA_TYPES_INT32];
+    statusVariant->typeId = UA_NODEID_STATIC(UA_TYPES_IDS[UA_TYPES_INT32],0);
+    UA_QualifiedName statusName;
+    UA_QUALIFIEDNAME_ASSIGN(statusName, "device status");
+    UA_Server_addVariableNode(server, statusVariant, &UA_NODEID_NULL, &statusName,
                               &UA_NODEID_STATIC(UA_NS0ID_OBJECTSFOLDER,0),
                               &UA_NODEID_STATIC(UA_NS0ID_ORGANIZES,0));
 
     UA_StatusCode retval = UA_Server_run(server, 1, &running);
 	UA_Server_delete(server);
+    pthread_rwlock_destroy(&deviceStatusLock);
 
 	return retval;
 }

+ 2 - 3
include/ua_server.h

@@ -123,7 +123,7 @@ UA_StatusCode UA_EXPORT UA_Server_addRepeatedWorkItem(UA_Server *server, const U
                                                       UA_UInt32 interval, UA_Guid *resultWorkGuid);
 
 /** Remove timed or repeated work */
-UA_Boolean UA_EXPORT UA_Server_removeWorkItem(UA_Server *server, UA_Guid workId);
+//UA_Boolean UA_EXPORT UA_Server_removeWorkItem(UA_Server *server, UA_Guid workId);
 
 /**
  * Interface to the binary network layers. This structure is returned from the
@@ -245,8 +245,7 @@ typedef struct UA_ExternalNodeStore {
 	UA_ExternalNodeStore_delete destroy;
 } UA_ExternalNodeStore;
 
-UA_StatusCode UA_EXPORT
-UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url, UA_ExternalNodeStore *nodeStore);
+//UA_StatusCode UA_EXPORT UA_Server_addExternalNamespace(UA_Server *server, UA_UInt16 namespaceIndex, const UA_String *url, UA_ExternalNodeStore *nodeStore);
 
 /** @} */
 

+ 71 - 157
src/server/ua_services_attribute.c

@@ -277,176 +277,133 @@ void Service_Read(UA_Server *server, UA_Session *session, const UA_ReadRequest *
 #endif
 }
 
-static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *aWriteValue) {
+static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
 
-    do {
-        const UA_Node *node = UA_NodeStore_get(server->nodestore, &aWriteValue->nodeId);
+    // we might repeat writing, e.g. when the node got replaced mid-work
+    UA_Boolean done = UA_FALSE;
+    while(!done) {
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, &wvalue->nodeId);
         if(!node)
             return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
-        UA_Node *newNode;
+        UA_Node* (*newNode)(void);
+        void (*deleteNode)(UA_Node*);
+        UA_StatusCode (*copyNode)(const UA_Node*, UA_Node*);
+
         switch(node->nodeClass) {
         case UA_NODECLASS_OBJECT:
-            newNode = (UA_Node *)UA_ObjectNode_new();
-            UA_ObjectNode_copy((const UA_ObjectNode*)node, (UA_ObjectNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_ObjectNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_ObjectNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_ObjectNode_copy;
             break;
-
         case UA_NODECLASS_VARIABLE:
-            newNode = (UA_Node *)UA_VariableNode_new();
-            UA_VariableNode_copy((const UA_VariableNode*)node, (UA_VariableNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_VariableNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_VariableNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_VariableNode_copy;
             break;
-
         case UA_NODECLASS_METHOD:
-            newNode = (UA_Node *)UA_MethodNode_new();
-            UA_MethodNode_copy((const UA_MethodNode*)node, (UA_MethodNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_MethodNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_MethodNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_MethodNode_copy;
             break;
-
         case UA_NODECLASS_OBJECTTYPE:
-            newNode = (UA_Node *)UA_ObjectTypeNode_new();
-            UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)node, (UA_ObjectTypeNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_ObjectTypeNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_ObjectTypeNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_ObjectTypeNode_copy;
             break;
-
         case UA_NODECLASS_VARIABLETYPE:
-            newNode = (UA_Node *)UA_VariableTypeNode_new();
-            UA_VariableTypeNode_copy((const UA_VariableTypeNode*)node, (UA_VariableTypeNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_VariableTypeNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_VariableTypeNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_VariableTypeNode_copy;
             break;
-
         case UA_NODECLASS_REFERENCETYPE:
-            newNode = (UA_Node *)UA_ReferenceTypeNode_new();
-            UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)node, (UA_ReferenceTypeNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_ReferenceTypeNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_ReferenceTypeNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_ReferenceTypeNode_copy;
             break;
-
         case UA_NODECLASS_DATATYPE:
-            newNode = (UA_Node *)UA_DataTypeNode_new();
-            UA_DataTypeNode_copy((const UA_DataTypeNode*)node, (UA_DataTypeNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_DataTypeNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_DataTypeNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_DataTypeNode_copy;
             break;
-
         case UA_NODECLASS_VIEW:
-            newNode = (UA_Node *)UA_ViewNode_new();
-            UA_ViewNode_copy((const UA_ViewNode*)node, (UA_ViewNode *)newNode);
+            newNode = (UA_Node *(*)(void))UA_ViewNode_new;
+            deleteNode = (void (*)(UA_Node*))UA_ViewNode_delete;
+            copyNode = (UA_StatusCode (*)(const UA_Node*, UA_Node*))UA_ViewNode_copy;
             break;
-
         default:
+            UA_NodeStore_release(node);
             return UA_STATUSCODE_BADATTRIBUTEIDINVALID;
-            break;
         }
 
-        switch(aWriteValue->attributeId) {
+        switch(wvalue->attributeId) {
         case UA_ATTRIBUTEID_NODEID:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_NODECLASS:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){ } */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_BROWSENAME:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_DISPLAYNAME:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_DESCRIPTION:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_WRITEMASK:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_USERWRITEMASK:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_ISABSTRACT:
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_SYMMETRIC:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_INVERSENAME:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_CONTAINSNOLOOPS:
-            /* if(writeValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_EVENTNOTIFIER:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
             retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
             break;
-
         case UA_ATTRIBUTEID_VALUE:
-            if((newNode->nodeClass != UA_NODECLASS_VARIABLE) && (newNode->nodeClass != UA_NODECLASS_VARIABLETYPE)) {
+            if((node->nodeClass != UA_NODECLASS_VARIABLE) && (node->nodeClass != UA_NODECLASS_VARIABLETYPE)) {
                 retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
                 break;
             }
 
-            if(aWriteValue->value.hasVariant)
-                retval |= UA_Variant_copy(&aWriteValue->value.value, &((UA_VariableNode *)newNode)->value); // todo: zero-copy
-            break;
+            const UA_VariableNode *vn = (const UA_VariableNode*)node;
+            // has the wvalue a variant of the right type?
+            // array sizes are not checked yet..
+            if(!wvalue->value.hasVariant || !UA_NodeId_equal(&vn->value.typeId, &wvalue->value.value.typeId)) {
+                retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+                break;
+            }
 
-        case UA_ATTRIBUTEID_DATATYPE:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
+            if(vn->value.storageType == UA_VARIANT_DATASOURCE) {
+                retval = vn->value.storage.datasource.write(vn->value.storage.datasource.handle,
+                                                            &wvalue->value.value.storage.data);
+                done = UA_TRUE;
+            } else {
+                // could be a variable or variabletype node. They fit for the value.. member
+                UA_VariableNode *newVn = (UA_VariableNode*)newNode();
+                if(!newVn) {
+                    retval = UA_STATUSCODE_BADOUTOFMEMORY;
+                    break;
+                }
+                retval = copyNode((const UA_Node*)vn, (UA_Node*)newVn);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    deleteNode((UA_Node*)newVn);
+                    break;
+                }
+                retval = UA_Variant_copy(&wvalue->value.value, &newVn->value);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    deleteNode((UA_Node*)newVn);
+                    break;
+                }
+                if(UA_NodeStore_replace(server->nodestore,node,(UA_Node*)newVn,UA_NULL) == UA_STATUSCODE_GOOD)
+                    done = UA_TRUE;
+                else
+                    deleteNode((UA_Node*)newVn);
+            } 
             break;
-
+        case UA_ATTRIBUTEID_DATATYPE:
         case UA_ATTRIBUTEID_VALUERANK:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_ARRAYDIMENSIONS:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_ACCESSLEVEL:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_USERACCESSLEVEL:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_HISTORIZING:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_EXECUTABLE:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
-            retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
-            break;
-
         case UA_ATTRIBUTEID_USEREXECUTABLE:
-            /* if(aWriteValue->value.encodingMask == UA_DATAVALUE_ENCODINGMASK_VARIANT){} */
             retval = UA_STATUSCODE_BADWRITENOTSUPPORTED;
             break;
-
         default:
             retval = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
             break;
@@ -455,51 +412,8 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *aWriteValue) {
         if(retval != UA_STATUSCODE_GOOD)
             break;
 
-        if(UA_NodeStore_replace(server->nodestore, node, newNode, UA_NULL) == UA_STATUSCODE_GOOD) {
-            UA_NodeStore_release(node);
-            break;
-        }
-
-        /* The node was replaced in another thread. Restart. */
         UA_NodeStore_release(node);
-        switch(node->nodeClass) {
-        case UA_NODECLASS_OBJECT:
-            UA_ObjectNode_delete((UA_ObjectNode *)newNode);
-            break;
-
-        case UA_NODECLASS_VARIABLE:
-            UA_VariableNode_delete((UA_VariableNode *)newNode);
-            break;
-
-        case UA_NODECLASS_METHOD:
-            UA_MethodNode_delete((UA_MethodNode *)newNode);
-            break;
-            
-        case UA_NODECLASS_OBJECTTYPE:
-            UA_ObjectTypeNode_delete((UA_ObjectTypeNode *)newNode);
-            break;
-            
-        case UA_NODECLASS_VARIABLETYPE:
-            UA_VariableTypeNode_delete((UA_VariableTypeNode *)newNode);
-            break;
-            
-        case UA_NODECLASS_REFERENCETYPE:
-            UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *)newNode);
-            break;
-            
-        case UA_NODECLASS_DATATYPE:
-            UA_DataTypeNode_delete((UA_DataTypeNode *)newNode);
-            break;
-            
-        case UA_NODECLASS_VIEW:
-            UA_ViewNode_delete((UA_ViewNode *)newNode);
-            break;
-
-        default:
-            break;
-        }
-
-    } while(UA_TRUE);
+    }
 
     return retval;
 }