diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 3eb1d371..89612b7f 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -3134,6 +3134,122 @@ exit_function: return isDeleted; } +static void +deleteNamedVariableListHandler(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_deleteDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, + IedConnection_GenericServiceHandler handler, void* parameter) +{ + *error = IED_ERROR_OK; + + char domainIdBuf[65]; + char *domainId = domainIdBuf; + char itemId[DATA_SET_MAX_NAME_LENGTH + 1]; + bool isAssociationSpecific = false; + bool isDeleted = false; + + int dataSetReferenceLength = strlen(dataSetReference); + + if (dataSetReference[0] != '@') { + if ((dataSetReference[0] == '/') + || (strchr(dataSetReference, '/') == NULL)) { + domainId = NULL; + + if (dataSetReference[0] == '/') + strcpy(itemId, dataSetReference + 1); + else + strcpy(itemId, dataSetReference); + } + else { + + if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, + domainId) == NULL) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 0; + } + + const char *itemIdString = dataSetReference + strlen(domainId) + 1; + + if (strlen(itemIdString) > DATA_SET_MAX_NAME_LENGTH) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 0; + } + + StringUtils_copyStringToBuffer(itemIdString, itemId); + + StringUtils_replace(itemId, '.', '$'); + } + } + else { + if (dataSetReferenceLength > 33) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 0; + } + + strcpy(itemId, dataSetReference + 1); + + isAssociationSpecific = true; + } + + MmsError mmsError; + + if ((domainId == NULL) || (itemId == NULL)) { + *error = IED_ERROR_OBJECT_REFERENCE_INVALID; + return 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; + + if (isAssociationSpecific) { + MmsConnection_deleteAssociationSpecificNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, itemId, deleteNamedVariableListHandler, self); + } + else { + MmsConnection_deleteNamedVariableListAsync(self->connection, &(call->invokeId), &mmsError, domainId, itemId, deleteNamedVariableListHandler, self); + } + + if (*error != IED_ERROR_OK) { + iedConnection_releaseOutstandingCall(self, call); + return 0; + } + + return call->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 9d1946e9..b596b88f 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -1,7 +1,7 @@ /* * iec61850_client.h * - * Copyright 2013-2019 Michael Zillgith + * Copyright 2013-2021 Michael Zillgith * * This file is part of libIEC61850. * @@ -1775,6 +1775,26 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha 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 + * + * 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. + * + * The data set was deleted successfully when the callback parameter "error" is IED_ERROR_OK. Otherwise the "error" + * parameter contains a particular error code. + * + * \param connection the connection object + * \param 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_deleteDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, + IedConnection_GenericServiceHandler handler, void* parameter); /** * \brief returns the object references of the elements of a data set diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 4dd10ded..57243230 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -291,7 +291,7 @@ mmsClient_createDeleteNamedVariableListRequest(long invokeId, ByteBuffer* writeB const char* domainId, const char* listNameId); LIB61850_INTERNAL bool -mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId); +mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId, long* numberDeleted, long* numberMatched); LIB61850_INTERNAL void mmsClient_createDeleteAssociationSpecificNamedVariableListRequest( diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index e4bd2ae5..d7eacfd0 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -804,9 +804,19 @@ handleAsyncResponse(MmsConnection self, ByteBuffer* response, uint32_t bufPos, M else { bool success = false; - if (mmsClient_parseDeleteNamedVariableListResponse(response, NULL)) + long numberMatched = 0; + long numberDeleted = 0; + + if (mmsClient_parseDeleteNamedVariableListResponse(response, NULL, &numberDeleted, &numberMatched)) success = true; + if (numberMatched == 0) + err = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; + else { + if (numberDeleted == 0) + err = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } + handler(outstandingCall->invokeId, outstandingCall->userParameter, err, success); } } 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 3ca329e1..74a0da1b 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 @@ -117,7 +117,7 @@ mmsClient_createDeleteAssociationSpecificNamedVariableListRequest( } bool -mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId) +mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* invokeId, long* numberDeleted, long* numberMatched) { MmsPdu_t* mmsPdu = 0; @@ -138,11 +138,10 @@ mmsClient_parseDeleteNamedVariableListResponse(ByteBuffer* message, uint32_t* in DeleteNamedVariableListResponse_t* response = &(mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.choice.deleteNamedVariableList); - long numberDeleted; + asn_INTEGER2long(&(response->numberDeleted), numberDeleted); + asn_INTEGER2long(&(response->numberMatched), numberMatched); - asn_INTEGER2long(&(response->numberDeleted), &numberDeleted); - - if (numberDeleted == 1) + if (*numberDeleted == 1) retVal = true; } }