From 394bf4ccbadbae75eaaa8738108786ff6c29d6e2 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 20 Oct 2021 10:52:49 +0200 Subject: [PATCH] - IED client: added function IedConnection_createDataSetAsync --- src/iec61850/client/ied_connection.c | 130 ++++++++++++++++++++++++++- src/iec61850/inc/iec61850_client.h | 25 ++++++ 2 files changed, 154 insertions(+), 1 deletion(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 89612b7f..8fa43915 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -2990,7 +2990,6 @@ void IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* */dataSetElements) { - char domainIdBuffer[65]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; @@ -3250,6 +3249,135 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons return call->invokeId; } +static void +createDataSetAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, bool success) +{ + IedConnection self = (IedConnection) parameter; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + + IedConnection_GenericServiceHandler handler = (IedConnection_GenericServiceHandler)call->callback; + + IedClientError err = iedConnection_mapMmsErrorToIedError(mmsError); + + if (err == IED_ERROR_OK) { + if (success == false) + err = IED_ERROR_ACCESS_DENIED; + } + + handler(invokeId, call->callbackParameter, err); + + iedConnection_releaseOutstandingCall(self, call); + } + else { + if (DEBUG_IED_CLIENT) + printf("IED_CLIENT: internal error - no matching outstanding call!\n"); + } +} + +uint32_t +IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, + IedConnection_GenericServiceHandler handler, void* parameter) +{ + int invokeId = 0; + + char domainIdBuffer[65]; + char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; + + const char* domainId; + const char* itemId; + bool isAssociationSpecific = false; + + if (dataSetReference[0] != '@') { + + if ((dataSetReference[0] == '/') || (strchr(dataSetReference, '/') == NULL)) { + domainId = NULL; + + if (dataSetReference[0] == '/') + itemId = dataSetReference + 1; + else + itemId = dataSetReference; + } + else { + domainId = MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainIdBuffer); + + if (domainId == NULL) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + goto exit_function; + } + + int domainIdLength = strlen(domainId); + + if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + goto exit_function; + } + + char* itemIdRef = StringUtils_copyStringToBuffer(dataSetReference + domainIdLength + 1, itemIdBuffer); + StringUtils_replace(itemIdRef, '.', '$'); + itemId = itemIdRef; + } + } + else { + itemId = dataSetReference + 1; + isAssociationSpecific = true; + } + + MmsError mmsError; + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + goto exit_function; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->invokeId = 0; + + LinkedList dataSetEntries = LinkedList_create(); + + LinkedList dataSetElement = LinkedList_getNext(dataSetElements); + + while (dataSetElement != NULL) { + + MmsVariableAccessSpecification* dataSetEntry = + MmsMapping_ObjectReferenceToVariableAccessSpec((char*) dataSetElement->data); + + if (dataSetEntry == NULL) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + goto cleanup_list; + } + + LinkedList_add(dataSetEntries, (void*) dataSetEntry); + + dataSetElement = LinkedList_getNext(dataSetElement); + } + + if (isAssociationSpecific) { + MmsConnection_defineNamedVariableListAssociationSpecificAsync(self->connection, &(call->invokeId), + &mmsError, itemId, dataSetEntries, createDataSetAsyncHandler, self); + } + else { + MmsConnection_defineNamedVariableListAsync(self->connection, &(call->invokeId), + &mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self); + } + + invokeId = call->invokeId; + + *error = iedConnection_mapMmsErrorToIedError(mmsError); + +cleanup_list: + /* delete list and all elements */ + LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); + +exit_function: + return invokeId; +} + LinkedList /* */ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index b596b88f..a534595e 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1759,6 +1759,31 @@ IedConnection_readDataSetValuesAsync(IedConnection self, IedClientError* error, LIB61850_API void IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements); +/** + * \brief create a new data set at the connected server device + * + * This function creates a new data set at the server. The parameter dataSetReference is the name of the new data set + * to create. It is either in the form LDName/LNodeName.dataSetName for permanent domain or VMD scope data sets or + * @dataSetName for an association specific data set. If the LDName part of the reference is missing the resulting + * data set will be of VMD scope. + * + * The dataSetElements parameter contains a linked list containing the object references of FCDs or FCDAs. The format of + * this object references is LDName/LNodeName.item(arrayIndex)component[FC]. + * + * \param connection the connection object + * \param error the error code if an error occurs + * \param dataSetReference object reference of the data set + * \param dataSetElements a list of object references defining the members of the new data set + * + * \param handler the callback handler that is called when the response is received or timeout + * \param parameter user provided parameter that is passed to the callback handler + * + * \return the invoke ID of the request + */ +LIB61850_API uint32_t +IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, + IedConnection_GenericServiceHandler handler, void* parameter); + /** * \brief delete a deletable data set at the connected server device *