Browse Source

feat(pubsub) added generation of metdata for PDS and DataSetFields

Andreas Ebner 4 years ago
parent
commit
3b170d0199

+ 5 - 0
include/open62541/server_pubsub.h

@@ -215,6 +215,11 @@ UA_StatusCode UA_EXPORT
 UA_Server_getPublishedDataSetConfig(UA_Server *server, const UA_NodeId pds,
                                     UA_PublishedDataSetConfig *config);
 
+/* Returns a deep copy of the DataSetMetaData for an specific PDS */
+UA_StatusCode UA_EXPORT
+UA_Server_getPublishedDataSetMetaData(UA_Server *server, const UA_NodeId pds,
+                                      UA_DataSetMetaDataType *metaData);
+
 /* Remove PublishedDataSet, identified by the NodeId. Deletion of PDS removes
  * all contained and linked PDS Fields. Connected WriterGroups will be also
  * removed. */

+ 41 - 1
src/pubsub/ua_pubsub_manager.c

@@ -201,10 +201,50 @@ UA_Server_addPublishedDataSet(UA_Server *server, const UA_PublishedDataSetConfig
     if(pdsIdentifier != NULL){
         UA_NodeId_copy(&newPubSubDataSet->identifier, pdsIdentifier);
     }
-    server->pubSubManager.publishedDataSetsSize++;
+
     result.addResult = UA_STATUSCODE_GOOD;
     result.fieldAddResults = NULL;
     result.fieldAddResultsSize = 0;
+
+    //fill the DataSetMetaData
+    switch(tmpPublishedDataSetConfig.publishedDataSetType){
+        case UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE:
+            if(UA_DataSetMetaDataType_copy(&tmpPublishedDataSetConfig.config.itemsTemplate.metaData,
+                    &newPubSubDataSet->dataSetMetaData) != UA_STATUSCODE_GOOD){
+                UA_Server_removeDataSetField(server, newPubSubDataSet->identifier);
+                result.addResult = UA_STATUSCODE_BADINTERNALERROR;
+            }
+            break;
+        case UA_PUBSUB_DATASET_PUBLISHEDEVENTS_TEMPLATE:
+            if(UA_DataSetMetaDataType_copy(&tmpPublishedDataSetConfig.config.eventTemplate.metaData,
+                    &newPubSubDataSet->dataSetMetaData) != UA_STATUSCODE_GOOD){
+                UA_Server_removeDataSetField(server, newPubSubDataSet->identifier);
+                result.addResult = UA_STATUSCODE_BADINTERNALERROR;
+            }
+            break;
+        case UA_PUBSUB_DATASET_PUBLISHEDEVENTS:
+            newPubSubDataSet->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference();
+            newPubSubDataSet->dataSetMetaData.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference();
+            newPubSubDataSet->dataSetMetaData.dataSetClassId = UA_GUID_NULL;
+            if(UA_String_copy(&tmpPublishedDataSetConfig.name, &newPubSubDataSet->dataSetMetaData.name) != UA_STATUSCODE_GOOD){
+                UA_Server_removeDataSetField(server, newPubSubDataSet->identifier);
+                result.addResult = UA_STATUSCODE_BADINTERNALERROR;
+            }
+            newPubSubDataSet->dataSetMetaData.description = UA_LOCALIZEDTEXT_ALLOC("", "");
+            break;
+        case UA_PUBSUB_DATASET_PUBLISHEDITEMS:
+            newPubSubDataSet->dataSetMetaData.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference();
+            newPubSubDataSet->dataSetMetaData.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference();
+            if(UA_String_copy(&tmpPublishedDataSetConfig.name, &newPubSubDataSet->dataSetMetaData.name) != UA_STATUSCODE_GOOD){
+                UA_Server_removeDataSetField(server, newPubSubDataSet->identifier);
+                result.addResult = UA_STATUSCODE_BADINTERNALERROR;
+            }
+            newPubSubDataSet->dataSetMetaData.description = UA_LOCALIZEDTEXT_ALLOC("", "");
+            newPubSubDataSet->dataSetMetaData.dataSetClassId = UA_GUID_NULL;
+            break;
+    }
+
+    server->pubSubManager.publishedDataSetsSize++;
     result.configurationVersion.majorVersion = UA_PubSubConfigurationVersionTimeDifference();
     result.configurationVersion.minorVersion = UA_PubSubConfigurationVersionTimeDifference();
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL

+ 150 - 1
src/pubsub/ua_pubsub_writer.c

@@ -355,6 +355,22 @@ UA_Server_getPublishedDataSetConfig(UA_Server *server, const UA_NodeId pds,
     return UA_STATUSCODE_GOOD;
 }
 
+UA_StatusCode
+UA_Server_getPublishedDataSetMetaData(UA_Server *server, const UA_NodeId pds, UA_DataSetMetaDataType *metaData){
+    if(!metaData)
+        return UA_STATUSCODE_BADINVALIDARGUMENT;
+
+    UA_PublishedDataSet *currentPublishedDataSet = UA_PublishedDataSet_findPDSbyId(server, pds);
+    if(!currentPublishedDataSet) 
+        return UA_STATUSCODE_BADNOTFOUND;
+
+    UA_DataSetMetaDataType tmpDataSetMetaData;
+    if(UA_DataSetMetaDataType_copy(&currentPublishedDataSet->dataSetMetaData, &tmpDataSetMetaData) != UA_STATUSCODE_GOOD)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    *metaData = tmpDataSetMetaData;
+    return UA_STATUSCODE_GOOD;
+}
+
 UA_PublishedDataSet *
 UA_PublishedDataSet_findPDSbyId(UA_Server *server, UA_NodeId identifier){
     for(size_t i = 0; i < server->pubSubManager.publishedDataSetsSize; i++){
@@ -391,14 +407,92 @@ void
 UA_PublishedDataSet_clear(UA_Server *server, UA_PublishedDataSet *publishedDataSet){
     UA_PublishedDataSetConfig_clear(&publishedDataSet->config);
     //delete PDS
-    UA_DataSetMetaDataType_clear(&publishedDataSet->dataSetMetaData);
     UA_DataSetField *field, *tmpField;
     TAILQ_FOREACH_SAFE(field, &publishedDataSet->fields, listEntry, tmpField) {
         UA_Server_removeDataSetField(server, field->identifier);
     }
+    UA_DataSetMetaDataType_clear(&publishedDataSet->dataSetMetaData);
     UA_NodeId_clear(&publishedDataSet->identifier);
 }
 
+static UA_StatusCode
+generateFieldMetaData(UA_Server *server, UA_DataSetField *field, UA_FieldMetaData *fieldMetaData){
+    switch (field->config.dataSetFieldType){
+        case UA_PUBSUB_DATASETFIELD_VARIABLE:
+            if(UA_String_copy(&field->config.field.variable.fieldNameAlias, &fieldMetaData->name) != UA_STATUSCODE_GOOD)
+                return UA_STATUSCODE_BADINTERNALERROR;
+            fieldMetaData->description = UA_LOCALIZEDTEXT_ALLOC("", "");
+            fieldMetaData->dataSetFieldId = UA_GUID_NULL;
+
+            //ToDo after freeze PR, the value source must be checked (other behavior for static value source)
+            if(field->config.field.variable.staticValueSourceEnabled){
+                fieldMetaData->arrayDimensions = (UA_UInt32 *) UA_calloc(
+                        field->config.field.variable.staticValueSource.value.arrayDimensionsSize, sizeof(UA_UInt32));
+                if(fieldMetaData->arrayDimensions == NULL)
+                    return UA_STATUSCODE_BADOUTOFMEMORY;
+                memcpy(fieldMetaData->arrayDimensions, field->config.field.variable.staticValueSource.value.arrayDimensions,
+                        sizeof(UA_UInt32) *field->config.field.variable.staticValueSource.value.arrayDimensionsSize);
+                fieldMetaData->arrayDimensionsSize = field->config.field.variable.staticValueSource.value.arrayDimensionsSize;
+                if(UA_NodeId_copy(&field->config.field.variable.staticValueSource.value.type->typeId,
+                        &fieldMetaData->dataType) != UA_STATUSCODE_GOOD){
+                    if(fieldMetaData->arrayDimensions){
+                        UA_free(fieldMetaData->arrayDimensions);
+                        return UA_STATUSCODE_BADINTERNALERROR;
+                    }
+                }
+                fieldMetaData->properties = NULL;
+                fieldMetaData->propertiesSize = 0;
+                //TODO collect value rank for the static field source
+                fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_NONE;
+                return UA_STATUSCODE_GOOD;
+            }
+            UA_Variant value;
+            UA_Variant_init(&value);
+            if(UA_Server_readArrayDimensions(server, field->config.field.variable.publishParameters.publishedVariable,
+                                             &value) != UA_STATUSCODE_GOOD){
+                UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                               "PubSub meta data generation. Reading ArrayDimension failed.");
+            } else {
+                fieldMetaData->arrayDimensions = (UA_UInt32 *) UA_calloc(value.arrayDimensionsSize, sizeof(UA_UInt32));
+                if(fieldMetaData->arrayDimensions == NULL)
+                    return UA_STATUSCODE_BADOUTOFMEMORY;
+                memcpy(fieldMetaData->arrayDimensions, value.arrayDimensions, sizeof(UA_UInt32)*value.arrayDimensionsSize);
+                fieldMetaData->arrayDimensionsSize = value.arrayDimensionsSize;
+            }
+            if(UA_Server_readDataType(server, field->config.field.variable.publishParameters.publishedVariable,
+                                      &fieldMetaData->dataType) != UA_STATUSCODE_GOOD){
+                if(fieldMetaData->arrayDimensions){
+                    UA_free(fieldMetaData->arrayDimensions);
+                    return UA_STATUSCODE_BADINTERNALERROR;
+                }
+            }
+            fieldMetaData->properties = NULL;
+            fieldMetaData->propertiesSize = 0;
+            UA_Int32 valueRank;
+            if(UA_Server_readValueRank(server, field->config.field.variable.publishParameters.publishedVariable,
+                                       &valueRank) != UA_STATUSCODE_GOOD){
+                if(fieldMetaData->arrayDimensions){
+                    UA_free(fieldMetaData->arrayDimensions);
+                    return UA_STATUSCODE_BADINTERNALERROR;
+                }
+            }
+            fieldMetaData->valueRank = valueRank;
+            if(field->config.field.variable.promotedField){
+                fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_PROMOTEDFIELD;
+            } else {
+                fieldMetaData->fieldFlags = UA_DATASETFIELDFLAGS_NONE;
+            }
+            //TODO collect the following fields
+            //fieldMetaData.builtInType
+            //fieldMetaData.maxStringLength
+            return UA_STATUSCODE_GOOD;
+        case UA_PUBSUB_DATASETFIELD_EVENT:
+            return UA_STATUSCODE_BADNOTSUPPORTED;
+        default:
+            return UA_STATUSCODE_BADNOTSUPPORTED;
+    }
+}
+
 UA_DataSetFieldResult
 UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet,
                           const UA_DataSetFieldConfig *fieldConfig,
@@ -452,6 +546,23 @@ UA_Server_addDataSetField(UA_Server *server, const UA_NodeId publishedDataSet,
     if(newField->config.field.variable.promotedField)
         currentDataSet->promotedFieldsCount++;
     currentDataSet->fieldSize++;
+
+    //generate fieldMetadata within the DataSetMetaData
+    currentDataSet->dataSetMetaData.fieldsSize++;
+    UA_FieldMetaData *fieldMetaData = (UA_FieldMetaData *) UA_realloc(currentDataSet->dataSetMetaData.fields, currentDataSet->dataSetMetaData.fieldsSize *
+            sizeof(UA_FieldMetaData));
+    if(!fieldMetaData){
+        result.result =  UA_STATUSCODE_BADOUTOFMEMORY;
+        return result;
+    }
+    currentDataSet->dataSetMetaData.fields = fieldMetaData;
+
+    UA_FieldMetaData_init(&fieldMetaData[currentDataSet->fieldSize-1]);
+    if(generateFieldMetaData(server, newField, &fieldMetaData[currentDataSet->fieldSize-1]) != UA_STATUSCODE_GOOD){
+        UA_Server_removeDataSetField(server, newField->identifier);
+        result.result =  UA_STATUSCODE_BADINTERNALERROR;
+        return result;
+    }
     result.result = retVal;
     result.configurationVersion.majorVersion = currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
     result.configurationVersion.minorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
@@ -491,11 +602,49 @@ UA_Server_removeDataSetField(UA_Server *server, const UA_NodeId dsf) {
     parentPublishedDataSet->dataSetMetaData.configurationVersion.majorVersion =
         UA_PubSubConfigurationVersionTimeDifference();
 
+    currentField->fieldMetaData.arrayDimensions = NULL;
+    currentField->fieldMetaData.properties = NULL;
+    currentField->fieldMetaData.name = UA_STRING_NULL;
+    currentField->fieldMetaData.description.locale = UA_STRING_NULL;
+    currentField->fieldMetaData.description.text = UA_STRING_NULL;
     UA_DataSetField_clear(currentField);
     TAILQ_REMOVE(&parentPublishedDataSet->fields, currentField, listEntry);
     UA_free(currentField);
 
     result.result = UA_STATUSCODE_GOOD;
+    //regenerate DataSetMetaData
+    parentPublishedDataSet->dataSetMetaData.fieldsSize--;
+    if(parentPublishedDataSet->dataSetMetaData.fieldsSize > 0){
+        for(size_t i = 0; i < parentPublishedDataSet->dataSetMetaData.fieldsSize+1; i++) {
+            UA_FieldMetaData_clear(&parentPublishedDataSet->dataSetMetaData.fields[i]);
+        }
+        UA_free(parentPublishedDataSet->dataSetMetaData.fields);
+        UA_FieldMetaData *fieldMetaData = (UA_FieldMetaData *) UA_calloc(parentPublishedDataSet->dataSetMetaData.fieldsSize,
+                                                                         sizeof(UA_FieldMetaData));
+
+        if(!fieldMetaData){
+            result.result =  UA_STATUSCODE_BADOUTOFMEMORY;
+            return result;
+        }
+        UA_DataSetField *tmpDSF;
+        size_t counter = 0;
+        TAILQ_FOREACH(tmpDSF, &parentPublishedDataSet->fields, listEntry){
+            UA_FieldMetaData tmpFieldMetaData;
+            UA_FieldMetaData_init(&tmpFieldMetaData);
+            if(generateFieldMetaData(server, tmpDSF, &tmpFieldMetaData) != UA_STATUSCODE_GOOD){
+                UA_LOG_WARNING(&server->config.logger, UA_LOGCATEGORY_SERVER,
+                               "PubSub MetaData generation failed!");
+                //TODO how to ensure consistency if the metadata regeneration fails
+                result.result = UA_STATUSCODE_BADINTERNALERROR;
+            }
+            fieldMetaData[counter++] = tmpFieldMetaData;
+        }
+        parentPublishedDataSet->dataSetMetaData.fields = fieldMetaData;
+    } else {
+        UA_FieldMetaData_delete(parentPublishedDataSet->dataSetMetaData.fields);
+        parentPublishedDataSet->dataSetMetaData.fields = NULL;
+    }
+
     result.configurationVersion.majorVersion = parentPublishedDataSet->dataSetMetaData.configurationVersion.majorVersion;
     result.configurationVersion.minorVersion = parentPublishedDataSet->dataSetMetaData.configurationVersion.minorVersion;
     return result;

+ 15 - 0
tests/pubsub/check_pubsub_config_freeze.c

@@ -75,6 +75,21 @@ START_TEST(CreateAndLockConfiguration) {
     UA_WriterGroup *writerGroup = UA_WriterGroup_findWGbyId(server, writerGroup1);
     ck_assert(writerGroup->state == UA_PUBSUBSTATE_DISABLED);
 
+    UA_DataSetMetaDataType dataSetMetaDataType; 
+    UA_DataSetMetaDataType_init(&dataSetMetaDataType);
+    ck_assert(UA_Server_getPublishedDataSetMetaData(server, publishedDataSet1, &dataSetMetaDataType) == UA_STATUSCODE_GOOD);
+    UA_DataSetMetaDataType_deleteMembers(&dataSetMetaDataType);
+
+    UA_PublishedDataSetConfig publishedDataSetConfig;
+    memset(&publishedDataSetConfig, 0, sizeof(UA_PublishedDataSetConfig));
+    publishedDataSetConfig.publishedDataSetType = UA_PUBSUB_DATASET_PUBLISHEDITEMS_TEMPLATE;
+    UA_DataSetMetaDataType metaDataType; 
+    UA_DataSetMetaDataType_init(&metaDataType);
+    publishedDataSetConfig.config.itemsTemplate.metaData = metaDataType;
+    publishedDataSetConfig.config.itemsTemplate.variablesToAddSize = 1;
+    publishedDataSetConfig.config.itemsTemplate.variablesToAdd = UA_PublishedVariableDataType_new();
+    UA_PublishedDataSetConfig_clear(&publishedDataSetConfig);
+
     UA_DataSetWriterConfig dataSetWriterConfig;
     memset(&dataSetWriterConfig, 0, sizeof(dataSetWriterConfig));
     dataSetWriterConfig.name = UA_STRING("DataSetWriter 1");