From 36c65bd2e2295ee3315099d08d392f08f95d189c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 4 Dec 2021 18:37:10 +0100 Subject: [PATCH] - IedConnection: Add function IedConnection_getDataSetDirectoryAsync --- src/iec61850/client/ied_connection.c | 120 ++++++++++++++++++++++++++- src/iec61850/inc/iec61850_client.h | 32 ++++++- 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 6740623c..47b035f3 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -3280,7 +3280,7 @@ uint32_t IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, IedConnection_GenericServiceHandler handler, void* parameter) { - int invokeId = 0; + uint32_t invokeId = 0; char domainIdBuffer[65]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; @@ -3443,8 +3443,8 @@ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, con dataSetMembers = LinkedList_create(); - while (entry != NULL) { - MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*) entry->data; + while (entry) { + MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(entry); char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); @@ -3465,6 +3465,120 @@ exit_function: return dataSetMembers; } +static void +getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mmsError, LinkedList /* */ specs, bool deletable) +{ + IedConnection self = (IedConnection)parameter; + + IedClientError err = IED_ERROR_OK; + + IedConnectionOutstandingCall call = iedConnection_lookupOutstandingCall(self, invokeId); + + if (call) { + LinkedList dataSetMembers = NULL; + + if (mmsError != MMS_ERROR_NONE) + err = iedConnection_mapMmsErrorToIedError(mmsError); + + if (specs) { + dataSetMembers = LinkedList_create(); + LinkedList specElem = LinkedList_getNext(specs); + + while (specElem) { + MmsVariableAccessSpecification* varAccessSpec = (MmsVariableAccessSpecification*)LinkedList_getData(specElem); + + char* objectReference = MmsMapping_varAccessSpecToObjectReference(varAccessSpec); + + LinkedList_add(dataSetMembers, objectReference); + + specElem = LinkedList_getNext(specElem); + } + } + + IedConnection_GetDataSetDirectoryHandler handler = (IedConnection_GetDataSetDirectoryHandler)call->callback; + + if (handler) + handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable); + } + + if (specs) + LinkedList_destroyDeep(specs, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); +} + +uint32_t +IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, + IedConnection_GetDataSetDirectoryHandler handler, void* parameter) +{ + uint32_t invokeId = 0; + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + return 0; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->invokeId = 0; + + char domainIdBuffer[65]; + char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; + + const char* domainId = NULL; + const char* itemId = NULL; + + 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; + } + + const char* itemIdRef = dataSetReference + strlen(domainId) + 1; + + if (strlen(itemIdRef) > DATA_SET_MAX_NAME_LENGTH) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + goto exit_function; + } + + char* itemIdRefInBuffer = StringUtils_copyStringToBuffer(itemIdRef, itemIdBuffer); + StringUtils_replace(itemIdRefInBuffer, '.', '$'); + itemId = itemIdRefInBuffer; + } + } + else { + itemId = dataSetReference + 1; + isAssociationSpecific = true; + } + + MmsError mmsError = MMS_ERROR_NONE; + + + if (isAssociationSpecific) + MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self); + + else + MmsConnection_readNamedVariableListDirectoryAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, getDataSetDirectoryAsyncHandler, self); + + *error = iedConnection_mapMmsErrorToIedError(mmsError); + +exit_function: + return invokeId; +} + ClientDataSet IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const char* dataSetReference, ClientDataSet dataSet) diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index a534595e..e29d6d7c 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1801,7 +1801,7 @@ LIB61850_API bool IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const char* dataSetReference); /** - * \brief delete a deletable data set at the connected server device - async version + * \brief delete a deletable data set at the connected server device - asynchronous version * * This function deletes a data set at the server. The parameter dataSetReference is the name of the data set * to delete. It is either in the form LDName/LNodeName.dataSetName or @dataSetName for an association specific data set. @@ -1822,7 +1822,7 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons IedConnection_GenericServiceHandler handler, void* parameter); /** - * \brief returns the object references of the elements of a data set + * \brief read the data set directory * * The return value 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]. @@ -1838,6 +1838,34 @@ IedConnection_deleteDataSetAsync(IedConnection self, IedClientError* error, cons LIB61850_API LinkedList /* */ IedConnection_getDataSetDirectory(IedConnection self, IedClientError* error, const char* dataSetReference, bool* isDeletable); +/** + * \brief GetDataSetDirectory response or timeout callback + * + * \param dataSetDirectory 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 isDeletable this is an output parameter indicating that the requested data set is deletable by clients. + */ +typedef void +(*IedConnection_GetDataSetDirectoryHandler) (uint32_t invokeId, void* parameter, IedClientError err, LinkedList /* */ dataSetDirectory, bool isDeletable); + +/** + * \brief read the data set directory - asynchronous version + * + * The result data is 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[out] error the error code if an error occurs + * \param dataSetReference object reference of the 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_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, + IedConnection_GetDataSetDirectoryHandler handler, void* parameter); + /** * \brief Write the data set values to the server *