Pārlūkot izejas kodu

stricter type checking when adding nodes; copy over value from the variabletype when required

Julius Pfrommer 8 gadi atpakaļ
vecāks
revīzija
b4a59bb5b9

+ 38 - 72
src/server/ua_server_ns0.c

@@ -53,56 +53,6 @@ readStatus(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
     return UA_STATUSCODE_GOOD;
 }
 
-/** TODO: rework the code duplication in the getter methods **/
-static UA_StatusCode
-readServiceLevel(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
-                 const UA_NumericRange *range, UA_DataValue *value) {
-    if(range) {
-        value->hasStatus = true;
-        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
-        return UA_STATUSCODE_GOOD;
-    }
-
-    value->value.type = &UA_TYPES[UA_TYPES_BYTE];
-    value->value.arrayLength = 0;
-    UA_Byte *byte = UA_Byte_new();
-    *byte = 255;
-    value->value.data = byte;
-    value->value.arrayDimensionsSize = 0;
-    value->value.arrayDimensions = NULL;
-    value->hasValue = true;
-    if(sourceTimeStamp) {
-        value->hasSourceTimestamp = true;
-        value->sourceTimestamp = UA_DateTime_now();
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
-/** TODO: rework the code duplication in the getter methods **/
-static UA_StatusCode
-readAuditing(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
-             const UA_NumericRange *range, UA_DataValue *value) {
-    if(range) {
-        value->hasStatus = true;
-        value->status = UA_STATUSCODE_BADINDEXRANGEINVALID;
-        return UA_STATUSCODE_GOOD;
-    }
-
-    value->value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
-    value->value.arrayLength = 0;
-    UA_Boolean *boolean = UA_Boolean_new();
-    *boolean = false;
-    value->value.data = boolean;
-    value->value.arrayDimensionsSize = 0;
-    value->value.arrayDimensions = NULL;
-    value->hasValue = true;
-    if(sourceTimeStamp) {
-        value->hasSourceTimestamp = true;
-        value->sourceTimestamp = UA_DateTime_now();
-    }
-    return UA_STATUSCODE_GOOD;
-}
-
 static UA_StatusCode
 readNamespaces(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimestamp,
                const UA_NumericRange *range, UA_DataValue *value) {
@@ -291,15 +241,15 @@ addReferenceTypeNode(UA_Server *server, char* name, char *inverseName, UA_UInt32
 static void
 addVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
                     UA_Boolean isAbstract, UA_Int32 valueRank, UA_UInt32 dataType,
-                    UA_Variant *value, UA_UInt32 parentid) {
+                    const UA_DataType *type, UA_UInt32 parentid) {
     UA_VariableTypeAttributes attr;
     UA_VariableTypeAttributes_init(&attr);
     attr.displayName = UA_LOCALIZEDTEXT("en_US", name);
     attr.isAbstract = isAbstract;
     attr.dataType = UA_NODEID_NUMERIC(0, dataType);
     attr.valueRank = valueRank;
-    if(value)
-        attr.value = *value;
+    if(type)
+        UA_Variant_setScalar(&attr.value, UA_new(type), type);
     UA_Server_addVariableTypeNode(server, UA_NODEID_NUMERIC(0, variabletypeid),
                                   UA_NODEID_NUMERIC(0, parentid), UA_NODEID_NULL,
                                   UA_QUALIFIEDNAME(0, name), UA_NODEID_NULL, attr, NULL, NULL);
@@ -466,10 +416,12 @@ void UA_Server_createNS0(UA_Server *server) {
                         false, -2, UA_NS0ID_BASEDATATYPE, NULL, UA_NS0ID_BASEVARIABLETYPE);
 
     addVariableTypeNode(server, "BuildInfoType", UA_NS0ID_BUILDINFOTYPE,
-                        false, -1, UA_NS0ID_BUILDINFO, NULL, UA_NS0ID_BASEDATAVARIABLETYPE);
+                        false, -1, UA_NS0ID_BUILDINFO, &UA_TYPES[UA_TYPES_BUILDINFO],
+                        UA_NS0ID_BASEDATAVARIABLETYPE);
 
     addVariableTypeNode(server, "ServerStatusType", UA_NS0ID_SERVERSTATUSTYPE,
-                        false, -1, UA_NS0ID_SERVERSTATUSDATATYPE, NULL, UA_NS0ID_BASEDATAVARIABLETYPE);
+                        false, -1, UA_NS0ID_SERVERSTATUSDATATYPE, &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE],
+                        UA_NS0ID_BASEDATAVARIABLETYPE);
 
     /***************/
     /* ObjectTypes */
@@ -672,12 +624,20 @@ void UA_Server_createNS0(UA_Server *server) {
                              UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE), NULL);
 
     // TODO: Begin Serverstatus
-    addVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS, "ServerStatus", -1,
-                    &UA_TYPES[UA_TYPES_SERVERSTATUSDATATYPE].typeId, NULL,
-                    UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEDATAVARIABLETYPE);
+    UA_VariableAttributes serverstatus_attr;
+    UA_VariableAttributes_init(&serverstatus_attr);
+    serverstatus_attr.displayName = UA_LOCALIZEDTEXT("en_US", "ServerStatus");
+    serverstatus_attr.valueRank = -1;
+    serverstatus_attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSDATATYPE);
+    UA_Server_addVariableNode_begin(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                                    UA_QUALIFIEDNAME(0, "ServerStatus"), serverstatus_attr, NULL);
     UA_DataSource statusDS = {.handle = server, .read = readStatus, .write = NULL};
     UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
                                          statusDS);
+    UA_Server_addNode_finish(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), NULL);
 
     UA_Variant_setScalar(&var, &server->startTime, &UA_TYPES[UA_TYPES_DATETIME]);
     addVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME, "StartTime", -1,
@@ -685,12 +645,20 @@ void UA_Server_createNS0(UA_Server *server) {
                     UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEDATAVARIABLETYPE);
 
     /* TODO: UTC Time Type */
-    addVariableNode(server, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME, "CurrentTime", -1,
-                    &UA_TYPES[UA_TYPES_DATETIME].typeId, NULL, UA_NS0ID_SERVER_SERVERSTATUS,
-                    UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEDATAVARIABLETYPE);
-    UA_DataSource currentDS = {.handle = NULL, .read = readCurrentTime, .write = NULL};
     const UA_NodeId currentTimeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
+    UA_VariableAttributes currenttime_attr;
+    UA_VariableAttributes_init(&currenttime_attr);
+    currenttime_attr.displayName = UA_LOCALIZEDTEXT("en_US", "CurrentTime");
+    currenttime_attr.valueRank = -1;
+    currenttime_attr.dataType = UA_TYPES[UA_TYPES_DATETIME].typeId;
+    UA_Server_addVariableNode_begin(server, currentTimeId, UA_QUALIFIEDNAME(0, "CurrentTime"),
+                                    currenttime_attr, NULL);
+    UA_DataSource currentDS = {.handle = NULL, .read = readCurrentTime, .write = NULL};
     UA_Server_setVariableNode_dataSource(server, currentTimeId, currentDS);
+    UA_Server_addNode_finish(server, currentTimeId,
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS),
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
+                             UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), NULL);
 
     UA_ServerState state = UA_SERVERSTATE_RUNNING;
     UA_Variant_setScalar(&var, &state, &UA_TYPES[UA_TYPES_SERVERSTATE]);
@@ -750,19 +718,17 @@ void UA_Server_createNS0(UA_Server *server) {
     
     /* Finish ServerStatus */
 
+    UA_Byte serviceLevel = 255;
+    UA_Variant_setScalar(&var, &serviceLevel, &UA_TYPES[UA_TYPES_BYTE]);
     addVariableNode(server, UA_NS0ID_SERVER_SERVICELEVEL, "ServiceLevel", -1,
-                    &UA_TYPES[UA_TYPES_BYTE].typeId, NULL,
-                    UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, UA_NS0ID_PROPERTYTYPE);
-    UA_DataSource servicelevelDS = {.handle = server, .read = readServiceLevel, .write = NULL};
-    UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVICELEVEL),
-                                         servicelevelDS);
+                    &UA_TYPES[UA_TYPES_BYTE].typeId, &var, UA_NS0ID_SERVER,
+                    UA_NS0ID_HASCOMPONENT, UA_NS0ID_PROPERTYTYPE);
 
+    UA_Boolean auditing = false;
+    UA_Variant_setScalar(&var, &auditing, &UA_TYPES[UA_TYPES_BOOLEAN]);
     addVariableNode(server, UA_NS0ID_SERVER_AUDITING, "Auditing", -1,
-                    &UA_TYPES[UA_TYPES_BOOLEAN].typeId, NULL,
-                    UA_NS0ID_SERVER, UA_NS0ID_HASCOMPONENT, UA_NS0ID_PROPERTYTYPE);
-    UA_DataSource auditingDS = {.handle = server, .read = readAuditing, .write = NULL};
-    UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_AUDITING),
-                                         auditingDS);
+                    &UA_TYPES[UA_TYPES_BOOLEAN].typeId, &var, UA_NS0ID_SERVER,
+                    UA_NS0ID_HASCOMPONENT, UA_NS0ID_PROPERTYTYPE);
 
     addObjectNode(server, "VendorServerInfo", UA_NS0ID_SERVER_VENDORSERVERINFO, UA_NS0ID_SERVER,
                   UA_NS0ID_HASCOMPONENT, UA_NS0ID_BASEOBJECTTYPE);

+ 15 - 7
src/server/ua_services_attribute.c

@@ -19,7 +19,8 @@ forceVariantSetScalar(UA_Variant *v, const void *p, const UA_DataType *t) {
 }
 
 static UA_UInt32
-getUserWriteMask(UA_Server *server, const UA_Session *session, const UA_Node *node) {
+getUserWriteMask(UA_Server *server, const UA_Session *session,
+                 const UA_Node *node) {
     if(session == &adminSession)
         return 0xFFFFFFFF; /* the local admin user has all rights */
     return node->writeMask &
@@ -232,8 +233,15 @@ typeCheckValue(UA_Server *server, const UA_NodeId *targetDataTypeId,
                const UA_UInt32 *targetArrayDimensions, const UA_Variant *value,
                const UA_NumericRange *range, UA_Variant *editableValue) {
     /* Empty variant is only allowed for BaseDataType */
-    if(!value->type)
-        return UA_STATUSCODE_GOOD;
+    if(!value->type) {
+        if(UA_NodeId_equal(targetDataTypeId, &UA_TYPES[UA_TYPES_VARIANT].typeId) ||
+           UA_NodeId_equal(targetDataTypeId, &UA_NODEID_NULL))
+            return UA_STATUSCODE_GOOD;
+        UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
+                    "Only Variables with data type BaseDataType may contain "
+                    "a null (empty) value");
+        return UA_STATUSCODE_BADTYPEMISMATCH;
+    }
 
     /* Has the value a subtype of the required type? BaseDataType (Variant) can
      * be anything... */
@@ -1060,7 +1068,7 @@ __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId,
 /* This function implements the main part of the write service and operates on a
    copy of the node (not in single-threaded mode). */
 static UA_StatusCode
-CopyAttributeIntoNode(UA_Server *server, UA_Session *session,
+copyAttributeIntoNode(UA_Server *server, UA_Session *session,
                       UA_Node *node, const UA_WriteValue *wvalue) {
     const void *value = wvalue->value.value.data;
     UA_UInt32 userWriteMask = getUserWriteMask(server, session, node);
@@ -1216,7 +1224,7 @@ Service_Write(UA_Server *server, UA_Session *session,
 #ifndef UA_ENABLE_EXTERNAL_NAMESPACES
     for(size_t i = 0;i < request->nodesToWriteSize;++i) {
         response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
-                                                  (UA_EditNodeCallback)CopyAttributeIntoNode,
+                                                  (UA_EditNodeCallback)copyAttributeIntoNode,
                                                   &request->nodesToWrite[i]);
     }
 #else
@@ -1243,7 +1251,7 @@ Service_Write(UA_Server *server, UA_Session *session,
         if(isExternal[i])
             continue;
         response->results[i] = UA_Server_editNode(server, session, &request->nodesToWrite[i].nodeId,
-                                                  (UA_EditNodeCallback)CopyAttributeIntoNode,
+                                                  (UA_EditNodeCallback)copyAttributeIntoNode,
                                                   &request->nodesToWrite[i]);
     }
 #endif
@@ -1254,7 +1262,7 @@ UA_Server_write(UA_Server *server, const UA_WriteValue *value) {
     UA_RCU_LOCK();
     UA_StatusCode retval =
         UA_Server_editNode(server, &adminSession, &value->nodeId,
-                           (UA_EditNodeCallback)CopyAttributeIntoNode, value);
+                           (UA_EditNodeCallback)copyAttributeIntoNode, value);
     UA_RCU_UNLOCK();
     return retval;
 }

+ 11 - 1
src/server/ua_services_nodemanagement.c

@@ -122,6 +122,17 @@ typeCheckVariableNode(UA_Server *server, UA_Session *session,
             goto cleanup;
     }
 
+    /* If no value is set, see if the vt provides one and copy that. THis needs to be done
+     * before copying the datatype from the vt. Setting the datatype triggers the
+     * typecheck. Here, we have only a typecheck when the datatype is already not null. */
+    if(!value.hasValue || !value.value.type) {
+        retval = readValueAttribute(server, (const UA_VariableNode*)vt, &value);
+        if(retval == UA_STATUSCODE_GOOD && value.hasValue && value.value.type)
+            retval = UA_Server_writeValue(server, node->nodeId, value.value);
+        if(retval != UA_STATUSCODE_GOOD)
+            goto cleanup;
+    }
+
     /* Workaround: Replace with datatype of the vt if not set */
     const UA_NodeId *dataType = &node->dataType;
     if(UA_NodeId_isNull(dataType)) {
@@ -1531,4 +1542,3 @@ UA_Server_setMethodNode_callback(UA_Server *server, const UA_NodeId methodNodeId
 }
 
 #endif
-