From 1d120f87de333cd3436a752f703d0a8e3738962c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 25 Oct 2018 11:33:55 +0200 Subject: [PATCH] - .NET API: Added IedConnection.ReadValueAsync method --- dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 59 ++++++++++ dotnet/client_example_async/Program.cs | 105 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 27 +++++ .../client_example_async.csproj | 44 ++++++++ dotnet/dotnet.sln | 6 + 5 files changed, 241 insertions(+) create mode 100644 dotnet/client_example_async/Program.cs create mode 100644 dotnet/client_example_async/Properties/AssemblyInfo.cs create mode 100644 dotnet/client_example_async/client_example_async.csproj diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 99945820..a02a7374 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -1459,6 +1459,65 @@ namespace IEC61850 return newList; } + + /// + /// Read object 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 read result value or null in case of an error + public delegate void ReadValueHandler(UInt32 invokeId, object parameter, IedClientError err, MmsValue value); + + private void nativeReadObjectHandler (UInt32 invokeId, IntPtr parameter, int err, IntPtr value) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); + + Tuple callbackInfo = handle.Target as Tuple; + + ReadValueHandler handler = callbackInfo.Item1; + object handlerParameter = callbackInfo.Item2; + + handle.Free(); + + IedClientError clientError = (IedClientError)err; + + MmsValue mmsValue = null; + + if (value != IntPtr.Zero) + { + mmsValue = new MmsValue(value, true); + } + + handler(invokeId, handlerParameter, clientError, mmsValue); + } + + /// Asynchronously read the value of a data attribute (DA) or functional constraint data object (FCDO). + /// The object reference of a DA or FCDO. + /// The functional constraint (FC) of the object + /// 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 ReadValueAsync(string objectReference, FunctionalConstraint fc, ReadValueHandler handler, object parameter) + { + int error; + + Tuple callbackInfo = Tuple.Create(handler, parameter); + + GCHandle handle = GCHandle.Alloc(callbackInfo); + + UInt32 invokeId = IedConnection_readObjectAsync(connection, out error, objectReference, (int)fc, nativeReadObjectHandler, GCHandle.ToIntPtr(handle)); + + if (error != 0) + { + handle.Free(); + throw new IedConnectionException("Reading value failed", error); + } + + return invokeId; + } + internal void UninstallReportHandler (string objectReference) { if (connection != IntPtr.Zero) { diff --git a/dotnet/client_example_async/Program.cs b/dotnet/client_example_async/Program.cs new file mode 100644 index 00000000..1bedc751 --- /dev/null +++ b/dotnet/client_example_async/Program.cs @@ -0,0 +1,105 @@ +using System; +using IEC61850.Client; +using IEC61850.Common; +using System.Threading; + +namespace client_example_async +{ + class MainClass + { + + public static void Main (string[] args) + { + IedConnection con = new IedConnection (); + + string hostname; + + if (args.Length > 0) + hostname = args[0]; + else + hostname = "127.0.0.1"; + + int port = 102; + + if (args.Length > 1) + port = Int32.Parse(args [1]); + + Console.WriteLine("Connect to " + hostname); + + try + { + con.Connect(hostname, port); + + /* read FCDO */ + con.ReadValueAsync("simpleIOGenericIO/GGIO1.AnIn1", FunctionalConstraint.MX, delegate(uint invokeId, object parameter, IedClientError err, MmsValue value) { + + if (err == IedClientError.IED_ERROR_OK) + { + if (value.GetType() == MmsType.MMS_STRUCTURE) + { + Console.WriteLine("Value is of complex type"); + + for (int i = 0; i < value.Size(); i++) + { + Console.WriteLine(" element: " + value.GetElement(i).GetType()); + if (value.GetElement(i).GetType() == MmsType.MMS_UTC_TIME) + { + Console.WriteLine(" -> " + value.GetElement(i).GetUtcTimeAsDateTimeOffset()); + } + } + } + } + else { + Console.WriteLine("Read error: " + err.ToString()); + } + + + }, null); + + con.ReadValueAsync("simpleIOGenericIO/GGIO1.AnIn1", FunctionalConstraint.MX, delegate(uint invokeId, object parameter, IedClientError err, MmsValue value) { + + if (err == IedClientError.IED_ERROR_OK) + { + if (value.GetType() == MmsType.MMS_STRUCTURE) + { + Console.WriteLine("Value is of complex type"); + + for (int i = 0; i < value.Size(); i++) + { + Console.WriteLine(" element: " + value.GetElement(i).GetType()); + if (value.GetElement(i).GetType() == MmsType.MMS_UTC_TIME) + { + Console.WriteLine(" -> " + value.GetElement(i).GetUtcTimeAsDateTimeOffset()); + } + } + } + } + else { + Console.WriteLine("Read error: " + err.ToString()); + } + + + }, null); + + + // Thread.Sleep(5000); + + con.Abort(); + } + catch (IedConnectionException e) + { + Console.WriteLine(e.Message); + } + + System.Threading.Thread.Sleep(2000); + + // release all resources - do NOT use the object after this call!! + con.Dispose (); + } + + static void HandleReadObjectHandler (uint invokeId, object parameter, IedClientError err, MmsValue value) + { + + } + } +} diff --git a/dotnet/client_example_async/Properties/AssemblyInfo.cs b/dotnet/client_example_async/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e909bebd --- /dev/null +++ b/dotnet/client_example_async/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("client_example_async")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("mzillgit")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/dotnet/client_example_async/client_example_async.csproj b/dotnet/client_example_async/client_example_async.csproj new file mode 100644 index 00000000..17879d2c --- /dev/null +++ b/dotnet/client_example_async/client_example_async.csproj @@ -0,0 +1,44 @@ + + + + Debug + AnyCPU + {71902641-776A-47D8-9C0E-9ACBBEAC1370} + Exe + client_example_async + client_example_async + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + full + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850.NET + + + \ No newline at end of file diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln index 97feef4c..c2ab42d8 100644 --- a/dotnet/dotnet.sln +++ b/dotnet/dotnet.sln @@ -46,6 +46,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_server_example", "tls_s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_setting_groups", "client_example_setting_groups\client_example_setting_groups.csproj", "{0DA95476-B149-450B-AC36-01CEECFC1A43}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_async", "client_example_async\client_example_async.csproj", "{71902641-776A-47D8-9C0E-9ACBBEAC1370}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -92,6 +94,10 @@ Global {71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.Build.0 = Debug|Any CPU {71485F99-2976-45E6-B73D-4946E594C15C}.Release|Any CPU.ActiveCfg = Release|Any CPU {71485F99-2976-45E6-B73D-4946E594C15C}.Release|Any CPU.Build.0 = Release|Any CPU + {71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71902641-776A-47D8-9C0E-9ACBBEAC1370}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71902641-776A-47D8-9C0E-9ACBBEAC1370}.Release|Any CPU.Build.0 = Release|Any CPU {77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Debug|Any CPU.Build.0 = Debug|Any CPU {77127456-19B9-4D1A-AEF9-40F8D1C5695E}.Release|Any CPU.ActiveCfg = Release|Any CPU