From e9d9bfbac2bda5fa831a870e78614387b2c65d31 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 12 Sep 2020 20:04:45 +0200 Subject: [PATCH] - fixed problems in handling array elements and array element components --- .../mhai_array.cid | 14 ++ .../static_model.c | 73 +++++++++- .../static_model.h | 2 +- src/iec61850/server/impl/ied_server.c | 54 +++++++- src/iec61850/server/mms_mapping/mms_mapping.c | 46 ++++++- src/mms/inc_private/mms_client_internal.h | 3 + src/mms/inc_private/mms_server_libinternal.h | 3 + src/mms/inc_private/mms_value_cache.h | 2 +- src/mms/iso_mms/client/mms_client_common.c | 9 +- .../client/mms_client_named_variable_list.c | 56 ++------ .../iso_mms/server/mms_named_variable_list.c | 2 + .../server/mms_named_variable_list_service.c | 125 ++++++++++++++++-- src/mms/iso_mms/server/mms_read_service.c | 62 ++++++++- src/mms/iso_mms/server/mms_server.c | 13 +- src/mms/iso_mms/server/mms_value_cache.c | 21 ++- tools/model_generator/genmodel.jar | Bin 94643 -> 94879 bytes .../tools/StaticModelGenerator.java | 39 +++++- 17 files changed, 437 insertions(+), 87 deletions(-) diff --git a/examples/server_example_complex_array/mhai_array.cid b/examples/server_example_complex_array/mhai_array.cid index be81a20d..0b588518 100644 --- a/examples/server_example_complex_array/mhai_array.cid +++ b/examples/server_example_complex_array/mhai_array.cid @@ -102,6 +102,20 @@ + + + + + + + + + + + + + + 16 diff --git a/examples/server_example_complex_array/static_model.c b/examples/server_example_complex_array/static_model.c index d9bc2a48..3f5a6f14 100644 --- a/examples/server_example_complex_array/static_model.c +++ b/examples/server_example_complex_array/static_model.c @@ -1,14 +1,79 @@ /* * static_model.c * - * automatically generated from mhai_array.icd + * automatically generated from mhai_array.cid */ #include "static_model.h" static void initializeValues(); +extern DataSet iedModelds_ComplexArray_MHAI1_TestMHAI; +extern DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda0; +extern DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda1; +extern DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda2; +extern DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda3; +extern DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda4; + +DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda0 = { + "ComplexArray", + false, + "MHAI1$MX$HA$phsAHar", + 7, + NULL, + NULL, + &iedModelds_ComplexArray_MHAI1_TestMHAI_fcda1 +}; + +DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda1 = { + "ComplexArray", + false, + "MHAI1$MX$HA$phsAHar", + 8, + NULL, + NULL, + &iedModelds_ComplexArray_MHAI1_TestMHAI_fcda2 +}; + +DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda2 = { + "ComplexArray", + false, + "MHAI1$MX$HA$phsAHar", + 9, + "cVal", + NULL, + &iedModelds_ComplexArray_MHAI1_TestMHAI_fcda3 +}; + +DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda3 = { + "ComplexArray", + false, + "MHAI1$MX$HA$phsAHar", + 10, + "cVal$mag", + NULL, + &iedModelds_ComplexArray_MHAI1_TestMHAI_fcda4 +}; + +DataSetEntry iedModelds_ComplexArray_MHAI1_TestMHAI_fcda4 = { + "ComplexArray", + false, + "MHAI1$MX$HA$phsAHar", + 11, + "cVal$mag$f", + NULL, + NULL +}; + +DataSet iedModelds_ComplexArray_MHAI1_TestMHAI = { + "ComplexArray", + "MHAI1$TestMHAI", + 5, + &iedModelds_ComplexArray_MHAI1_TestMHAI_fcda0, + NULL +}; + LogicalDevice iedModel_ComplexArray = { LogicalDeviceModelType, "ComplexArray", @@ -1977,7 +2042,9 @@ DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency = { NULL, 0}; +extern ReportControlBlock iedModel_ComplexArray_MHAI1_report0; +ReportControlBlock iedModel_ComplexArray_MHAI1_report0 = {&iedModel_ComplexArray_MHAI1, "MHAIRCB01", "TestMHAI", false, "TestMHAI", 1, 24, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL}; @@ -1988,8 +2055,8 @@ DataAttribute iedModel_ComplexArray_MHAI1_HA_frequency = { IedModel iedModel = { "test", &iedModel_ComplexArray, - NULL, - NULL, + &iedModelds_ComplexArray_MHAI1_TestMHAI, + &iedModel_ComplexArray_MHAI1_report0, NULL, NULL, NULL, diff --git a/examples/server_example_complex_array/static_model.h b/examples/server_example_complex_array/static_model.h index 18f5206e..d6bb6179 100644 --- a/examples/server_example_complex_array/static_model.h +++ b/examples/server_example_complex_array/static_model.h @@ -1,7 +1,7 @@ /* * static_model.h * - * automatically generated from mhai_array.icd + * automatically generated from mhai_array.cid */ #ifndef STATIC_MODEL_H_ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 38428134..b64c37f1 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -377,18 +377,64 @@ updateDataSetsWithCachedValues(IedServer self) MmsDomain* domain = MmsDevice_getDomain(self->mmsDevice, domainName); - MmsValue* value = MmsServer_getValueFromCache(self->mmsServer, domain, dataSetEntry->variableName); + char variableName[66]; + + strncpy(variableName, dataSetEntry->variableName, 65); + variableName[65] = 0; + + MmsVariableSpecification* typeSpec = NULL; + + MmsValue* value = MmsServer_getValueFromCacheEx(self->mmsServer, domain, variableName, &typeSpec); if (value == NULL) { if (DEBUG_IED_SERVER) { - printf("LD: %s dataset: %s : error cannot get value from cache for %s -> %s!\n", + printf("IED_SERVER: LD: %s dataset: %s : error cannot get value from cache for %s -> %s!\n", dataSet->logicalDeviceName, dataSet->name, dataSetEntry->logicalDeviceName, dataSetEntry->variableName); } } - else - dataSetEntry->value = value; + else { + /* check if array element */ + + if (dataSetEntry->index != -1) { + if (typeSpec->type == MMS_ARRAY) { + MmsValue* elementValue = MmsValue_getElement(value, dataSetEntry->index); + + if (elementValue) { + + if (dataSetEntry->componentName) { + MmsVariableSpecification* elementType = typeSpec->typeSpec.array.elementTypeSpec; + + MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, dataSetEntry->componentName); + + if (subElementValue) { + dataSetEntry->value = subElementValue; + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - component %s of array element not found\n", dataSetEntry->componentName); + } + + } + else { + dataSetEntry->value = elementValue; + } + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - array element %i not found\n", dataSetEntry->index); + } + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - variable %s/%s is not an array\n", dataSetEntry->logicalDeviceName, dataSetEntry->variableName); + } + } + else { + dataSetEntry->value = value; + } + } dataSetEntry = dataSetEntry->sibling; } diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index aa9ed330..fd753fa9 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3144,7 +3144,6 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi /* use variable name part of domain name as logicalDeviceName */ dataSetEntry->logicalDeviceName = MmsDomain_getName(listEntry->domain) + strlen(self->model->name); - dataSetEntry->variableName = listEntry->variableName; dataSetEntry->index = listEntry->arrayIndex; dataSetEntry->componentName = listEntry->componentName; @@ -3155,8 +3154,49 @@ MmsMapping_createDataSetByNamedVariableList(MmsMapping* self, MmsNamedVariableLi else lastDataSetEntry->sibling = dataSetEntry; - dataSetEntry->value = - MmsServer_getValueFromCache(self->mmsServer, listEntry->domain, listEntry->variableName); + MmsVariableSpecification* dataSetEntryVarSpec = NULL; + + MmsValue* dataSetEntryValue = MmsServer_getValueFromCacheEx(self->mmsServer, listEntry->domain, listEntry->variableName, &dataSetEntryVarSpec); + + if (dataSetEntryValue) { + if (dataSetEntry->index != -1) { + if (dataSetEntryVarSpec->type == MMS_ARRAY) { + MmsValue* elementValue = MmsValue_getElement(dataSetEntryValue, dataSetEntry->index); + + if (elementValue) { + + if (dataSetEntry->componentName) { + MmsVariableSpecification* elementType = dataSetEntryVarSpec->typeSpec.array.elementTypeSpec; + + MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, dataSetEntry->componentName); + + if (subElementValue) { + dataSetEntry->value = subElementValue; + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - component %s of array element not found\n", dataSetEntry->componentName); + } + + } + else { + dataSetEntry->value = elementValue; + } + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - array element %i not found\n", dataSetEntry->index); + } + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - variable %s/%s is not an array\n", dataSetEntry->logicalDeviceName, dataSetEntry->variableName); + } + } + else { + dataSetEntry->value = dataSetEntryValue; + } + } lastDataSetEntry = dataSetEntry; diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 6bc4455c..a1b14e5f 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -177,6 +177,9 @@ mmsClient_createAlternateAccess(uint32_t index, uint32_t elementCount); LIB61850_INTERNAL void mmsClient_deleteAlternateAccess(AlternateAccess_t* alternateAccess); +LIB61850_INTERNAL AlternateAccess_t* +mmsClient_createAlternateAccessComponent(const char* componentName); + LIB61850_INTERNAL void mmsClient_deleteAlternateAccessIndexComponent(AlternateAccess_t* alternateAccess); diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index b516513a..b939abb5 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -73,6 +73,9 @@ MmsServer_getDevice(MmsServer self); LIB61850_INTERNAL MmsValue* MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, const char* itemId); +LIB61850_INTERNAL MmsValue* +MmsServer_getValueFromCacheEx(MmsServer self, MmsDomain* domain, const char* itemId, MmsVariableSpecification** typeSpec); + LIB61850_INTERNAL bool MmsServer_isLocked(MmsServer self); diff --git a/src/mms/inc_private/mms_value_cache.h b/src/mms/inc_private/mms_value_cache.h index 8c89be4e..bdacbb27 100644 --- a/src/mms/inc_private/mms_value_cache.h +++ b/src/mms/inc_private/mms_value_cache.h @@ -36,7 +36,7 @@ LIB61850_INTERNAL void MmsValueCache_insertValue(MmsValueCache self, char* itemId, MmsValue* value); LIB61850_INTERNAL MmsValue* -MmsValueCache_lookupValue(MmsValueCache self, const char* itemId); +MmsValueCache_lookupValue(MmsValueCache self, const char* itemId, MmsVariableSpecification** outSpec); LIB61850_INTERNAL void MmsValueCache_destroy(MmsValueCache self); diff --git a/src/mms/iso_mms/client/mms_client_common.c b/src/mms/iso_mms/client/mms_client_common.c index 1b5e1972..11a92994 100644 --- a/src/mms/iso_mms/client/mms_client_common.c +++ b/src/mms/iso_mms/client/mms_client_common.c @@ -137,8 +137,8 @@ mmsClient_deleteAlternateAccess(AlternateAccess_t* alternateAccess) GLOBAL_FREEMEM(alternateAccess); } -static AlternateAccess_t* -createAlternateAccessComponent(const char* componentName) +AlternateAccess_t* +mmsClient_createAlternateAccessComponent(const char* componentName) { AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); alternateAccess->list.count = 1; @@ -161,7 +161,7 @@ createAlternateAccessComponent(const char* componentName) (uint8_t*) StringUtils_copySubString((char*) componentName, (char*) separator); alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.size = size; - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(separator + 1); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = mmsClient_createAlternateAccessComponent(separator + 1); } else { int size = strlen(componentName); @@ -201,7 +201,7 @@ mmsClient_createAlternateAccessIndexComponent(uint32_t index, const char* compon asn_long2INTEGER(asnIndex, index); - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(componentName); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = mmsClient_createAlternateAccessComponent(componentName); } else { alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; @@ -217,3 +217,4 @@ mmsClient_createAlternateAccessIndexComponent(uint32_t index, const char* compon return alternateAccess; } + diff --git a/src/mms/iso_mms/client/mms_client_named_variable_list.c b/src/mms/iso_mms/client/mms_client_named_variable_list.c index 630eb9dd..3ca329e1 100644 --- a/src/mms/iso_mms/client/mms_client_named_variable_list.c +++ b/src/mms/iso_mms/client/mms_client_named_variable_list.c @@ -354,49 +354,12 @@ mmsClient_createDefineNamedVariableListRequest( domainspecific.itemId.buf = (uint8_t*) StringUtils_copyString(variableSpec->itemId); if (variableSpec->arrayIndex != -1) { - - AlternateAccess_t* alternateAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); - alternateAccess->list.count = 1; - alternateAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); - alternateAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); - - alternateAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; - alternateAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - - alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; - - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = - AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index; - - asn_long2INTEGER(&(alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), - variableSpec->arrayIndex); - - if (variableSpec->componentName != NULL) { - - AlternateAccess_t* componentAccess = (AlternateAccess_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccess_t)); - - componentAccess->list.count = 1; - componentAccess->list.array = (struct AlternateAccess__Member**) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member*)); - componentAccess->list.array[0] = (struct AlternateAccess__Member*) GLOBAL_CALLOC(1, sizeof(struct AlternateAccess__Member)); - - componentAccess->list.array[0]->present = AlternateAccess__Member_PR_unnamed; - componentAccess->list.array[0]->choice.unnamed = (AlternateAccessSelection_t*) GLOBAL_CALLOC(1, sizeof(AlternateAccessSelection_t)); - - componentAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; - componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = - AlternateAccessSelection__selectAccess_PR_component; - - Identifier_t* componentIdentifier = - &(componentAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component); - - componentIdentifier->size = strlen(variableSpec->componentName); - componentIdentifier->buf = (uint8_t*) StringUtils_copyString(variableSpec->componentName); - - alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess - = componentAccess; + if (variableSpec->componentName) { + request->listOfVariable.list.array[i]->alternateAccess = mmsClient_createAlternateAccessIndexComponent(variableSpec->arrayIndex, variableSpec->componentName); + } + else { + request->listOfVariable.list.array[i]->alternateAccess = mmsClient_createAlternateAccess(variableSpec->arrayIndex, 0); } - - request->listOfVariable.list.array[i]->alternateAccess = alternateAccess; } element = LinkedList_getNext(element); @@ -404,9 +367,16 @@ mmsClient_createDefineNamedVariableListRequest( i++; } - der_encode(&asn_DEF_MmsPdu, mmsPdu, + asn_enc_rval_t rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + if (rval.encoded == -1) { + writeBuffer->size = 0; + + if (DEBUG_MMS_SERVER) + printf("MMS_CLIENT: createDefineNamedVariableListRequest - failed to encode request!\n"); + } + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); } diff --git a/src/mms/iso_mms/server/mms_named_variable_list.c b/src/mms/iso_mms/server/mms_named_variable_list.c index 69770a97..31694780 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list.c +++ b/src/mms/iso_mms/server/mms_named_variable_list.c @@ -46,6 +46,8 @@ void MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self) { GLOBAL_FREEMEM(self->variableName); + if (self->componentName) + GLOBAL_FREEMEM(self->componentName); GLOBAL_FREEMEM(self); } diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 72d30f90..a8039236 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -1,7 +1,7 @@ /* * mms_named_variable_list_service.c * - * Copyright 2013-2015 Michael Zillgith + * Copyright 2013-2020 Michael Zillgith * * This file is part of libIEC61850. * @@ -23,6 +23,7 @@ #include "libiec61850_platform_includes.h" #include "mms_server_internal.h" +#include "mms_client_internal.h" #include "mms_named_variable_list.h" #include "ber_encoder.h" @@ -281,6 +282,8 @@ checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier) return false; if (accessSpecifier->componentName != NULL) { + variableSpec = variableSpec->typeSpec.array.elementTypeSpec; + if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL) return false; } @@ -289,6 +292,74 @@ checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier) return true; } +static char* +getComponentNameFromAlternateAccess(AlternateAccess_t* alternateAccess, char* componentNameBuf, int nameBufPos) +{ + if (alternateAccess->list.count == 1) { + + if (alternateAccess->list.array[0]->present == AlternateAccess__Member_PR_unnamed) { + + if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) { + + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component) + { + Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed-> + choice.selectAlternateAccess.accessSelection.choice.component; + + AlternateAccess_t* nextAlternateAccess = alternateAccess->list.array[0]->choice.unnamed-> + choice.selectAlternateAccess.alternateAccess; + + if (nextAlternateAccess) { + if (nameBufPos + componentIdentifier.size + 1 < 65) { + memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size); + nameBufPos += componentIdentifier.size; + componentNameBuf[nameBufPos++] = '$'; + return getComponentNameFromAlternateAccess(nextAlternateAccess, componentNameBuf, nameBufPos); + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: component identifier name too long!\n"); + } + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: next alternate access specification is missing!\n"); + } + } + } + else if (alternateAccess->list.array[0]->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) { + + /* final component part */ + + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present == + AlternateAccessSelection__selectAccess_PR_component) + { + Identifier_t componentIdentifier = alternateAccess->list.array[0]->choice.unnamed-> + choice.selectAccess.choice.component; + + if (nameBufPos + componentIdentifier.size + 1 < 65) { + memcpy(componentNameBuf + nameBufPos, componentIdentifier.buf, componentIdentifier.size); + nameBufPos += componentIdentifier.size; + componentNameBuf[nameBufPos++] = 0; + return componentNameBuf; + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: component identifier name too long!\n"); + } + } + } + + } + + } + + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: invalid component access specification\n"); + + return NULL; +} static MmsNamedVariableList createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, @@ -299,6 +370,9 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, int variableCount = request->listOfVariable.list.count; + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries); + #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) { #else @@ -324,6 +398,10 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, if (request->listOfVariable.list.array[i]->alternateAccess != NULL) { if (request->listOfVariable.list.array[i]->alternateAccess->list.count != 1) { + + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n"); + MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; break; @@ -334,21 +412,31 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, request->listOfVariable.list.array[i]->alternateAccess->list.array[0]; if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) - &&(alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)) { asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), &arrayIndex); - Identifier_t componentIdentifier = alternateAccess->choice.unnamed-> - choice.selectAlternateAccess.alternateAccess->list.array[0]-> - choice.unnamed->choice.selectAccess.choice.component; - - componentName = - StringUtils_createStringFromBufferInBuffer(componentNameBuf, - componentIdentifier.buf, componentIdentifier.size); + if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) { + componentNameBuf[0] = 0; + componentName = getComponentNameFromAlternateAccess( + alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + componentNameBuf, 0); + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n"); + } + } + else if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) + && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) + ) + { + asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex); } else { MmsNamedVariableList_destroy(namedVariableList); @@ -356,9 +444,7 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, *mmsError = MMS_ERROR_DEFINITION_INVALID_ADDRESS; break; } - } - } if (varSpec->present == VariableSpecification_PR_name) { @@ -383,15 +469,21 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, accessSpecifier.arrayIndex = arrayIndex; accessSpecifier.componentName = componentName; + if (DEBUG_MMS_SERVER) + printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName); + /* check if element exists */ if (checkIfVariableExists(device, &accessSpecifier) == true) { - MmsNamedVariableListEntry variable = MmsNamedVariableListEntry_create(accessSpecifier); MmsNamedVariableList_addVariable(namedVariableList, variable); } else { + + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: failed - variable does not exist!\n"); + MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; i = variableCount; /* exit loop after freeing loop variables */ @@ -655,6 +747,15 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, varListResponse->listOfVariable.list.array[i]->variableSpecification.choice.name.choice. domainspecific.itemId.size = strlen(variableEntry->variableName); + if (variableEntry->arrayIndex != -1) { + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName); + } + else if (variableEntry->componentName) { + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessComponent(variableEntry->componentName); + } + variable = LinkedList_getNext(variable); } diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index dba52d1b..3bffbd04 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -270,9 +270,6 @@ alternateArrayAccess(MmsServerConnection connection, int lowIndex = mmsServer_getLowIndex(alternateAccess); int numberOfElements = mmsServer_getNumberOfElements(alternateAccess); - if (DEBUG_MMS_SERVER) printf("Alternate access index: %i elements %i\n", - lowIndex, numberOfElements); - int index = lowIndex; MmsValue* arrayValue = mmsServer_getValue(connection->server, domain, itemId, connection); @@ -687,6 +684,61 @@ exit: #if (MMS_DATA_SET_SERVICE == 1) +static void +addNamedVariableToNamedVariableListResultList(MmsVariableSpecification* namedVariable, MmsDomain* domain, char* nameIdStr, + LinkedList /**/ values, MmsServerConnection connection, MmsNamedVariableListEntry listEntry) +{ + if (namedVariable != NULL) { + + if (DEBUG_MMS_SERVER) + printf("MMS read: found named variable %s with search string %s\n", + namedVariable->name, nameIdStr); + + MmsValue* value = mmsServer_getValue(connection->server, domain, nameIdStr, connection); + + if (value) { + if (listEntry->arrayIndex != -1) { + if (MmsValue_getType(value) == MMS_ARRAY) { + + MmsValue* elementValue = MmsValue_getElement(value, listEntry->arrayIndex); + + if (listEntry->componentName) { + MmsVariableSpecification* elementType = namedVariable->typeSpec.array.elementTypeSpec; + + MmsValue* subElementValue = MmsVariableSpecification_getChildValue(elementType, elementValue, listEntry->componentName); + + if (subElementValue) { + appendValueToResultList(subElementValue, values); + } + else { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: ERROR - component %s of array element not found\n", listEntry->componentName); + } + } + else { + appendValueToResultList(elementValue, values); + } + + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: data set entry of unexpected type!\n"); + + appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); + } + } + else { + appendValueToResultList(value, values); + } + } + else { + appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); + } + } + else + appendErrorToResultList(values, DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT); +} + static void createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariableList namedList, int invokeId, ByteBuffer* response, ReadRequest_t* read, VarAccessSpec* accessSpec) @@ -711,8 +763,8 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(variableDomain, variableName); - addNamedVariableToResultList(namedVariable, variableDomain, variableName, - values, connection, NULL); + addNamedVariableToNamedVariableListResultList(namedVariable, variableDomain, variableName, + values, connection, variableListEntry); variable = LinkedList_getNext(variable); } diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index b5af1737..93622031 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -323,7 +323,18 @@ MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, const char* itemI MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); if (cache != NULL) - return MmsValueCache_lookupValue(cache, itemId); + return MmsValueCache_lookupValue(cache, itemId, NULL); + + return NULL ; +} + +MmsValue* +MmsServer_getValueFromCacheEx(MmsServer self, MmsDomain* domain, const char* itemId, MmsVariableSpecification** typeSpec) +{ + MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); + + if (cache != NULL) + return MmsValueCache_lookupValue(cache, itemId, typeSpec); return NULL ; } diff --git a/src/mms/iso_mms/server/mms_value_cache.c b/src/mms/iso_mms/server/mms_value_cache.c index 0af1d164..fcabff0f 100644 --- a/src/mms/iso_mms/server/mms_value_cache.c +++ b/src/mms/iso_mms/server/mms_value_cache.c @@ -89,7 +89,7 @@ getChildSubString (const char* itemId, char* parentId) } static MmsValue* -searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId) +searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId, MmsVariableSpecification** outSpec) { MmsValueCacheEntry* cacheEntry; MmsValue* value = NULL; @@ -100,7 +100,7 @@ searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId) char* parentItemId = getParentSubString(parentId); if (parentItemId != NULL) { - value = searchCacheForValue(self, itemId, parentItemId); + value = searchCacheForValue(self, itemId, parentItemId, outSpec); } } else { @@ -109,13 +109,18 @@ searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId) MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, parentId); value = MmsVariableSpecification_getChildValue(typeSpec, cacheEntry->value, childId); + + if (outSpec) { + *outSpec = MmsVariableSpecification_getNamedVariableRecursive(typeSpec, childId); + } + } return value; } MmsValue* -MmsValueCache_lookupValue(MmsValueCache self, const char* itemId) +MmsValueCache_lookupValue(MmsValueCache self, const char* itemId, MmsVariableSpecification** outSpec) { /* * get value for first matching key substring! @@ -133,14 +138,20 @@ MmsValueCache_lookupValue(MmsValueCache self, const char* itemId) char* parentItemId = getParentSubString(itemIdCopy); if (parentItemId != NULL) { - value = searchCacheForValue(self, itemId, parentItemId); + value = searchCacheForValue(self, itemId, parentItemId, outSpec); } GLOBAL_FREEMEM(itemIdCopy); } - if (cacheEntry != NULL) + if (cacheEntry != NULL) { + + if (outSpec) { + *outSpec = cacheEntry->typeSpec; + } + return cacheEntry->value; + } else return value; } diff --git a/tools/model_generator/genmodel.jar b/tools/model_generator/genmodel.jar index cbe2108bd08b45433a49327babdc14b358e1e699..c85e3a6c6be22bc2813816dd1fb5514cebb820a4 100644 GIT binary patch delta 18635 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 delta 18309 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 diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java index 4e1ae291..6198bb76 100644 --- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java @@ -1565,12 +1565,41 @@ public class StaticModelGenerator { if (fcda.getDaName() != null) mmsVariableName += "$" + toMmsString(fcda.getDaName()); - - // TODO implement index processing! + + int arrayStart = mmsVariableName.indexOf('('); + + String variableName = mmsVariableName; + int arrayIndex = -1; + String componentName = null; + + if (arrayStart != -1) { + variableName = mmsVariableName.substring(0, arrayStart); + + int arrayEnd = mmsVariableName.indexOf(')'); + + String arrayIndexStr = mmsVariableName.substring(arrayStart + 1, arrayEnd); + arrayIndex = Integer.parseInt(arrayIndexStr); + + String componentNamePart = mmsVariableName.substring(arrayEnd + 1); + + if ((componentNamePart != null) && (componentNamePart.length() > 0)) { + if (componentNamePart.charAt(0) == '$') { + componentNamePart = componentNamePart.substring(1); + } + + if ((componentNamePart != null) && (componentNamePart.length() > 0)) + componentName = componentNamePart; + } + } + cOut.println(" false,"); - cOut.println(" \"" + mmsVariableName + "\","); - cOut.println(" -1,"); - cOut.println(" NULL,"); + cOut.println(" \"" + variableName + "\", "); + cOut.println(" " + arrayIndex + ","); + if (componentName == null) + cOut.println(" NULL,"); + else + cOut.println(" \"" + componentName + "\","); + cOut.println(" NULL,"); if (fcdaCount + 1 < numberOfFcdas)