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; }