From 8b957b8f82fbc4b1c2c38a36012cffefac7bf0ec Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 31 Aug 2018 22:12:05 +0200 Subject: [PATCH] - IEC 61850 client: add support for single array element access (with component specification) --- src/iec61850/client/ied_connection.c | 30 ++++- src/mms/inc/mms_client_connection.h | 19 ++- src/mms/inc_private/mms_client_internal.h | 4 + .../iso_mms/client/mms_client_connection.c | 31 ++++- src/mms/iso_mms/client/mms_client_read.c | 114 ++++++++++++++++++ 5 files changed, 195 insertions(+), 3 deletions(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index f271dadc..de323ef4 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -694,7 +694,35 @@ IedConnection_readObject(IedConnection self, IedClientError* error, const char* MmsError mmsError; - value = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId); + /* check if item ID contains an array "(..)" */ + char* brace = strchr(itemId, '('); + + if (brace) { + char* secondBrace = strchr(brace, ')'); + + if (secondBrace) { + char* endPtr; + + int index = (int) strtol(brace + 1, &endPtr, 10); + + if (endPtr == secondBrace) { + char* component = NULL; + + if (strlen(secondBrace + 1) > 1) + component = secondBrace + 2; /* skip "." after array element specifier */ + + *brace = 0; + + value = MmsConnection_readSingleArrayElementWithComponent(self->connection, &mmsError, domainId, itemId, index, component); + } + else + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + } + else + *error = IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT; + } + else + value = MmsConnection_readVariable(self->connection, &mmsError, domainId, itemId); if (value != NULL) *error = IED_ERROR_OK; diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index d5bd40ed..f4d631b3 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -374,7 +374,7 @@ MmsValue* MmsConnection_readVariable(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId); /** - * \brief Read an element of a single array variable from the server. + * \brief Read one or more elements of a single array variable from the server. * * \param self MmsConnection instance to operate on * \param mmsError user provided variable to store error code @@ -391,6 +391,23 @@ MmsValue* MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, const char* domainId, const char* itemId, uint32_t startIndex, uint32_t numberOfElements); + +/** + * \brief Read a single element (with optional component specification) from the server + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be read + * \param itemId name of the variable to be read + * \param index array element index + * \param componentId array element component name + * + * \return Returns a MmsValue object or NULL if the request failed. + */ +MmsValue* +MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, uint32_t index, const char* componentId); + /** * \brief Read multiple variables of a domain from the server with one request message. * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 45c64c25..9ffdb731 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -164,6 +164,10 @@ int mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t index, uint32_t elementCount, ByteBuffer* writeBuffer); +int +mmsClient_createReadRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, + uint32_t index, const char* component, ByteBuffer* writeBuffer); + int mmsClient_createReadRequestMultipleValues(uint32_t invokeId, const char* domainId, LinkedList /**/ items, ByteBuffer* writeBuffer); diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 49f8f0df..b6a05546 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -1615,7 +1615,36 @@ MmsConnection_readArrayElements(MmsConnection self, MmsError* mmsError, releaseResponse(self); - exit_function: +exit_function: + return value; +} + +MmsValue* +MmsConnection_readSingleArrayElementWithComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, uint32_t index, const char* componentId) +{ + MmsValue* value = NULL; + + if (getAssociationState(self) != MMS_STATE_CONNECTED) { + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + uint32_t invokeId = getNextInvokeId(self); + + mmsClient_createReadRequestAlternateAccessSingleIndexComponent(invokeId, domainId, itemId, index, componentId, + payload); + + ByteBuffer* responseMessage = sendRequestAndWaitForResponse(self, invokeId, payload, mmsError); + + if (responseMessage != NULL) + value = mmsClient_parseReadResponse(self->lastResponse, NULL, false); + + releaseResponse(self); + +exit_function: return value; } diff --git a/src/mms/iso_mms/client/mms_client_read.c b/src/mms/iso_mms/client/mms_client_read.c index 2aea38c0..8f68f131 100644 --- a/src/mms/iso_mms/client/mms_client_read.c +++ b/src/mms/iso_mms/client/mms_client_read.c @@ -461,6 +461,85 @@ createAlternateAccess(uint32_t index, uint32_t elementCount) return alternateAccess; } +static AlternateAccess_t* +createAlternateAccessComponent(const char* componentName) +{ + 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)); + + char* separator = strchr(componentName, '$'); + + if (separator) { + int size = separator - componentName; + + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAlternateAccess; + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.present = + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_component; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.component.buf = (uint8_t*) strndup(componentName, size); + 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); + } + else { + int size = strlen(componentName); + + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_component; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.buf = (uint8_t*) strndup(componentName, size); + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component.size = size; + } + + return alternateAccess; +} + +static AlternateAccess_t* +createAlternateAccessIndexComponent(uint32_t index, const char* componentName) +{ + 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)); + + if (componentName) { + 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; + + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + + asn_long2INTEGER(asnIndex, index); + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess = createAlternateAccessComponent(componentName); + } + else { + alternateAccess->list.array[0]->choice.unnamed->present = AlternateAccessSelection_PR_selectAccess; + + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.present = + AlternateAccessSelection__selectAccess_PR_index; + + INTEGER_t* asnIndex = + &(alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.index); + + asn_long2INTEGER(asnIndex, index); + } + + return alternateAccess; +} + static ListOfVariableSeq_t* createVariableIdentifier(const char* domainId, const char* itemId) { @@ -511,6 +590,41 @@ mmsClient_createReadRequestAlternateAccessIndex(uint32_t invokeId, const char* d return rval.encoded; } +int +mmsClient_createReadRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, + uint32_t index, const char* component, ByteBuffer* writeBuffer) +{ + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + ReadRequest_t* readRequest = createReadRequest(mmsPdu); + + readRequest->specificationWithResult = NULL; + + readRequest->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + + readRequest->variableAccessSpecification.choice.listOfVariable.list.array = (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + readRequest->variableAccessSpecification.choice.listOfVariable.list.count = 1; + + ListOfVariableSeq_t* variableIdentifier = createVariableIdentifier(domainId, itemId); + + readRequest->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; + + variableIdentifier->alternateAccess = createAlternateAccessIndexComponent(index, component); + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.domainId.size = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.buf = 0; + variableIdentifier->variableSpecification.choice.name.choice.domainspecific.itemId.size = 0; + + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; +} + static ListOfVariableSeq_t** createListOfVariables(ReadRequest_t* readRequest, int valuesCount) {