Bladeren bron

Improve testing of ValueRank - ArrayDimensions compatibility

Julius Pfrommer 6 jaren geleden
bovenliggende
commit
83a83410cb

+ 6 - 0
examples/server_ctt.c

@@ -241,6 +241,7 @@ setInformationModel(UA_Server *server) {
                             UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr, NULL, NULL);
 
     /* Fill demo nodes for each type*/
+    UA_UInt32 matrixDims[2] = {3, 3};
     UA_UInt32 id = 51000; // running id in namespace 0
     for(UA_UInt32 type = 0; type < UA_TYPES_DIAGNOSTICINFO; type++) {
         if(type == UA_TYPES_VARIANT || type == UA_TYPES_DIAGNOSTICINFO)
@@ -275,7 +276,10 @@ setInformationModel(UA_Server *server) {
         UA_Variant_deleteMembers(&attr.value);
 
         /* add an array node for every built-in type */
+        UA_UInt32 arrayDims = 0;
         attr.valueRank = 1;
+        attr.arrayDimensions = &arrayDims;
+        attr.arrayDimensionsSize = 1;
         UA_Variant_setArray(&attr.value, UA_Array_new(10, &UA_TYPES[type]), 10, &UA_TYPES[type]);
         UA_Server_addVariableNode(server, UA_NODEID_NUMERIC(1, ++id), UA_NODEID_NUMERIC(1, ARRAYID),
                                   UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), qualifiedName,
@@ -284,6 +288,8 @@ setInformationModel(UA_Server *server) {
 
         /* add an matrix node for every built-in type */
         attr.valueRank = 2;
+        attr.arrayDimensions = matrixDims;
+        attr.arrayDimensionsSize = 2;
         void *myMultiArray = UA_Array_new(9, &UA_TYPES[type]);
         attr.value.arrayDimensions = (UA_UInt32 *)UA_Array_new(2, &UA_TYPES[UA_TYPES_INT32]);
         attr.value.arrayDimensions[0] = 3;

+ 3 - 2
src/server/ua_server_internal.h

@@ -304,7 +304,7 @@ readValueAttribute(UA_Server *server, UA_Session *session,
  * byte array to bytestring or uint32 to some enum. If editableValue is non-NULL,
  * we try to create a matching variant that points to the original data. */
 UA_Boolean
-compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
+compatibleValue(UA_Server *server, UA_Session *session, const UA_NodeId *targetDataTypeId,
                 UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
                 const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
                 const UA_NumericRange *range);
@@ -320,7 +320,8 @@ compatibleValueArrayDimensions(const UA_Variant *value, size_t targetArrayDimens
                                const UA_UInt32 *targetArrayDimensions);
 
 UA_Boolean
-compatibleValueRankArrayDimensions(UA_Int32 valueRank, size_t arrayDimensionsSize);
+compatibleValueRankArrayDimensions(UA_Server *server, UA_Session *session,
+                                   UA_Int32 valueRank, size_t arrayDimensionsSize);
 
 UA_Boolean
 compatibleDataType(UA_Server *server, const UA_NodeId *dataType,

+ 62 - 34
src/server/ua_services_attribute.c

@@ -617,32 +617,38 @@ compatibleDataType(UA_Server *server, const UA_NodeId *dataType,
     return false;
 }
 
-/* Test whether a valurank and the given arraydimensions are compatible. zero
- * array dimensions indicate a scalar */
+/* Test whether a ValueRank and the given arraydimensions are compatible.
+ *
+ * 5.6.2 Variable NodeClass: If the maximum is unknown the value shall be 0. The
+ * number of elements shall be equal to the value of the ValueRank Attribute.
+ * This Attribute shall be null if ValueRank <= 0. */
 UA_Boolean
-compatibleValueRankArrayDimensions(UA_Int32 valueRank, size_t arrayDimensionsSize) {
-    switch(valueRank) {
-    case -3: /* the value can be a scalar or a one dimensional array */
-        if(arrayDimensionsSize > 1)
-            return false;
-        break;
-    case -2: /* the value can be a scalar or an array with any number of dimensions */
-        break;
-    case -1: /* the value is a scalar */
-        if(arrayDimensionsSize > 0)
-            return false;
-        break;
-    case 0: /* the value is an array with one or more dimensions */
-        if(arrayDimensionsSize < 1)
-            return false;
-        break;
-    default: /* >= 1: the value is an array with the specified number of dimensions */
-        if(valueRank < (UA_Int32) 0)
-            return false;
-        /* Must hold if the array has a defined length. Null arrays (length -1)
-         * need to be caught before. */
-        if(arrayDimensionsSize != (size_t)valueRank)
+compatibleValueRankArrayDimensions(UA_Server *server, UA_Session *session,
+                                   UA_Int32 valueRank, size_t arrayDimensionsSize) {
+    /* ValueRank invalid */
+    if(valueRank < -3) {
+        UA_LOG_INFO_SESSION(server->config.logger, session, "The ValueRank is invalid (< -3)");
+        return false;
+    }
+
+    /* case -3: the value can be a scalar or a one dimensional array */
+    /* case -2: the value can be a scalar or an array with any number of dimensions */
+    /* case -1: the value is a scalar */
+    /* case 0:  the value is an array with one or more dimensions */
+    if(valueRank <= 0) {
+        if(arrayDimensionsSize > 0) {
+            UA_LOG_INFO_SESSION(server->config.logger, session,
+                                "No ArrayDimensions can be defined for a ValueRank <= 0");
             return false;
+        }
+        return true;
+    }
+    
+    /* case >= 1: the value is an array with the specified number of dimensions */
+    if(arrayDimensionsSize != (size_t)valueRank) {
+        UA_LOG_INFO_SESSION(server->config.logger, session,
+                            "The number of ArrayDimensions is not equal to the (positive) ValueRank");
+        return false;
     }
     return true;
 }
@@ -673,9 +679,15 @@ compatibleValueRanks(UA_Int32 valueRank, UA_Int32 constraintValueRank) {
     return true;
 }
 
-/* Check if the valuerank allows for the value dimension */
+/* Check if the ValueRank allows for the value dimension. This is more
+ * permissive than checking for the ArrayDimensions attribute. Because the value
+ * can have dimensions if the ValueRank < 0 */
 static UA_Boolean
 compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) {
+    /* Invalid ValueRank */
+    if(valueRank < -3)
+        return false;
+
     /* Empty arrays (-1) always match */
     if(!value->data)
         return true;
@@ -683,7 +695,24 @@ compatibleValueRankValue(UA_Int32 valueRank, const UA_Variant *value) {
     size_t arrayDims = value->arrayDimensionsSize;
     if(arrayDims == 0 && !UA_Variant_isScalar(value))
         arrayDims = 1; /* array but no arraydimensions -> implicit array dimension 1 */
-    return compatibleValueRankArrayDimensions(valueRank, arrayDims);
+
+    /* We cannot simply use compatibleValueRankArrayDimensions since we can have
+     * defined ArrayDimensions for the value if the ValueRank is -2 */
+    switch(valueRank) {
+    case -3: /* The value can be a scalar or a one dimensional array */
+        return (arrayDims <= 1);
+    case -2: /* The value can be a scalar or an array with any number of dimensions */
+        return true;
+    case -1: /* The value is a scalar */
+        return (arrayDims == 0);
+    default:
+        break;
+    }
+
+    UA_assert(valueRank >= 0);
+
+    /* case 0:  the value is an array with one or more dimensions */
+    return (arrayDims == (UA_UInt32)valueRank);
 }
 
 UA_Boolean
@@ -724,7 +753,7 @@ compatibleValueArrayDimensions(const UA_Variant *value, size_t targetArrayDimens
 }
 
 UA_Boolean
-compatibleValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
+compatibleValue(UA_Server *server, UA_Session *session, const UA_NodeId *targetDataTypeId,
                 UA_Int32 targetValueRank, size_t targetArrayDimensionsSize,
                 const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
                 const UA_NumericRange *range) {
@@ -802,7 +831,6 @@ adjustValue(UA_Server *server, UA_Variant *value,
     /* No more possible equivalencies */
 }
 
-/* Stack layout: ... | node | type */
 static UA_StatusCode
 writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session,
                               UA_VariableNode *node, const UA_VariableTypeNode *type,
@@ -820,9 +848,9 @@ writeArrayDimensionsAttribute(UA_Server *server, UA_Session *session,
     }
 
     /* Check that the array dimensions match with the valuerank */
-    if(!compatibleValueRankArrayDimensions(node->valueRank, arrayDimensionsSize)) {
+    if(!compatibleValueRankArrayDimensions(server, session, node->valueRank, arrayDimensionsSize)) {
         UA_LOG_DEBUG(server->config.logger, UA_LOGCATEGORY_SERVER,
-                     "The current value rank does not match the new array dimensions");
+                     "Cannot write the ArrayDimensions. The ValueRank does not match.");
         return UA_STATUSCODE_BADTYPEMISMATCH;
     }
 
@@ -906,7 +934,7 @@ writeValueRankAttribute(UA_Server *server, UA_Session *session,
             arrayDims = 1;
         UA_DataValue_deleteMembers(&value);
     }
-    if(!compatibleValueRankArrayDimensions(valueRank, arrayDims))
+    if(!compatibleValueRankArrayDimensions(server, session, valueRank, arrayDims))
         return UA_STATUSCODE_BADTYPEMISMATCH;
 
     /* All good, apply the change */
@@ -938,7 +966,7 @@ writeDataTypeAttribute(UA_Server *server, UA_Session *session,
     if(retval != UA_STATUSCODE_GOOD)
         return retval;
     if(value.hasValue) {
-        if(!compatibleValue(server, dataType, node->valueRank,
+        if(!compatibleValue(server, session, dataType, node->valueRank,
                             node->arrayDimensionsSize, node->arrayDimensions,
                             &value.value, NULL))
             retval = UA_STATUSCODE_BADTYPEMISMATCH;
@@ -1045,11 +1073,11 @@ writeValueAttribute(UA_Server *server, UA_Session *session,
         if(value->value.type->typeId.identifierType == UA_NODEIDTYPE_NUMERIC &&
            value->value.type->typeId.identifier.numeric == UA_NS0ID_STRUCTURE) {
             const UA_NodeId nodeDataType = UA_NODEID_NUMERIC(0, UA_NS0ID_STRUCTURE);
-            compatible = compatibleValue(server, &nodeDataType, node->valueRank,
+            compatible = compatibleValue(server, session, &nodeDataType, node->valueRank,
                                     node->arrayDimensionsSize, node->arrayDimensions,
                                     &adjustedValue.value, rangeptr);
         } else {
-            compatible = compatibleValue(server, &node->dataType, node->valueRank,
+            compatible = compatibleValue(server, session, &node->dataType, node->valueRank,
                                      node->arrayDimensionsSize, node->arrayDimensions,
                                      &adjustedValue.value, rangeptr);
         }

+ 7 - 7
src/server/ua_services_call.c

@@ -50,9 +50,9 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
 
 /* inputArgumentResults has the length request->inputArgumentsSize */
 static UA_StatusCode
-typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
-                   size_t argsSize, UA_Variant *args,
-                   UA_StatusCode *inputArgumentResults) {
+typeCheckArguments(UA_Server *server, UA_Session *session,
+                   const UA_VariableNode *argRequirements, size_t argsSize,
+                   UA_Variant *args, UA_StatusCode *inputArgumentResults) {
     /* Verify that we have a Variant containing UA_Argument (scalar or array) in
      * the "InputArguments" node */
     if(argRequirements->valueSource != UA_VALUESOURCE_DATA)
@@ -76,7 +76,7 @@ typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
     for(size_t i = 0; i < argReqsSize; ++i) {
-        if(!compatibleValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
+        if(!compatibleValue(server, session, &argReqs[i].dataType, argReqs[i].valueRank,
                             argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
                             &args[i], NULL)) {
             inputArgumentResults[i] = UA_STATUSCODE_BADTYPEMISMATCH;
@@ -88,7 +88,7 @@ typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
 
 /* inputArgumentResults has the length request->inputArgumentsSize */
 static UA_StatusCode
-validMethodArguments(UA_Server *server, const UA_MethodNode *method,
+validMethodArguments(UA_Server *server, UA_Session *session, const UA_MethodNode *method,
                      const UA_CallMethodRequest *request,
                      UA_StatusCode *inputArgumentResults) {
     /* Get the input arguments node */
@@ -101,7 +101,7 @@ validMethodArguments(UA_Server *server, const UA_MethodNode *method,
     }
 
     /* Verify the request */
-    UA_StatusCode retval = typeCheckArguments(server, inputArguments,
+    UA_StatusCode retval = typeCheckArguments(server, session, inputArguments,
                                               request->inputArgumentsSize,
                                               request->inputArguments,
                                               inputArgumentResults);
@@ -185,7 +185,7 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
     result->inputArgumentResultsSize = request->inputArgumentsSize;
 
     /* Verify Input Arguments */
-    result->statusCode = validMethodArguments(server, method, request, result->inputArgumentResults);
+    result->statusCode = validMethodArguments(server, session, method, request, result->inputArgumentResults);
 
     /* Return inputArgumentResults only for BADINVALIDARGUMENT */
     if(result->statusCode != UA_STATUSCODE_BADINVALIDARGUMENT) {

+ 8 - 15
src/server/ua_services_nodemanagement.c

@@ -161,22 +161,9 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
         return UA_STATUSCODE_BADTYPEMISMATCH;
     }
 
-    /* Get the array dimensions */
-    size_t arrayDims = node->arrayDimensionsSize;
-    if(arrayDims == 0 && value.hasValue && value.value.type &&
-       !UA_Variant_isScalar(&value.value)) {
-        arrayDims = 1; /* No array dimensions on an array implies one dimension */
-    }
-
     /* Check valueRank against array dimensions */
-    if(arrayDims != 0 &&
-       !(node->nodeClass == UA_NODECLASS_VARIABLETYPE &&
-         ((const UA_VariableTypeNode*)node)->isAbstract && node->valueRank == 0) &&
-       !compatibleValueRankArrayDimensions(node->valueRank, arrayDims)) {
-        UA_LOG_INFO_SESSION(server->config.logger, session,
-                            "AddNodes: The value rank and array dimensions are not compatible");
+    if(!compatibleValueRankArrayDimensions(server, session, node->valueRank, node->arrayDimensionsSize))
         return UA_STATUSCODE_BADTYPEMISMATCH;
-    }
 
     /* Check valueRank against the vt */
     if(!compatibleValueRanks(node->valueRank, vt->valueRank)) {
@@ -197,7 +184,7 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
     if(value.hasValue) {
         /* If the type-check failed write the same value again. The
          * write-service tries to convert to the correct type... */
-        if(!compatibleValue(server, &node->dataType, node->valueRank,
+        if(!compatibleValue(server, session, &node->dataType, node->valueRank,
                             node->arrayDimensionsSize, node->arrayDimensions,
                             &value.value, NULL))
             retval = UA_Server_writeValue(server, node->nodeId, value.value);
@@ -1639,6 +1626,9 @@ UA_Server_addMethodNodeEx_finish(UA_Server *server, const UA_NodeId nodeId,
         attr.displayName = UA_LOCALIZEDTEXT("", name);
         attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId;
         attr.valueRank = 1;
+        UA_UInt32 inputArgsSize32 = (UA_UInt32)inputArgumentsSize;
+        attr.arrayDimensions = &inputArgsSize32;
+        attr.arrayDimensionsSize = 1;
         UA_Variant_setArray(&attr.value, (void*)(uintptr_t) inputArguments,
                             inputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
         retval |= UA_Server_addVariableNode(server, inputArgumentsRequestedNewNodeId, nodeId,
@@ -1653,6 +1643,9 @@ UA_Server_addMethodNodeEx_finish(UA_Server *server, const UA_NodeId nodeId,
         attr.displayName = UA_LOCALIZEDTEXT("", name);
         attr.dataType = UA_TYPES[UA_TYPES_ARGUMENT].typeId;
         attr.valueRank = 1;
+        UA_UInt32 outputArgsSize32 = (UA_UInt32)outputArgumentsSize;
+        attr.arrayDimensions = &outputArgsSize32;
+        attr.arrayDimensionsSize = 1;
         UA_Variant_setArray(&attr.value, (void*)(uintptr_t) outputArguments,
                             outputArgumentsSize, &UA_TYPES[UA_TYPES_ARGUMENT]);
         retval |= UA_Server_addVariableNode(server, outputArgumentsRequestedNewNodeId, nodeId,