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;
 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
     memory, or functions from which the data can be accessed. Variants are replaced together with
     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 */
 
+/**
+ * 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
  * 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_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;
-
     size_t dimensionsIndex = 0;
     size_t dimensionsMax = 3; // more should be uncommon
     struct UA_NumericRangeDimension *dimensions = UA_malloc(sizeof(struct UA_NumericRangeDimension) * 3);
     if(!dimensions)
         return UA_STATUSCODE_BADOUTOFMEMORY;
     
+    UA_StatusCode retval = UA_STATUSCODE_GOOD;
     do {
-        UA_UInt32 min, max;
+        UA_Int32 min, max;
         UA_Int32 progress;
         UA_Int32 res = sscanf(&cstring[index], "%" SCNu32 "%n", &min, &progress);
-        if(res <= 0)
+        if(res <= 0 || min < 0) {
+            retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
             break;
+        }
         index += progress;
-        if(index >= str->length || cstring[index] != ':')
+        if(index >= str.length || cstring[index] == ',')
             max = min;
         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;
+            }
             index += progress;
         }
         
@@ -58,18 +55,18 @@ static UA_StatusCode parse_numericrange(UA_String *str, UA_NumericRange *range)
         dimensions[dimensionsIndex].min = min;
         dimensions[dimensionsIndex].max = max;
         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);
-        range->dimensions = UA_NULL;
-        range->dimensionsSize = 0;
-        return UA_STATUSCODE_BADINDEXRANGENODATA;
+        return retval;
     }
         
     range->dimensions = dimensions;
     range->dimensionsSize = dimensionsIndex;
-    return UA_STATUSCODE_GOOD;
+    return retval;
 }
 
 #define CHECK_NODECLASS(CLASS)                                  \
@@ -191,12 +188,28 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
     case UA_ATTRIBUTEID_VALUE:
         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;
 				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;
+                    }
 					v->hasVariant = UA_TRUE;
 					handleSourceTimestamps(timestamps, v);
 				} else {
@@ -211,7 +224,12 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 					vn->variable.dataSource.release(vn->variable.dataSource.handle, &val);
 					if(retval != UA_STATUSCODE_GOOD)
 						break;
+                    // todo: selection of indexranges
 				}
+
+                if(hasRange)
+                    UA_free(range.dimensions);
+                
         	} else {
     			v->hasVariant = UA_FALSE;
     			handleSourceTimestamps(timestamps, v);
@@ -339,7 +357,7 @@ static void readValue(UA_Server *server, UA_TimestampsToReturn timestamps,
 
     if(retval != UA_STATUSCODE_GOOD) {
         v->hasStatus = UA_TRUE;
-        v->status = UA_STATUSCODE_BADNOTREADABLE;
+        v->status = retval;
     }
 
     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;
 }
 
+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) {
     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) {
-    if(noElements <= 0 || !p)
-        return;
-
     if(!dataType->fixedSize) {
         uintptr_t ptr = (uintptr_t)p;
         for(UA_Int32 i = 0; i<noElements; i++) {