diff --git a/dotnet/IEC61850forCSharp/GooseControlBlock.cs b/dotnet/IEC61850forCSharp/GooseControlBlock.cs
index f2b9c88f..03df5709 100644
--- a/dotnet/IEC61850forCSharp/GooseControlBlock.cs
+++ b/dotnet/IEC61850forCSharp/GooseControlBlock.cs
@@ -147,6 +147,9 @@ namespace IEC61850
if (flagDatSet)
parametersMask += 4;
+ if (flagDstAddress)
+ parametersMask += 32;
+
int error;
IedConnection_setGoCBValues (connection, out error, self, parametersMask, singleRequest);
diff --git a/dotnet/IEC61850forCSharp/GooseSubscriber.cs b/dotnet/IEC61850forCSharp/GooseSubscriber.cs
new file mode 100644
index 00000000..9b2c6e69
--- /dev/null
+++ b/dotnet/IEC61850forCSharp/GooseSubscriber.cs
@@ -0,0 +1,313 @@
+/*
+ * GooseSubscriber.cs
+ *
+ * Copyright 2017 Michael Zillgith
+ *
+ * This file is part of libIEC61850.
+ *
+ * libIEC61850 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libIEC61850 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libIEC61850. If not, see .
+ *
+ * See COPYING file for the complete license text.
+ */
+
+using System;
+using System.Runtime.InteropServices;
+using IEC61850.Common;
+
+namespace IEC61850
+{
+ namespace GOOSE
+ {
+
+ namespace Subscriber
+ {
+
+ ///
+ /// GOOSE listener.
+ ///
+ public delegate void GooseListener (GooseSubscriber report, object parameter);
+
+ public class GooseReceiver : IDisposable
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr GooseReceiver_create ();
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_addSubscriber(IntPtr self, IntPtr subscriber);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_removeSubscriber(IntPtr self, IntPtr subscriber);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_start(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_stop(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private static extern bool GooseReceiver_isRunning (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_destroy(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseReceiver_setInterfaceId(IntPtr self, string interfaceId);
+
+ private IntPtr self;
+
+ private bool isDisposed = false;
+
+ public GooseReceiver()
+ {
+ self = GooseReceiver_create ();
+ }
+
+ public void SetInterfaceId(string interfaceId)
+ {
+ GooseReceiver_setInterfaceId (self, interfaceId);
+ }
+
+ public void AddSubscriber(GooseSubscriber subscriber)
+ {
+ GooseReceiver_addSubscriber (self, subscriber.self);
+ }
+
+ public void RemoveSubscriber(GooseSubscriber subscriber)
+ {
+ GooseReceiver_removeSubscriber (self, subscriber.self);
+ }
+
+ public void Start()
+ {
+ GooseReceiver_start (self);
+ }
+
+ public void Stop()
+ {
+ GooseReceiver_stop (self);
+ }
+
+ public bool IsRunning()
+ {
+ return GooseReceiver_isRunning (self);
+ }
+
+ public void Dispose()
+ {
+ if (isDisposed == false) {
+ isDisposed = true;
+ GooseReceiver_destroy (self);
+ self = IntPtr.Zero;
+ }
+ }
+
+ ~GooseReceiver()
+ {
+ Dispose ();
+ }
+ }
+
+
+ ///
+ /// Representing a GOOSE subscriber
+ ///
+ ///
+ /// NOTE: After SetListener is called, do not call any function outside of
+ /// the callback handler!
+ ///
+ public class GooseSubscriber : IDisposable
+ {
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void InternalGooseListener (IntPtr subscriber, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr GooseSubscriber_create (string goCbRef, IntPtr dataSetValue);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseSubscriber_setAppId(IntPtr self, UInt16 appId);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private static extern bool GooseSubscriber_isValid (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern UInt32 GooseSubscriber_getStNum (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern UInt32 GooseSubscriber_getSqNum (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private static extern bool GooseSubscriber_isTest (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern UInt32 GooseSubscriber_getConfRev (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ private static extern bool GooseSubscriber_needsCommission (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern UInt32 GooseSubscriber_getTimeAllowedToLive (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern UInt64 GooseSubscriber_getTimestamp (IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern IntPtr GooseSubscriber_getDataSetValues(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseSubscriber_destroy(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ private static extern void GooseSubscriber_setListener (IntPtr self, InternalGooseListener listener, IntPtr parameter);
+
+ internal IntPtr self;
+
+ private bool isDisposed = false;
+
+ private GooseListener listener = null;
+ private object listenerParameter = null;
+
+ private event InternalGooseListener internalListener = null;
+
+ private void internalGooseListener (IntPtr subscriber, IntPtr parameter)
+ {
+ try {
+
+ if (listener != null) {
+ listener(this, listenerParameter);
+ }
+
+ } catch (Exception e)
+ {
+ // older versions of mono 2.10 (for linux?) cause this exception
+ Console.WriteLine(e.Message);
+ }
+ }
+
+ public GooseSubscriber(string goCbRef)
+ {
+ self = GooseSubscriber_create (goCbRef, IntPtr.Zero);
+ }
+
+ public void SetAppId(UInt16 appId)
+ {
+ GooseSubscriber_setAppId (self, appId);
+ }
+
+ public bool IsValid ()
+ {
+ return GooseSubscriber_isValid (self);
+ }
+
+
+ public void SetListener(GooseListener listener, object parameter)
+ {
+ this.listener = listener;
+ this.listenerParameter = parameter;
+
+ if (internalListener == null) {
+ internalListener = new InternalGooseListener (internalGooseListener);
+
+ GooseSubscriber_setListener (self, internalListener, IntPtr.Zero);
+ }
+ }
+
+ public UInt32 GetStNum()
+ {
+ return GooseSubscriber_getStNum (self);
+ }
+
+ public UInt32 GetSqNum()
+ {
+ return GooseSubscriber_getSqNum (self);
+ }
+
+ public bool IsTest()
+ {
+ return GooseSubscriber_isTest (self);
+ }
+
+ public UInt32 GetConfRev()
+ {
+ return GooseSubscriber_getConfRev (self);
+ }
+
+ public bool NeedsCommission()
+ {
+ return GooseSubscriber_needsCommission (self);
+ }
+
+ public UInt32 GetTimeAllowedToLive()
+ {
+ return GooseSubscriber_getTimeAllowedToLive (self);
+ }
+
+ public UInt64 GetTimestamp ()
+ {
+ return GooseSubscriber_getTimestamp (self);
+ }
+
+ public DateTimeOffset GetTimestampsDateTimeOffset ()
+ {
+ UInt64 entryTime = GetTimestamp ();
+
+ DateTimeOffset retVal = new DateTimeOffset (1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
+
+ return retVal.AddMilliseconds (entryTime);
+ }
+
+ ///
+ /// Get the values of the GOOSE data set from the last received GOOSE message
+ ///
+ ///
+ /// The MmsValue instance is only valid in the context of the GooseLister callback.
+ /// Do not store for outside use!
+ ///
+ /// The data set values.
+ public MmsValue GetDataSetValues()
+ {
+ IntPtr mmsValueRef = GooseSubscriber_getDataSetValues (self);
+
+ return (new MmsValue (mmsValueRef));
+ }
+
+ ///
+ /// Releases all resource used by the object.
+ ///
+ /// >
+ /// This function has only to be called when the
+ /// has not been added to the GooseReceiver or has been removed from the GooseReceiver.
+ /// When the GooseReceiver holds a reference it will take care for releasing the resources.
+ /// In this case Dispose MUST not be called! Otherwise the natice resources will be
+ /// released twice.
+ ///
+ public void Dispose()
+ {
+ if (isDisposed == false) {
+ isDisposed = true;
+ GooseSubscriber_destroy (self);
+ self = IntPtr.Zero;
+ }
+ }
+
+ }
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj
index e752d257..61829bc6 100644
--- a/dotnet/IEC61850forCSharp/IEC61850.NET.csproj
+++ b/dotnet/IEC61850forCSharp/IEC61850.NET.csproj
@@ -48,6 +48,7 @@
+
\ No newline at end of file
diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln
index b199c1aa..59941c71 100644
--- a/dotnet/dotnet.sln
+++ b/dotnet/dotnet.sln
@@ -38,6 +38,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server1", "server1\server1.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_client_example", "tls_client_example\tls_client_example.csproj", "{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "goose_subscriber", "goose_subscriber\goose_subscriber.csproj", "{1285372C-2E62-494A-A661-8D5D3873318C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -48,6 +50,10 @@ Global
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BECEC77-2315-4B95-AFF9-E6007E644BBF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1285372C-2E62-494A-A661-8D5D3873318C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1285372C-2E62-494A-A661-8D5D3873318C}.Release|Any CPU.Build.0 = Release|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14C71267-2F38-460D-AA55-6803EE80AFB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/dotnet/goose_subscriber/Program.cs b/dotnet/goose_subscriber/Program.cs
new file mode 100644
index 00000000..df84f032
--- /dev/null
+++ b/dotnet/goose_subscriber/Program.cs
@@ -0,0 +1,68 @@
+using System;
+
+using IEC61850.GOOSE.Subscriber;
+using System.Threading;
+using IEC61850.Common;
+
+namespace goose_subscriber
+{
+ class MainClass
+ {
+ private static void gooseListener (GooseSubscriber subscriber, object parameter)
+ {
+ Console.WriteLine ("Received GOOSE message:\n-------------------------");
+
+ Console.WriteLine (" stNum: " + subscriber.GetStNum ());
+
+ Console.WriteLine (" sqNum: " + subscriber.GetSqNum ());
+
+
+ MmsValue values = subscriber.GetDataSetValues ();
+
+ Console.WriteLine (" values: " +values.Size ().ToString ());
+
+ foreach (MmsValue value in values) {
+ Console.WriteLine (" value: " + value.ToString ());
+ }
+ }
+
+ public static void Main (string[] args)
+ {
+ Console.WriteLine ("Starting GOOSE subscriber...");
+
+ GooseReceiver receiver = new GooseReceiver ();
+
+ receiver.SetInterfaceId ("eth0");
+
+ GooseSubscriber subscriber = new GooseSubscriber ("simpleIOGenericIO/LLN0$GO$gcbAnalogValues");
+
+ subscriber.SetAppId(1000);
+
+ subscriber.SetListener (gooseListener, null);
+
+ receiver.AddSubscriber (subscriber);
+
+ receiver.Start ();
+
+ if (receiver.IsRunning ()) {
+
+ bool running = true;
+
+ /* run until Ctrl-C is pressed */
+ Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
+ e.Cancel = true;
+ running = false;
+ };
+
+ while (running) {
+ Thread.Sleep (100);
+ }
+
+ receiver.Stop ();
+ } else
+ Console.WriteLine ("Failed to start GOOSE receiver. Running as root?");
+
+ receiver.Dispose ();
+ }
+ }
+}
diff --git a/dotnet/goose_subscriber/Properties/AssemblyInfo.cs b/dotnet/goose_subscriber/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..d42b17b9
--- /dev/null
+++ b/dotnet/goose_subscriber/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 ("goose_subscriber")]
+[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/src/goose/goose_receiver.c b/src/goose/goose_receiver.c
index d18dccd8..e7c70eb9 100644
--- a/src/goose/goose_receiver.c
+++ b/src/goose/goose_receiver.c
@@ -722,13 +722,16 @@ gooseReceiverLoop(void* threadParameter)
GooseReceiver_startThreadless(self);
- while (self->running) {
+ if (self->running) {
- if (GooseReceiver_tick(self) == false)
- Thread_sleep(1);
- }
+ while (self->running) {
+
+ if (GooseReceiver_tick(self) == false)
+ Thread_sleep(1);
+ }
- GooseReceiver_stopThreadless(self);
+ GooseReceiver_stopThreadless(self);
+ }
self->stopped = true;
}
@@ -815,7 +818,8 @@ GooseReceiver_startThreadless(GooseReceiver self)
void
GooseReceiver_stopThreadless(GooseReceiver self)
{
- Ethernet_destroySocket(self->ethSocket);
+ if (self->ethSocket)
+ Ethernet_destroySocket(self->ethSocket);
self->running = false;
}