diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index 26221b4d..a417ab0a 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
- * Copyright 2014-2019 Michael Zillgith
+ * Copyright 2014-2021 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -625,13 +625,31 @@ namespace IEC61850
int fc, IedConnection_GetVariableSpecificationHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
- private delegate void IedConnection_ReadDataSetHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr dataSet);
+ private delegate void IedConnection_ReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet,
IedConnection_ReadDataSetHandler handler, IntPtr parameter);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern UInt32
+ IedConnection_createDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet,
+ IedConnection_GenericServiceHandler handler, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern UInt32
+ IedConnection_deleteDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
+ IedConnection_GenericServiceHandler handler, IntPtr parameter);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void IedConnection_GetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, [MarshalAs(UnmanagedType.I1)] bool isDeletable);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern UInt32
+ IedConnection_getDataSetDirectoryAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
+ IedConnection_GetDataSetDirectoryHandler handler, IntPtr parameter);
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetRCBValuesHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr rcb);
@@ -1742,9 +1760,6 @@ namespace IEC61850
return invokeId;
}
-
-
-
///
/// Abort (close) the connection.
///
@@ -2004,6 +2019,47 @@ namespace IEC61850
}
+ ///
+ /// Create a new data set - asynchronous version.
+ ///
+ /// This function creates a new data set at the server. The data set consists of the members defined
+ /// by the list of object references provided.
+ /// The object reference of the data set
+ /// A list of object references of the data set elements
+ /// Callback function to handle the received response or service timeout
+ /// User provided callback parameter. Will be passed to the callback function
+ /// the invoke ID of the sent request
+ /// This exception is thrown if there is a connection or service error
+ public UInt32 CreateDataSetAsync(string dataSetReference, List dataSetElements, GenericServiceHandler handler, object parameter)
+ {
+ int error = 0;
+
+ IntPtr linkedList = LinkedList_create();
+
+ foreach (string dataSetElement in dataSetElements)
+ {
+ IntPtr dataSetElementHandle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(dataSetElement);
+
+ LinkedList_add(linkedList, dataSetElementHandle);
+ }
+
+ Tuple callbackInfo = Tuple.Create(handler, parameter);
+
+ GCHandle handle = GCHandle.Alloc(callbackInfo);
+
+ UInt32 invokeId = IedConnection_createDataSetAsync(connection, out error, dataSetReference, linkedList, nativeGenericServiceHandler, GCHandle.ToIntPtr(handle));
+
+ LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
+
+ if (error != 0)
+ {
+ handle.Free();
+ throw new IedConnectionException("Create data set failed", error);
+ }
+
+ return invokeId;
+ }
+
///
/// Delete a data set.
///
@@ -2024,6 +2080,35 @@ namespace IEC61850
return isDeleted;
}
+ ///
+ /// Delete a data set - asynchronous version.
+ ///
+ /// This function will delete a data set at the server. This function may fail if the data set is not
+ /// deletable.
+ /// The object reference of the data set
+ /// Callback function to handle the received response or service timeout
+ /// User provided callback parameter. Will be passed to the callback function
+ /// the invoke ID of the sent request
+ /// This exception is thrown if there is a connection or service error
+ public UInt32 DeleteDataSetAsync(string dataSetReference, GenericServiceHandler handler, object parameter)
+ {
+ int error = 0;
+
+ Tuple callbackInfo = Tuple.Create(handler, parameter);
+
+ GCHandle handle = GCHandle.Alloc(callbackInfo);
+
+ UInt32 invokeId = IedConnection_deleteDataSetAsync(connection, out error, dataSetReference, nativeGenericServiceHandler, GCHandle.ToIntPtr(handle));
+
+ if (error != 0)
+ {
+ handle.Free();
+ throw new IedConnectionException("Delete data set failed", error);
+ }
+
+ return invokeId;
+ }
+
///
/// Get the directory of the data set.
///
@@ -2073,6 +2158,72 @@ namespace IEC61850
return newList;
}
+ ///
+ /// Get data set directory handler.
+ ///
+ /// The invoke ID of the reqeust triggering this callback
+ /// user provided callback parameter
+ /// Error code of response or timeout error in case of a response timeout
+ /// the list of data set entry references
+ /// data set can be deleted by a client (dynamic data set)
+ public delegate void GetDataSetDirectoryHandler(UInt32 invokeId, object parameter, IedClientError err, List dataSetDirectory, bool isDeletable);
+
+ private void nativeGetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, bool isDeletable)
+ {
+ GCHandle handle = GCHandle.FromIntPtr(parameter);
+
+ Tuple callbackInfo = handle.Target as Tuple;
+
+ GetDataSetDirectoryHandler handler = callbackInfo.Item1;
+ object handlerParameter = callbackInfo.Item2;
+
+ IntPtr element = LinkedList_getNext(dataSetDirectory);
+
+ handle.Free();
+
+ List newList = new List();
+
+ while (element != IntPtr.Zero)
+ {
+ string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
+
+ newList.Add(dataObject);
+
+ element = LinkedList_getNext(element);
+ }
+
+ LinkedList_destroy(dataSetDirectory);
+
+ handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable);
+ }
+
+ ///
+ /// Read the data set directory - asynchronous version
+ ///
+ /// The data set directory async.
+ /// Data set reference.
+ /// Callback function to handle the received response or service timeout
+ /// User provided callback parameter. Will be passed to the callback function
+ /// the invoke ID of the sent request
+ /// This exception is thrown if there is a connection or service error
+ public UInt32 GetDataSetDirectoryAsync(string dataSetReference, GetDataSetDirectoryHandler handler, object parameter)
+ {
+ int error = 0;
+
+ Tuple callbackInfo = Tuple.Create(handler, parameter);
+
+ GCHandle handle = GCHandle.Alloc(callbackInfo);
+
+ UInt32 invokeId = IedConnection_getDataSetDirectoryAsync(connection, out error, dataSetReference, nativeGetDataSetDirectoryHandler, GCHandle.ToIntPtr(handle));
+
+ if (error != 0)
+ {
+ handle.Free();
+ throw new IedConnectionException("Get data set directory failed", error);
+ }
+
+ return invokeId;
+ }
///
/// Read object handler.
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 7d31dad5..0bb73ebb 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -3009,7 +3009,6 @@ void
IedConnection_createDataSet(IedConnection self, IedClientError* error, const char* dataSetReference,
LinkedList /* */dataSetElements)
{
-
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
@@ -3153,6 +3152,250 @@ 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;
+
+ 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;
+}
+
+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)
+{
+ uint32_t 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)
{
@@ -3219,8 +3462,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);
@@ -3241,6 +3484,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 01a9cb95..dd1b1848 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.
*
@@ -1771,6 +1771,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
*
@@ -1787,9 +1812,29 @@ 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 - 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.
+ *
+ * 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
+ * \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].
@@ -1805,6 +1850,34 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha
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
*
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;
}
}