diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 7d251e51..48ec76f0 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -316,6 +316,32 @@ namespace IEC61850 } } + /// + /// Asynchonous service handler for the get RCB values service + /// + /// The invoke ID of the request triggering this callback + /// user provided callback parameter + /// Error code of response or timeout error in case of a response timeout + /// the report control block instance + public delegate void GetRCBValuesHandler(UInt32 invokeId, object parameter, IedClientError err, ReportControlBlock rcb); + + /// + /// Asynchonous service handler for the set RCB values service + /// + /// The invoke ID of the request triggering this callback + /// user provided callback parameter + /// Error code of response or timeout error in case of a response timeout + /// the report control block instance + public delegate void SetRCBValuesHandler(UInt32 invokeId, object parameter, IedClientError err, ReportControlBlock rcb); + + /// + /// Generic asynchonous service handler - used by simple services that have only success or error result + /// + /// The invoke ID of the request triggering this callback + /// user provided callback parameter + /// Error code of response or timeout error in case of a response timeout + public delegate void GenericServiceHandler(UInt32 invokeId, object parameter, IedClientError err); + /// /// This class acts as the entry point for the IEC 61850 client API. It represents a single /// (MMS) connection to a server. @@ -560,6 +586,27 @@ namespace IEC61850 static extern UInt32 IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet, IedConnection_ReadDataSetHandler handler, IntPtr parameter); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IedConnection_GetRCBValuesHandler (UInt32 invokeId, IntPtr parameter, int err, IntPtr rcb); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 + IedConnection_getRCBValuesAsync(IntPtr self, out int error, string rcbReference, IntPtr updateRcb, + IedConnection_GetRCBValuesHandler handler, IntPtr parameter); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void IedConnection_GenericServiceHandler (UInt32 invokeId, IntPtr parameter, int err); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 + IedConnection_setRCBValuesAsync(IntPtr self, out int error, IntPtr rcb, + UInt32 parametersMask, [MarshalAs(UnmanagedType.I1)] bool singleRequest, IedConnection_GenericServiceHandler handler, IntPtr parameter); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 + IedConnection_deleteFileAsync(IntPtr self, out int error, string fileName, + IedConnection_GenericServiceHandler handler, IntPtr parameter); /******************** @@ -1309,7 +1356,45 @@ namespace IEC61850 if (error != 0) throw new IedConnectionException ("Deleting file " + fileName + " failed", error); - } + } + + private void nativeGenericServiceHandler (UInt32 invokeId, IntPtr parameter, int err) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); + + Tuple callbackInfo = handle.Target as Tuple; + + GenericServiceHandler handler = callbackInfo.Item1; + object handlerParameter = callbackInfo.Item2; + + handle.Free(); + + IedClientError clientError = (IedClientError)err; + + handler(invokeId, handlerParameter, clientError); + } + + /// Delete file- asynchronous version + /// The name of the file. + /// This exception is thrown if there is a connection or service error + public UInt32 DeleteFileAsync(string filename, GenericServiceHandler handler, object parameter) + { + int error; + + Tuple callbackInfo = Tuple.Create(handler, parameter); + + GCHandle handle = GCHandle.Alloc(callbackInfo); + + UInt32 invokeId = IedConnection_deleteFileAsync(connection, out error, filename, nativeGenericServiceHandler, GCHandle.ToIntPtr(handle)); + + if (error != 0) + { + handle.Free(); + throw new IedConnectionException("Get file failed", error); + } + + return invokeId; + } /// Read the content of a file directory. /// The name of the directory. @@ -1349,8 +1434,8 @@ namespace IEC61850 /// The name of the directory. /// the filename that defines the continuation point, or null for the first request /// true, when more files are available, false otherwise + /// list of file directory entries /// This exception is thrown if there is a connection or service error - public List GetFileDirectoryEx(string directoryName, string continueAfter, out bool moreFollows) { int error; @@ -1382,7 +1467,8 @@ namespace IEC61850 return fileDirectory; } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] private delegate bool InternalIedClientGetFileHandler(IntPtr parameter, IntPtr buffer, UInt32 bytesRead); private bool iedClientGetFileHandler(IntPtr parameter, IntPtr buffer, UInt32 bytesRead) @@ -1429,6 +1515,7 @@ namespace IEC61850 /// /// User provided parameter that is passed to the callback handler /// + /// This exception is thrown if there is a connection or service error public void GetFile(string fileName, GetFileHandler handler, object parameter) { int error; @@ -1446,6 +1533,90 @@ namespace IEC61850 handle.Free(); } + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + private delegate bool IedConnection_GetFileAsyncHandler (UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId, + IntPtr buffer, UInt32 bytesRead, bool moreFollows); + + /// + /// Callback handler for the asynchronous get file service. Will be invoked for each chunk of received data + /// + /// 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 invokeId of the first (file open) request + /// the file data received with the last response, or null if no file data available + /// indicates that more file data follows + public delegate bool GetFileAsyncHandler(UInt32 invokeId, object parameter, IedClientError err, UInt32 originalInvokeId, byte[] buffer, bool moreFollows); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern UInt32 + IedConnection_getFileAsync(IntPtr self, out int error, string fileName, IedConnection_GetFileAsyncHandler handler, + IntPtr parameter); + + private bool nativeGetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId, + IntPtr buffer, UInt32 bytesRead, bool moreFollows) + { + + GCHandle handle = GCHandle.FromIntPtr(parameter); + + Tuple callbackInfo = handle.Target as Tuple; + + GetFileAsyncHandler handler = callbackInfo.Item1; + object handlerParameter = callbackInfo.Item2; + + handle.Free(); + + IedClientError clientError = (IedClientError)err; + + byte[] bytes = null; + + if (clientError == IedClientError.IED_ERROR_OK) + { + bytes = new byte[bytesRead]; + + Marshal.Copy(buffer, bytes, 0, (int) bytesRead); + } + + return handler(invokeId, handlerParameter, clientError, originalInvokeId, bytes, moreFollows); + } + + /// + /// Download a file from the server. + /// + /// + /// File name of the file (full path) + /// + /// + /// Callback handler that is invoked for each chunk of the file received + /// + /// + /// User provided parameter that is passed to the callback handler + /// + /// invoke ID of the request + /// This exception is thrown if there is a connection or service error + public UInt32 GetFileAsync(string fileName, GetFileAsyncHandler handler, object parameter) + { + int error; + + Tuple callbackInfo = Tuple.Create(handler, parameter); + + GCHandle handle = GCHandle.Alloc(callbackInfo); + + UInt32 invokeId = IedConnection_getFileAsync(connection, out error, fileName, nativeGetFileAsyncHandler, GCHandle.ToIntPtr(handle)); + + if (error != 0) + { + handle.Free(); + throw new IedConnectionException("Get file failed", error); + } + + return invokeId; + } + + + + /// /// Abort (close) the connection. /// @@ -2234,6 +2405,79 @@ namespace IEC61850 error = 1; /* not connected */ } } + + + private void nativeGetRCBValuesHandler (UInt32 invokeId, IntPtr parameter, int err, IntPtr rcbPtr) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); + + Tuple callbackInfo = handle.Target as Tuple; + + GetRCBValuesHandler handler = callbackInfo.Item1; + object handlerParameter = callbackInfo.Item2; + ReportControlBlock rcb = callbackInfo.Item3; + + handle.Free(); + + IedClientError clientError = (IedClientError)err; + + handler(invokeId, handlerParameter, clientError, rcb); + } + + internal UInt32 GetRCBValuesAsync(string objectReference, ReportControlBlock updateRcb, GetRCBValuesHandler handler, object parameter) + { + int error; + + Tuple callbackInfo = Tuple.Create(handler, parameter, updateRcb); + + GCHandle handle = GCHandle.Alloc(callbackInfo); + + UInt32 invokeId = IedConnection_getRCBValuesAsync(connection, out error, objectReference, updateRcb.self, nativeGetRCBValuesHandler, GCHandle.ToIntPtr(handle)); + + if (error != 0) + { + handle.Free(); + throw new IedConnectionException("GetRCBValues failed", error); + } + + return invokeId; + } + + private void nativeSetRcbValuesHandler (UInt32 invokeId, IntPtr parameter, int err) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); + + Tuple callbackInfo = handle.Target as Tuple; + + SetRCBValuesHandler handler = callbackInfo.Item1; + object handlerParameter = callbackInfo.Item2; + ReportControlBlock rcb = callbackInfo.Item3; + + handle.Free(); + + IedClientError clientError = (IedClientError)err; + + handler(invokeId, handlerParameter, clientError, rcb); + } + + internal UInt32 SetRCBValuesAsync(ReportControlBlock rcb, UInt32 parametersMask, bool singleRequest, SetRCBValuesHandler handler, object parameter) + { + int error; + + Tuple callbackInfo = Tuple.Create(handler, parameter, rcb); + + GCHandle handle = GCHandle.Alloc(callbackInfo); + + UInt32 invokeId = IedConnection_setRCBValuesAsync(connection, out error, rcb.self, parametersMask, singleRequest, nativeSetRcbValuesHandler, GCHandle.ToIntPtr(handle)); + + if (error != 0) + { + handle.Free(); + throw new IedConnectionException("SetRCBValues failed", error); + } + + return invokeId; + } } diff --git a/dotnet/IEC61850forCSharp/ReportControlBlock.cs b/dotnet/IEC61850forCSharp/ReportControlBlock.cs index 1854480f..e5b0ad2a 100644 --- a/dotnet/IEC61850forCSharp/ReportControlBlock.cs +++ b/dotnet/IEC61850forCSharp/ReportControlBlock.cs @@ -149,8 +149,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientReportControlBlock_getOwner (IntPtr self); - private IntPtr self; - private IedConnection iedConnection = null; + internal IntPtr self; + + private IedConnection iedConnection = null; private string objectReference; private bool flagRptId = false; private bool flagRptEna = false; @@ -302,6 +303,18 @@ namespace IEC61850 throw new IedConnectionException ("getRCBValues service failed", error); } + /// + /// Read all RCB values from the server - asynchronous version + /// + /// the invoke ID of the request + /// user provided callback function + /// user provided callback parameter + /// This exception is thrown if there is a connection or service error + public UInt32 GetRCBValuesAsync(GetRCBValuesHandler handler, object parameter) + { + return iedConnection.GetRCBValuesAsync(GetObjectReference(), this, handler, parameter); + } + /// /// Write changed RCB values to the server. /// @@ -315,17 +328,7 @@ namespace IEC61850 SetRCBValues (true); } - /// - /// Write changed RCB values to the server. - /// - /// - /// This function will only write the RCB values that were set by one of the setter methods. - /// - /// This exception is thrown if there is a connection or service error - /// - /// If true the values are sent by single MMS write request. Otherwise the values are all sent by their own MMS write requests. - /// - public void SetRCBValues (bool singleRequest) + private UInt32 CreateParametersMask() { UInt32 parametersMask = 0; @@ -371,6 +374,35 @@ namespace IEC61850 if (flagResvTms) parametersMask += 16384; + return parametersMask; + } + + public UInt32 SetRCBValuesAsync(SetRCBValuesHandler handler, object parameter) + { + return SetRCBValuesAsync(true, handler, parameter); + } + + public UInt32 SetRCBValuesAsync(bool singleRequest, SetRCBValuesHandler handler, object parameter) + { + UInt32 parametersMask = CreateParametersMask(); + + return iedConnection.SetRCBValuesAsync(this, parametersMask, singleRequest, handler, parameter); + } + + /// + /// Write changed RCB values to the server. + /// + /// + /// This function will only write the RCB values that were set by one of the setter methods. + /// + /// This exception is thrown if there is a connection or service error + /// + /// If true the values are sent by single MMS write request. Otherwise the values are all sent by their own MMS write requests. + /// + public void SetRCBValues (bool singleRequest) + { + UInt32 parametersMask = CreateParametersMask(); + int error; iedConnection.SetRCBValues (out error, self, parametersMask, singleRequest); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 6873d636..cc46604f 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -322,8 +322,8 @@ IedServer_destroy(IedServer self); /** * \brief Set the local IP address to listen on * - * \param self the IedServer instance - * \param localIpAddress the local IP address as C string (an internal copy will be created) + * \param self the IedServer instance + * \param localIpAddress the local IP address as C string (an internal copy will be created) */ LIB61850_API void IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress);