From 74058846b34dd5a55a3050f5f1a1e44d2ad6f0e8 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 5 Dec 2014 10:43:37 +0100 Subject: [PATCH] - added new dotnet example - fixed problem with garbage collected report handler delegate in .NET API --- .../IEC61850forCSharp/ReportControlBlock.cs | 15 ++- dotnet/datasets/DataSetExample.cs | 5 +- dotnet/dotnet.sln | 6 + dotnet/report_new_dataset/AssemblyInfo.cs | 27 +++++ dotnet/report_new_dataset/Main.cs | 114 ++++++++++++++++++ .../report_new_dataset.csproj | 45 +++++++ src/iec61850/client/ied_connection.c | 18 ++- 7 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 dotnet/report_new_dataset/AssemblyInfo.cs create mode 100644 dotnet/report_new_dataset/Main.cs create mode 100644 dotnet/report_new_dataset/report_new_dataset.csproj diff --git a/dotnet/IEC61850forCSharp/ReportControlBlock.cs b/dotnet/IEC61850forCSharp/ReportControlBlock.cs index de66949d..60f7a5fc 100644 --- a/dotnet/IEC61850forCSharp/ReportControlBlock.cs +++ b/dotnet/IEC61850forCSharp/ReportControlBlock.cs @@ -170,6 +170,7 @@ namespace IEC61850 private event ReportHandler reportHandler = null; private object reportHandlerParameter; private bool reportHandlerInstalled = false; + private event InternalReportHandler internalHandler = null; private void resetSendFlags () { @@ -237,12 +238,14 @@ namespace IEC61850 if (reportHandlerInstalled == false) { - string reportId = this.GetRptId (); - -// if ((GetRptId() == null) || (GetRptId().Length == 0)) -// reportId = - - IedConnection_installReportHandler (this.connection, objectReference, reportId, new InternalReportHandler(internalReportHandler), IntPtr.Zero); + string reportId = this.GetRptId (); + + if (internalHandler == null) + { + internalHandler = new InternalReportHandler(internalReportHandler); + } + + IedConnection_installReportHandler(this.connection, objectReference, reportId, internalHandler, IntPtr.Zero); reportHandlerInstalled = true; } } diff --git a/dotnet/datasets/DataSetExample.cs b/dotnet/datasets/DataSetExample.cs index f816c24c..a9af7267 100644 --- a/dotnet/datasets/DataSetExample.cs +++ b/dotnet/datasets/DataSetExample.cs @@ -16,9 +16,8 @@ namespace datasets if (args.Length > 0) hostname = args[0]; - else - hostname = "10.0.2.2"; - //hostname = "localhost"; + else + hostname = "localhost"; Console.WriteLine("Connect to " + hostname); diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln index e781ee8a..8ea2e272 100644 --- a/dotnet/dotnet.sln +++ b/dotnet/dotnet.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "files", "files\files.csproj EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example3", "example3\example3.csproj", "{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "report_new_dataset", "report_new_dataset\report_new_dataset.csproj", "{71485F99-2976-45E6-B73D-4946E594C15C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.ActiveCfg = Release|Any CPU {5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}.Release|Any CPU.Build.0 = Release|Any CPU + {71485F99-2976-45E6-B73D-4946E594C15C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {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 {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 diff --git a/dotnet/report_new_dataset/AssemblyInfo.cs b/dotnet/report_new_dataset/AssemblyInfo.cs new file mode 100644 index 00000000..1d974f12 --- /dev/null +++ b/dotnet/report_new_dataset/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("report_new_dataset")] +[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/report_new_dataset/Main.cs b/dotnet/report_new_dataset/Main.cs new file mode 100644 index 00000000..3af2241c --- /dev/null +++ b/dotnet/report_new_dataset/Main.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +using IEC61850.Common; +using IEC61850.Client; +using System.Threading; + +namespace report_new_dataset +{ + class ReportNewDataSetExample + { + private static void reportHandler (Report report, object parameter) + { + Console.WriteLine ("Received report:\n----------------"); + + if (report.HasTimestamp ()) + Console.WriteLine (" timestamp: " + MmsValue.MsTimeToDateTimeOffset (report.GetTimestamp ()).ToString ()); + + MmsValue values = report.GetDataSetValues (); + + Console.WriteLine (" report dataset contains " + values.Size () + " elements"); + + for (int i = 0; i < values.Size(); i++) { + if (report.GetReasonForInclusion(i) != ReasonForInclusion.REASON_NOT_INCLUDED) { + Console.WriteLine(" element " + i + " included for reason " + report.GetReasonForInclusion(i).ToString() + " " + values.GetElement(i)); + } + } + } + + private static bool running = true; + + public static void Main (string[] args) + { + IedConnection con = new IedConnection (); + + string hostname; + + if (args.Length > 0) + hostname = args[0]; + else + hostname = "10.0.2.2"; + //hostname = "localhost"; + + Console.WriteLine("Connect to " + hostname); + + try + { + con.Connect(hostname, 102); + + List serverDirectory = con.GetServerDirectory(false); + + foreach (string entry in serverDirectory) + { + Console.WriteLine("LD: " + entry); + } + + // create a new data set + + List dataSetElements = new List(); + + dataSetElements.Add("simpleIOGenericIO/GGIO1.AnIn1.mag.f[MX]"); + dataSetElements.Add("simpleIOGenericIO/GGIO1.AnIn2.mag.f[MX]"); + dataSetElements.Add("simpleIOGenericIO/GGIO1.AnIn3.mag.f[MX]"); + dataSetElements.Add("simpleIOGenericIO/GGIO1.AnIn4.mag.f[MX]"); + + string dataSetReference = "simpleIOGenericIO/LLN0.ds1"; + + // Note: this function will throw an exception when a data set with the same name already exists + con.CreateDataSet(dataSetReference, dataSetElements); + + // reconfigure existing RCB with new data set + + string rcbReference = "simpleIOGenericIO/LLN0.RP.EventsRCB01"; + + ReportControlBlock rcb = con.GetReportControlBlock(rcbReference); + + rcb.GetRCBValues(); + + // note: the second parameter is not required! + rcb.InstallReportHandler(reportHandler, rcb); + + string rcbDataSetReference = dataSetReference.Replace('.', '$'); + + rcb.SetDataSetReference(rcbDataSetReference); + rcb.SetTrgOps(TriggerOptions.DATA_CHANGED | TriggerOptions.INTEGRITY); + rcb.SetIntgPd(5000); + rcb.SetRptEna(true); + + rcb.SetRCBValues(); + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { + e.Cancel = true; + running = false; + }; + + while (running) { + Thread.Sleep(1000); + Console.WriteLine("Total memory: " + GC.GetTotalMemory(false)); + } + + // delete the data set + con.DeleteDataSet("simpleIOGenericIO/LLN0.ds1"); + + con.Abort(); + } + catch (IedConnectionException e) + { + Console.WriteLine(e.Message); + } + + } + } +} diff --git a/dotnet/report_new_dataset/report_new_dataset.csproj b/dotnet/report_new_dataset/report_new_dataset.csproj new file mode 100644 index 00000000..e33af7a3 --- /dev/null +++ b/dotnet/report_new_dataset/report_new_dataset.csproj @@ -0,0 +1,45 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {71485F99-2976-45E6-B73D-4946E594C15C} + Exe + report_new_dataset + report_new_dataset + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + none + true + bin\Release + prompt + 4 + true + + + + + + + + + + + + {C35D624E-5506-4560-8074-1728F1FA1A4D} + IEC61850forCSharp + + + \ No newline at end of file diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index b97434fb..c8f39820 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1838,14 +1838,14 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha if (domainId == NULL) { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; - return; + goto exit_function; } int domainIdLength = strlen(domainId); if ((strlen(dataSetReference) - domainIdLength - 1) > 32) { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; - return; + goto exit_function; } char* itemIdRef = copyStringToBuffer(dataSetReference + domainIdLength + 1, itemIdBuffer); @@ -1884,6 +1884,9 @@ IedConnection_createDataSet(IedConnection self, IedClientError* error, const cha LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); *error = iedConnection_mapMmsErrorToIedError(mmsError); + +exit_function: + return; } void @@ -1898,14 +1901,14 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha if (dataSetReference[0] != '@') { if (MmsMapping_getMmsDomainFromObjectReference(dataSetReference, domainId) == NULL) { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; - return; + goto exit_function; } const char* itemIdString = dataSetReference + strlen(domainId) + 1; if (strlen(itemIdString) > 32) { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; - return; + goto exit_function; } copyStringToBuffer(itemIdString, itemId); @@ -1915,7 +1918,7 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha else { if (dataSetReferenceLength > 33) { *error = IED_ERROR_OBJECT_REFERENCE_INVALID; - return; + goto exit_function; } strcpy(itemId, dataSetReference + 1); @@ -1931,6 +1934,9 @@ IedConnection_deleteDataSet(IedConnection self, IedClientError* error, const cha MmsConnection_deleteNamedVariableList(self->connection, &mmsError, domainId, itemId); *error = iedConnection_mapMmsErrorToIedError(mmsError); + +exit_function: + return; } LinkedList /* */ @@ -2047,7 +2053,7 @@ IedConnection_readDataSetValues(IedConnection self, IedClientError* error, const MmsValue_update(dataSetValues, dataSetVal); } - cleanup_and_exit: +cleanup_and_exit: return dataSet; }