Browse Source

API freezing: add handling of ranges to data sources

Julius Pfrommer 9 years ago
parent
commit
9e51e84caa
6 changed files with 121 additions and 50 deletions
  1. 1 1
      examples/networklayer_tcp.c
  2. 33 6
      examples/server.c
  3. 0 2
      include/ua_log.h
  4. 46 20
      include/ua_server.h
  5. 27 11
      src/server/ua_server.c
  6. 14 10
      src/server/ua_services_attribute.c

+ 1 - 1
examples/networklayer_tcp.c

@@ -455,7 +455,7 @@ UA_ServerNetworkLayer ServerNetworkLayerTCP_new(UA_ConnectionConfig conf, UA_UIn
     WSAStartup(wVersionRequested, &wsaData);
     WSAStartup(wVersionRequested, &wsaData);
 #endif
 #endif
     UA_ServerNetworkLayer nl;
     UA_ServerNetworkLayer nl;
-    UA_ServerNetworkLayer_init(&nl);
+    memset(&nl, 0, sizeof(UA_ServerNetworkLayer));
 
 
     ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP));
     ServerNetworkLayerTCP *layer = malloc(sizeof(ServerNetworkLayerTCP));
     if(!layer){
     if(!layer){

+ 33 - 6
examples/server.c

@@ -35,7 +35,13 @@ UA_Logger logger;
 /*************************/
 /*************************/
 /* Read-only data source */
 /* Read-only data source */
 /*************************/
 /*************************/
-static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp,
+                                  const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
 	UA_DateTime *currentTime = UA_DateTime_new();
 	UA_DateTime *currentTime = UA_DateTime_new();
 	if(!currentTime)
 	if(!currentTime)
 		return UA_STATUSCODE_BADOUTOFMEMORY;
 		return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -54,7 +60,8 @@ static UA_StatusCode readTimeData(void *handle, UA_Boolean sourceTimeStamp, UA_D
 }
 }
 
 
 static void releaseTimeData(void *handle, UA_DataValue *value) {
 static void releaseTimeData(void *handle, UA_DataValue *value) {
-	UA_DateTime_delete((UA_DateTime*)value->value.data);
+    if(value->hasValue)
+        UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 }
 
 
 /*****************************/
 /*****************************/
@@ -62,7 +69,14 @@ static void releaseTimeData(void *handle, UA_DataValue *value) {
 /*      Only on Linux        */
 /*      Only on Linux        */
 /*****************************/
 /*****************************/
 FILE* temperatureFile = NULL;
 FILE* temperatureFile = NULL;
-static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp,
+                                     const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
+
 	UA_Double* currentTemperature = UA_Double_new();
 	UA_Double* currentTemperature = UA_Double_new();
 
 
 	if(!currentTemperature)
 	if(!currentTemperature)
@@ -87,7 +101,8 @@ static UA_StatusCode readTemperature(void *handle, UA_Boolean sourceTimeStamp, U
 }
 }
 
 
 static void releaseTemperature(void *handle, UA_DataValue *value) {
 static void releaseTemperature(void *handle, UA_DataValue *value) {
-	UA_Double_delete((UA_Double*)value->value.data);
+    if(value->hasValue)
+        UA_Double_delete((UA_Double*)value->value.data);
 }
 }
 
 
 /*************************/
 /*************************/
@@ -100,7 +115,14 @@ FILE* triggerFile = NULL;
 FILE* ledFile = NULL;
 FILE* ledFile = NULL;
 UA_Boolean ledStatus = 0;
 UA_Boolean ledStatus = 0;
 
 
-static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp,
+                                   const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
+
 	/* In order to reduce blocking time, we could alloc memory for every read
 	/* In order to reduce blocking time, we could alloc memory for every read
        and return a copy of the data. */
        and return a copy of the data. */
 #ifdef UA_MULTITHREADING
 #ifdef UA_MULTITHREADING
@@ -120,6 +142,8 @@ static UA_StatusCode readLedStatus(void *handle, UA_Boolean sourceTimeStamp, UA_
 }
 }
 
 
 static void releaseLedStatus(void *handle, UA_DataValue *value) {
 static void releaseLedStatus(void *handle, UA_DataValue *value) {
+    if(!value->hasValue)
+        return;
 	/* If we allocated memory for a specific read, free the content of the
 	/* If we allocated memory for a specific read, free the content of the
        variantdata. */
        variantdata. */
 	value->value.arrayLength = -1;
 	value->value.arrayLength = -1;
@@ -129,7 +153,10 @@ static void releaseLedStatus(void *handle, UA_DataValue *value) {
 #endif
 #endif
 }
 }
 
 
-static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data) {
+static UA_StatusCode writeLedStatus(void *handle, const UA_Variant *data, const UA_NumericRange *range) {
+    if(range)
+        return UA_STATUSCODE_BADINDEXRANGEINVALID;
+    
 #ifdef UA_MULTITHREADING
 #ifdef UA_MULTITHREADING
 	pthread_rwlock_wrlock(&writeLock);
 	pthread_rwlock_wrlock(&writeLock);
 #endif
 #endif

+ 0 - 2
include/ua_log.h

@@ -23,8 +23,6 @@ extern "C" {
 #include "ua_config.h"
 #include "ua_config.h"
 
 
 /**
 /**
- * @ingroup server
- *
  * @defgroup logging Logging
  * @defgroup logging Logging
  *
  *
  * @brief Custom logging solutions can be "plugged in" with this interface
  * @brief Custom logging solutions can be "plugged in" with this interface

+ 46 - 20
include/ua_server.h

@@ -55,7 +55,7 @@ void UA_EXPORT UA_Server_delete(UA_Server *server);
 
 
 /** Sets the logger used by the server */
 /** Sets the logger used by the server */
 void UA_EXPORT UA_Server_setLogger(UA_Server *server, UA_Logger logger);
 void UA_EXPORT UA_Server_setLogger(UA_Server *server, UA_Logger logger);
-UA_Logger UA_EXPORT * UA_Server_getLogger(UA_Server *server);
+UA_Logger UA_EXPORT UA_Server_getLogger(UA_Server *server);
 
 
 /**
 /**
  * Runs the main loop of the server. In each iteration, this calls into the
  * Runs the main loop of the server. In each iteration, this calls into the
@@ -73,22 +73,53 @@ UA_Logger UA_EXPORT * UA_Server_getLogger(UA_Server *server);
  */
  */
 UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
 UA_StatusCode UA_EXPORT UA_Server_run(UA_Server *server, UA_UInt16 nThreads, UA_Boolean *running);
 
 
-/** @brief A datasource is the interface to interact with a local data provider.
- *
- * Implementors of datasources need to provide functions for the callbacks in
- * this structure. After every read, the handle needs to be released to indicate
- * that the pointer is no longer accessed. As a rule, datasources are never
- * copied, but only their content. The only way to write into a datasource is
- * via the write-service.
+/**
+ * Datasources are the interface to local data providers. Implementors of datasources need to
+ * provide functions for the callbacks in this structure. After every read, the handle needs to be
+ * released to indicate that the pointer is no longer accessed. As a rule, datasources are never
+ * copied, but only their content. The only way to write into a datasource is via the write-service.
  *
  *
  * It is expected that the read and release callbacks are implemented. The write
  * It is expected that the read and release callbacks are implemented. The write
  * callback can be set to null.
  * callback can be set to null.
- **/
+ */
 typedef struct {
 typedef struct {
-    void *handle;
-    UA_StatusCode (*read)(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value);
+    void *handle; ///> A custom pointer to reuse the same datasource functions for multiple sources
+
+    /**
+     * Read current data from the data source
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param includeSourceTimeStamp If true, then the datasource is expected to set the source
+     *        timestamp in the returned value
+     * @param range If not null, then the datasource shall return only a selection of the (nonscalar)
+     *        data. Set UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not apply.
+     * @param value The (non-null) DataValue that is returned to the client. The data source sets the
+     *        read data, the result status and optionally a sourcetimestamp.
+     * @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, UA_Boolean includeSourceTimeStamp, const UA_NumericRange *range,
+                          UA_DataValue *value);
+
+    /**
+     * Release data that was allocated during a read (and/or release locks in the data source)
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param value The DataValue that was used for a successful read.
+     */
     void (*release)(void *handle, UA_DataValue *value);
     void (*release)(void *handle, UA_DataValue *value);
-    UA_StatusCode (*write)(void *handle, const UA_Variant *data);
+
+    /**
+     * Write into a data source. The write member of UA_DataSource can be empty if the operation
+     * is unsupported.
+     *
+     * @param handle An optional pointer to user-defined data for the specific data source
+     * @param data The data to be written into the data source
+     * @param range An optional data range. If the data source is scalar or does not support writing
+     *        of ranges, then an error code is returned.
+     * @return Returns a status code that is returned to the user
+     */
+    UA_StatusCode (*write)(void *handle, const UA_Variant *data, const UA_NumericRange *range);
 } UA_DataSource;
 } UA_DataSource;
 
 
 /** @brief Add a new namespace to the server. Returns the index of the new namespace */
 /** @brief Add a new namespace to the server. Returns the index of the new namespace */
@@ -103,15 +134,15 @@ UA_Server_addVariableNode(UA_Server *server, UA_Variant *value, const UA_Qualifi
 
 
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName,
 UA_Server_addObjectNode(UA_Server *server, const UA_QualifiedName browseName,
-                          UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId, const UA_NodeId typeDefinition);
+                        UA_NodeId nodeId, const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,
+                        const UA_NodeId typeDefinition);
 
 
 UA_StatusCode UA_EXPORT
 UA_StatusCode UA_EXPORT
 UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
 UA_Server_addDataSourceVariableNode(UA_Server *server, UA_DataSource dataSource,
                                     const UA_QualifiedName browseName, UA_NodeId nodeId,
                                     const UA_QualifiedName browseName, UA_NodeId nodeId,
                                     const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId);
                                     const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId);
 
 
-/** Work that is run in the main loop (singlethreaded) or dispatched to a worker
-    thread. */
+/** Work that is run in the main loop (singlethreaded) or dispatched to a worker thread */
 typedef struct UA_WorkItem {
 typedef struct UA_WorkItem {
     enum {
     enum {
         UA_WORKITEMTYPE_NOTHING,
         UA_WORKITEMTYPE_NOTHING,
@@ -228,11 +259,6 @@ typedef struct {
     UA_String* discoveryUrl;
     UA_String* discoveryUrl;
 } UA_ServerNetworkLayer;
 } UA_ServerNetworkLayer;
 
 
-/**
- * Initializes server network layer with save values
- */
-void UA_EXPORT UA_ServerNetworkLayer_init(UA_ServerNetworkLayer *nl);
-
 /**
 /**
  * Adds a network layer to the server. The network layer is destroyed together
  * Adds a network layer to the server. The network layer is destroyed together
  * with the server. Do not use it after adding it as it might be moved around on
  * with the server. Do not use it after adding it as it might be moved around on

+ 27 - 11
src/server/ua_server.c

@@ -33,15 +33,10 @@ static void UA_ExternalNamespace_deleteMembers(UA_ExternalNamespace *ens) {
 /* Configuration */
 /* Configuration */
 /*****************/
 /*****************/
 
 
-UA_Logger * UA_Server_getLogger(UA_Server *server) {
-    return &server->logger;
+UA_Logger UA_Server_getLogger(UA_Server *server) {
+    return server->logger;
 }
 }
 
 
-void UA_ServerNetworkLayer_init(UA_ServerNetworkLayer *nl){
-    memset(nl,0,sizeof(UA_ServerNetworkLayer));
-}
-
-
 void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkLayer) {
 void UA_Server_addNetworkLayer(UA_Server *server, UA_ServerNetworkLayer networkLayer) {
     UA_ServerNetworkLayer *newlayers =
     UA_ServerNetworkLayer *newlayers =
         UA_realloc(server->networkLayers, sizeof(UA_ServerNetworkLayer)*(server->networkLayersSize+1));
         UA_realloc(server->networkLayers, sizeof(UA_ServerNetworkLayer)*(server->networkLayersSize+1));
@@ -140,7 +135,13 @@ static void getBulidInfo(const UA_Server* server, UA_BuildInfo *buildInfo) {
     buildInfo->buildDate = server->buildDate;
     buildInfo->buildDate = server->buildDate;
 }
 }
 
 
-static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp,
+                                const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     UA_ServerStatusDataType *status = UA_ServerStatusDataType_new();
     status->startTime   = ((const UA_Server*)handle)->startTime;
     status->startTime   = ((const UA_Server*)handle)->startTime;
     status->currentTime = UA_DateTime_now();
     status->currentTime = UA_DateTime_now();
@@ -162,13 +163,21 @@ static UA_StatusCode readStatus(void *handle, UA_Boolean sourceTimeStamp, UA_Dat
 }
 }
 
 
 static void releaseStatus(void *handle, UA_DataValue *value) {
 static void releaseStatus(void *handle, UA_DataValue *value) {
+    if(!value->hasValue)
+        return;
     UA_ServerStatusDataType_delete((UA_ServerStatusDataType*)value->value.data);
     UA_ServerStatusDataType_delete((UA_ServerStatusDataType*)value->value.data);
     value->value.data = UA_NULL;
     value->value.data = UA_NULL;
     value->hasValue = UA_FALSE;
     value->hasValue = UA_FALSE;
     UA_DataValue_deleteMembers(value);
     UA_DataValue_deleteMembers(value);
 }
 }
 
 
-static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp, UA_DataValue *value) {
+static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp,
+                                    const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_Server *server = (UA_Server*)handle;
     UA_Server *server = (UA_Server*)handle;
     value->hasValue = UA_TRUE;
     value->hasValue = UA_TRUE;
     value->value.storageType = UA_VARIANT_DATA_NODELETE;
     value->value.storageType = UA_VARIANT_DATA_NODELETE;
@@ -185,7 +194,13 @@ static UA_StatusCode readNamespaces(void *handle, UA_Boolean sourceTimestamp, UA
 static void releaseNamespaces(void *handle, UA_DataValue *value) {
 static void releaseNamespaces(void *handle, UA_DataValue *value) {
 }
 }
 
 
-static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp, UA_DataValue *value) {
+static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp,
+                                     const UA_NumericRange *range, UA_DataValue *value) {
+    if(range) {
+        value->hasStatus = UA_TRUE;
+        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
+        return UA_STATUSCODE_GOOD;
+    }
     UA_DateTime *currentTime = UA_DateTime_new();
     UA_DateTime *currentTime = UA_DateTime_new();
     if(!currentTime)
     if(!currentTime)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -201,7 +216,8 @@ static UA_StatusCode readCurrentTime(void *handle, UA_Boolean sourceTimeStamp, U
 }
 }
 
 
 static void releaseCurrentTime(void *handle, UA_DataValue *value) {
 static void releaseCurrentTime(void *handle, UA_DataValue *value) {
-    UA_DateTime_delete((UA_DateTime*)value->value.data);
+    if(value->hasValue)
+        UA_DateTime_delete((UA_DateTime*)value->value.data);
 }
 }
 
 
 static void copyNames(UA_Node *node, char *name) {
 static void copyNames(UA_Node *node, char *name) {

+ 14 - 10
src/server/ua_services_attribute.c

@@ -5,9 +5,7 @@
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
-static UA_StatusCode
-parse_numericrange(const UA_String str, UA_NumericRange *range)
-{
+static UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
     if(str.length < 0 || str.length >= 1023)
     if(str.length < 0 || str.length >= 1023)
         return UA_STATUSCODE_BADINTERNALERROR;
         return UA_STATUSCODE_BADINTERNALERROR;
     char *cstring = UA_alloca(str.length+1);
     char *cstring = UA_alloca(str.length+1);
@@ -15,7 +13,7 @@ parse_numericrange(const UA_String str, UA_NumericRange *range)
     cstring[str.length] = 0;
     cstring[str.length] = 0;
     UA_Int32 index = 0;
     UA_Int32 index = 0;
     size_t dimensionsIndex = 0;
     size_t dimensionsIndex = 0;
-    size_t dimensionsMax = 3; // more should be uncommon
+    size_t dimensionsMax = 3; // more should be uncommon, realloc if necessary
     struct UA_NumericRangeDimension *dimensions = UA_malloc(sizeof(struct UA_NumericRangeDimension) * 3);
     struct UA_NumericRangeDimension *dimensions = UA_malloc(sizeof(struct UA_NumericRangeDimension) * 3);
     if(!dimensions)
     if(!dimensions)
         return UA_STATUSCODE_BADOUTOFMEMORY;
         return UA_STATUSCODE_BADOUTOFMEMORY;
@@ -217,11 +215,14 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
                 UA_DataValue_init(&val);
                 UA_DataValue_init(&val);
                 UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
                 UA_Boolean sourceTimeStamp = (timestamps == UA_TIMESTAMPSTORETURN_SOURCE ||
                                               timestamps == UA_TIMESTAMPSTORETURN_BOTH);
                                               timestamps == UA_TIMESTAMPSTORETURN_BOTH);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &val);
+                if(hasRange)
+                    retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, &range, &val);
+                else
+                    retval |= vn->value.dataSource.read(vn->value.dataSource.handle, sourceTimeStamp, UA_NULL, &val);
                 if(retval == UA_STATUSCODE_GOOD) {
                 if(retval == UA_STATUSCODE_GOOD) {
-                    retval |= UA_DataValue_copy(&val, v); // todo: selection of indexranges
+                    retval |= UA_DataValue_copy(&val, v); // todo: still too much copying necessary!!
+                    vn->value.dataSource.release(vn->value.dataSource.handle, &val);
                 }
                 }
-                vn->value.dataSource.release(vn->value.dataSource.handle, &val);
             }
             }
 
 
             if(hasRange)
             if(hasRange)
@@ -238,7 +239,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
         else {
         else {
             UA_DataValue val;
             UA_DataValue val;
             UA_DataValue_init(&val);
             UA_DataValue_init(&val);
-            retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
+            retval = vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, UA_NULL, &val);
             if(retval != UA_STATUSCODE_GOOD)
             if(retval != UA_STATUSCODE_GOOD)
                 break;
                 break;
             retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
             retval = UA_Variant_setScalarCopy(&v->value, &val.value.type->typeId, &UA_TYPES[UA_TYPES_NODEID]);
@@ -269,7 +270,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
             } else {
             } else {
                 UA_DataValue val;
                 UA_DataValue val;
                 UA_DataValue_init(&val);
                 UA_DataValue_init(&val);
-                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, &val);
+                retval |= vn->value.dataSource.read(vn->value.dataSource.handle, UA_FALSE, UA_NULL, &val);
                 if(retval != UA_STATUSCODE_GOOD)
                 if(retval != UA_STATUSCODE_GOOD)
                     break;
                     break;
                 if(!val.hasValue)
                 if(!val.hasValue)
@@ -483,7 +484,10 @@ static UA_StatusCode writeValue(UA_Server *server, UA_WriteValue *wvalue) {
                     goto clean_up_range;
                     goto clean_up_range;
                 }
                 }
                 // todo: writing ranges
                 // todo: writing ranges
-                retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value);
+                if(hasRange)
+                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, &range);
+                else
+                    retval = vn->value.dataSource.write(vn->value.dataSource.handle, &wvalue->value.value, UA_NULL);
                 done = UA_TRUE;
                 done = UA_TRUE;
                 goto clean_up_range;
                 goto clean_up_range;
             }
             }