- .NET API: Added GooseReceiver/GooseSubscriber

pull/143/head
Michael Zillgith 8 years ago
parent 2282741ef6
commit c20a530761

@ -147,6 +147,9 @@ namespace IEC61850
if (flagDatSet)
parametersMask += 4;
if (flagDstAddress)
parametersMask += 32;
int error;
IedConnection_setGoCBValues (connection, out error, self, parametersMask, singleRequest);

@ -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 <http://www.gnu.org/licenses/>.
*
* See COPYING file for the complete license text.
*/
using System;
using System.Runtime.InteropServices;
using IEC61850.Common;
namespace IEC61850
{
namespace GOOSE
{
namespace Subscriber
{
/// <summary>
/// GOOSE listener.
/// </summary>
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 ();
}
}
/// <summary>
/// Representing a GOOSE subscriber
/// </summary>
/// <description>
/// NOTE: After SetListener is called, do not call any function outside of
/// the callback handler!
/// </description>
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);
}
/// <summary>
/// Get the values of the GOOSE data set from the last received GOOSE message
/// </summary>
/// <remarks>
/// The MmsValue instance is only valid in the context of the GooseLister callback.
/// Do not store for outside use!
/// </remarks>
/// <returns>The data set values.</returns>
public MmsValue GetDataSetValues()
{
IntPtr mmsValueRef = GooseSubscriber_getDataSetValues (self);
return (new MmsValue (mmsValueRef));
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.GOOSE.Subscriber.GooseSubscriber"/> object.
/// </summary>
/// <remarks>>
/// This function has only to be called when the <see cref="IEC61850.GOOSE.Subscriber.GooseSubscriber"/>
/// 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.
/// </remarks>
public void Dispose()
{
if (isDisposed == false) {
isDisposed = true;
GooseSubscriber_destroy (self);
self = IntPtr.Zero;
}
}
}
}
}
}

@ -48,6 +48,7 @@
<Compile Include="TLS.cs" />
<Compile Include="SampledValuesControlBlock.cs" />
<Compile Include="GooseControlBlock.cs" />
<Compile Include="GooseSubscriber.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

@ -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

@ -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 ();
}
}
}

@ -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("")]

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

Loading…
Cancel
Save