Browse Source

read indexranges

Julius Pfrommer 10 years ago
parent
commit
24e46949d2
3 changed files with 157 additions and 33 deletions
  1. 18 0
      include/ua_types.h
  2. 48 30
      src/server/ua_services_attribute.c
  3. 91 3
      src/ua_types.c

+ 18 - 0
include/ua_types.h

@@ -191,6 +191,18 @@ typedef struct {
 struct UA_DataType;
 struct UA_DataType;
 typedef struct UA_DataType UA_DataType; 
 typedef struct UA_DataType UA_DataType; 
 
 
+/** @brief NumericRanges are used select a subset in a (multidimensional) variant array.
+    NumericRange has no official type structure in the standard. Officially, it only exists as an
+    encoded string, such as "1:2,0:3,5". The colon separates min/max index and the comma separates
+    dimensions. A single value indicates a range with a single element (min==max). */
+typedef struct {
+    UA_Int32 dimensionsSize;
+    struct UA_NumericRangeDimension {
+        UA_UInt32 min;
+        UA_UInt32 max;
+    } *dimensions;
+} UA_NumericRange;
+
 /** @brief Variants store (arrays of) any data type. Either they provide a pointer to the data in
 /** @brief Variants store (arrays of) any data type. Either they provide a pointer to the data in
     memory, or functions from which the data can be accessed. Variants are replaced together with
     memory, or functions from which the data can be accessed. Variants are replaced together with
     the data they store (exception: use a data source).*/
     the data they store (exception: use a data source).*/
@@ -395,6 +407,12 @@ UA_StatusCode UA_EXPORT UA_LocalizedText_copycstring(char const *src, UA_Localiz
 
 
 /* Variant */
 /* Variant */
 
 
+/**
+ * Copy the variant, but use only a subset of the (multidimensional) array. Returns an error code if
+ * the variant is no array or if the indicated range does not fit.
+ */
+UA_StatusCode UA_EXPORT UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, UA_NumericRange range);
+
 /**
 /**
  * Set the variant to a scalar value that already resides in memory. The value takes on the
  * Set the variant to a scalar value that already resides in memory. The value takes on the
  * lifecycle of the variant and is deleted with it.
  * lifecycle of the variant and is deleted with it.

+ 48 - 30
src/server/ua_services_attribute.c

@@ -7,40 +7,37 @@
 #include "ua_nodestore.h"
 #include "ua_nodestore.h"
 #include "ua_util.h"
 #include "ua_util.h"
 
 
-typedef struct {
-    struct UA_NumericRangeDimension{
-        UA_UInt32 min;
-        UA_UInt32 max;
-    } *dimensions;
-    size_t dimensionsSize;
-} UA_NumericRange;
-
-static UA_StatusCode parse_numericrange(UA_String *str, UA_NumericRange *range) {
-    char *cstring = (char*)UA_realloc(str->data, str->length +1);
-    if(!cstring)
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    str->data = (UA_Byte*)cstring;
+static UA_StatusCode parse_numericrange(const UA_String str, UA_NumericRange *range) {
+    if(str.length < 0 || str.length >= 1023)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    char cstring[str.length];
+    UA_memcpy(cstring, str.data, str.length);
+    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
     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;
     
     
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     do {
     do {
-        UA_UInt32 min, max;
+        UA_Int32 min, max;
         UA_Int32 progress;
         UA_Int32 progress;
         UA_Int32 res = sscanf(&cstring[index], "%" SCNu32 "%n", &min, &progress);
         UA_Int32 res = sscanf(&cstring[index], "%" SCNu32 "%n", &min, &progress);
-        if(res <= 0)
+        if(res <= 0 || min < 0) {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
             break;
             break;
+        }
         index += progress;
         index += progress;
-        if(index >= str->length || cstring[index] != ':')
+        if(index >= str.length || cstring[index] == ',')
             max = min;
             max = min;
         else {
         else {
-            res = sscanf(&cstring[++index], "%" SCNu32 "%n", &max, &progress);
-            if(res <= 0)
+            res = sscanf(&cstring[index], ":%" SCNu32 "%n", &max, &progress);
+            if(res <= 0 || max < 0 || min >= max) {
+                retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
                 break;
                 break;
+            }
             index += progress;
             index += progress;
         }
         }
         
         
@@ -58,18 +55,18 @@ static UA_StatusCode parse_numericrange(UA_String *str, UA_NumericRange *range)
         dimensions[dimensionsIndex].min = min;
         dimensions[dimensionsIndex].min = min;
         dimensions[dimensionsIndex].max = max;
         dimensions[dimensionsIndex].max = max;
         dimensionsIndex++;
         dimensionsIndex++;
-    } while(index + 1 < str->length && cstring[index] == ',' && ++index);
-    
-    if(dimensionsIndex <= 0) {
+    } while(retval == UA_STATUSCODE_GOOD && index + 1 < str.length && cstring[index] == ',' && ++index);
+
+    if(retval == UA_STATUSCODE_GOOD && dimensionsIndex <= 0)
+        retval = UA_STATUSCODE_BADINDEXRANGENODATA;
+    if(retval != UA_STATUSCODE_GOOD) {
         UA_free(dimensions);
         UA_free(dimensions);
-        range->dimensions = UA_NULL;
-        range->dimensionsSize = 0;
-        return UA_STATUSCODE_BADINDEXRANGENODATA;
+        return retval;
     }
     }
         
         
     range->dimensions = dimensions;
     range->dimensions = dimensions;
     range->dimensionsSize = dimensionsIndex;
     range->dimensionsSize = dimensionsIndex;
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 }
 
 
 #define CHECK_NODECLASS(CLASS)                                  \
 #define CHECK_NODECLASS(CLASS)                                  \
@@ -191,12 +188,28 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     case UA_ATTRIBUTEID_VALUE:
     case UA_ATTRIBUTEID_VALUE:
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         CHECK_NODECLASS(UA_NODECLASS_VARIABLE | UA_NODECLASS_VARIABLETYPE);
         {
         {
-        	if(node->nodeClass == UA_NODECLASS_VARIABLE){
+        	if(node->nodeClass == UA_NODECLASS_VARIABLE) {
+                // todo: copy only selective...
+                UA_Boolean hasRange = UA_FALSE;
+                UA_NumericRange range;
+                if(id->indexRange.length > 0) {
+                    hasRange = UA_TRUE;
+                    retval = parse_numericrange(id->indexRange, &range);
+                    if(retval != UA_STATUSCODE_GOOD)
+                        break;
+                }
+
 				const UA_VariableNode *vn = (const UA_VariableNode*)node;
 				const UA_VariableNode *vn = (const UA_VariableNode*)node;
 				if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
 				if(vn->variableType == UA_VARIABLENODETYPE_VARIANT) {
-					retval = UA_Variant_copy(&vn->variable.variant, &v->value);
-					if(retval != UA_STATUSCODE_GOOD)
+                    if(hasRange)
+                        retval = UA_Variant_copyRange(&vn->variable.variant, &v->value, range);
+                    else
+                        retval = UA_Variant_copy(&vn->variable.variant, &v->value);
+					if(retval != UA_STATUSCODE_GOOD) {
+                        if(hasRange)
+                            UA_free(range.dimensions);
 						break;
 						break;
+                    }
 					v->hasVariant = UA_TRUE;
 					v->hasVariant = UA_TRUE;
 					handleSourceTimestamps(timestamps, v);
 					handleSourceTimestamps(timestamps, v);
 				} else {
 				} else {
@@ -211,7 +224,12 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 					vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
 					vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
 					if(retval != UA_STATUSCODE_GOOD)
 					if(retval != UA_STATUSCODE_GOOD)
 						break;
 						break;
+                    // todo: selection of indexranges
 				}
 				}
+
+                if(hasRange)
+                    UA_free(range.dimensions);
+                
         	} else {
         	} else {
     			v->hasVariant = UA_FALSE;
     			v->hasVariant = UA_FALSE;
     			handleSourceTimestamps(timestamps, v);
     			handleSourceTimestamps(timestamps, v);
@@ -339,7 +357,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 
 
     if(retval != UA_STATUSCODE_GOOD) {
     if(retval != UA_STATUSCODE_GOOD) {
         v->hasStatus = UA_TRUE;
         v->hasStatus = UA_TRUE;
-        v->status = UA_STATUSCODE_BADNOTREADABLE;
+        v->status = retval;
     }
     }
 
 
     handleServerTimestamps(timestamps, v);
     handleServerTimestamps(timestamps, v);

+ 91 - 3
src/ua_types.c

@@ -623,6 +623,97 @@ UA_StatusCode UA_Variant_copy(UA_Variant const *src, UA_Variant *dst) {
     return retval;
     return retval;
 }
 }
 
 
+UA_StatusCode UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst, const UA_NumericRange range) {
+    UA_Variant_init(dst);
+
+    // test the integrity of the variant dimensions
+    UA_Int32 dims_count = 1;
+    const UA_Int32 *dims = &src->arrayLength; // default: the array has only one dimension
+    if(src->arrayDimensionsSize > 0) {
+        dims_count = src->arrayDimensionsSize;
+        dims = src->arrayDimensions;
+        UA_Int32 elements = 1;
+        for(UA_Int32 i = 0; i < dims_count; i++)
+            elements *= dims[i];
+        if(elements != src->arrayLength)
+            return UA_STATUSCODE_BADINTERNALERROR;
+    }
+
+    // test the integrity of the range and count objects
+    size_t count = 1;
+    if(range.dimensionsSize != dims_count)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    for(UA_Int32 i = 0; i < range.dimensionsSize; i++) {
+        if(range.dimensions[i].min > range.dimensions[i].max ||
+           range.dimensions[i].max > (UA_UInt32)dims[i])
+            return UA_STATUSCODE_BADINDEXRANGENODATA;
+        count *= (range.dimensions[i].max - range.dimensions[i].min) + 1;
+    }
+
+    dst->dataPtr = UA_malloc(src->type->memSize * count);
+    if(!dst->dataPtr)
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    
+    // copy a subset of the tensor with as few calls as possible.
+    // shift from copying single elements to contiguous blocks
+    size_t elem_size = src->type->memSize;
+    uintptr_t nextsrc = (uintptr_t)src->dataPtr; // the start ptr of the next copy operation
+    size_t contiguous_elems = src->arrayLength; // the length of the copy operation
+    ptrdiff_t jump_length = elem_size * dims[0]; // how far to jump until the next contiguous copy
+    size_t copy_count = count; // how often to copy
+
+    size_t running_dimssize = 1; // how big is a contiguous block for the dimensions k_max to k
+    UA_Boolean found_contiguous = UA_FALSE;
+    for(UA_Int32 k = dims_count - 1; k >= 0; k--) {
+        if(!found_contiguous) {
+            if(range.dimensions[k].min != 0 || range.dimensions[k].max + 1 != (UA_UInt32)dims[k]) {
+                found_contiguous = UA_TRUE;
+                contiguous_elems = (range.dimensions[k].max - range.dimensions[k].min + 1) * running_dimssize;
+                jump_length = ((dims[k] * running_dimssize) - contiguous_elems) * elem_size;
+                copy_count /= range.dimensions[k].max - range.dimensions[k].min + 1;
+            } else
+                copy_count /= dims[k];
+        } 
+        nextsrc += running_dimssize * range.dimensions[k].min * elem_size;
+        running_dimssize *= dims[k];
+    }
+
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
+    uintptr_t nextdst = (uintptr_t)dst->dataPtr;
+    size_t copied = 0;
+    for(size_t i = 0; i < copy_count; i++) {
+        if(src->type->fixedSize) {
+            memcpy((void*)nextdst, (void*)nextsrc, elem_size * contiguous_elems);
+        } else {
+            for(size_t j = 0; j < contiguous_elems; j++)
+                retval = UA_copy((const void*)(nextsrc + (j * elem_size)), (void*)(nextdst + (j * elem_size)), src->type);
+            if(retval != UA_STATUSCODE_GOOD) {
+                UA_Array_delete(dst->dataPtr, src->type, copied);
+                return retval;
+            }
+            copied += contiguous_elems;
+        }
+        nextdst += elem_size * contiguous_elems;
+        nextsrc += jump_length;
+    }
+
+    if(src->arrayDimensionsSize > 0) {
+        retval = UA_Array_copy(dims, (void**)&dst->arrayDimensions, &UA_TYPES[UA_TYPES_INT32], dims_count);
+        if(retval != UA_STATUSCODE_GOOD) {
+            UA_Array_delete(dst->dataPtr, src->type, copied);
+            return retval;
+        }
+        for(UA_Int32 k = 0; k < dims_count; k++) {
+            dst->arrayDimensions[k] = range.dimensions[k].max - range.dimensions[k].min + 1;
+        }
+        dst->arrayDimensionsSize = dims_count;
+    }
+    dst->arrayLength = count;
+    dst->type = src->type;
+    dst->storageType = UA_VARIANT_DATA;
+    return UA_STATUSCODE_GOOD;
+}
+
 UA_StatusCode UA_Variant_setValue(UA_Variant *v, void *p, const UA_DataType *type) {
 UA_StatusCode UA_Variant_setValue(UA_Variant *v, void *p, const UA_DataType *type) {
     return UA_Variant_setArray(v, p, -1, type);
     return UA_Variant_setArray(v, p, -1, type);
 }
 }
@@ -1047,9 +1138,6 @@ UA_StatusCode UA_Array_copy(const void *src, void **dst, const UA_DataType *data
 }
 }
 
 
 void UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 noElements) {
 void UA_Array_delete(void *p, const UA_DataType *dataType, UA_Int32 noElements) {
-    if(noElements <= 0 || !p)
-        return;
-
     if(!dataType->fixedSize) {
     if(!dataType->fixedSize) {
         uintptr_t ptr = (uintptr_t)p;
         uintptr_t ptr = (uintptr_t)p;
         for(UA_Int32 i = 0; i<noElements; i++) {
         for(UA_Int32 i = 0; i<noElements; i++) {