You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
371 lines
14 KiB
C#
371 lines
14 KiB
C#
/*
|
|
* GooseSubscriber.cs
|
|
*
|
|
* Copyright 2017-2025 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 IEC61850.Common;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
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;
|
|
|
|
private List<GooseSubscriber> subscribers = new List<GooseSubscriber>();
|
|
|
|
public GooseReceiver()
|
|
{
|
|
self = GooseReceiver_create();
|
|
}
|
|
|
|
public void SetInterfaceId(string interfaceId)
|
|
{
|
|
GooseReceiver_setInterfaceId(self, interfaceId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add the subscriber to be handled by this receiver instance
|
|
/// </summary>
|
|
/// <remarks>A GooseSubscriber can only be added to one GooseReceiver!</remarks>
|
|
/// <param name="subscriber"></param>
|
|
public void AddSubscriber(GooseSubscriber subscriber)
|
|
{
|
|
if (subscriber.attachedToReceiver == false)
|
|
{
|
|
subscriber.attachedToReceiver = true;
|
|
GooseReceiver_addSubscriber(self, subscriber.self);
|
|
subscribers.Add(subscriber);
|
|
}
|
|
}
|
|
|
|
public void RemoveSubscriber(GooseSubscriber subscriber)
|
|
{
|
|
if (subscriber.attachedToReceiver)
|
|
{
|
|
GooseReceiver_removeSubscriber(self, subscriber.self);
|
|
subscribers.Remove(subscriber);
|
|
subscriber.attachedToReceiver = false;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern IntPtr GooseSubscriber_getGoId(IntPtr self);
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern IntPtr GooseSubscriber_getGoCbRef(IntPtr self);
|
|
|
|
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
|
|
private static extern IntPtr GooseSubscriber_getDataSet(IntPtr self);
|
|
|
|
internal IntPtr self;
|
|
|
|
private bool isDisposed = false;
|
|
|
|
// don't call native destructor when attached to a receiver
|
|
internal bool attachedToReceiver = 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;
|
|
listenerParameter = parameter;
|
|
|
|
if (internalListener == null)
|
|
{
|
|
internalListener = new InternalGooseListener(internalGooseListener);
|
|
|
|
GooseSubscriber_setListener(self, internalListener, IntPtr.Zero);
|
|
}
|
|
}
|
|
|
|
public string GetGoId()
|
|
{
|
|
return Marshal.PtrToStringAnsi(GooseSubscriber_getGoId(self));
|
|
}
|
|
|
|
public string GetGoCbRef()
|
|
{
|
|
return Marshal.PtrToStringAnsi(GooseSubscriber_getGoCbRef(self));
|
|
}
|
|
|
|
public string GetDataSet()
|
|
{
|
|
return Marshal.PtrToStringAnsi(GooseSubscriber_getDataSet(self));
|
|
}
|
|
|
|
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;
|
|
|
|
if (attachedToReceiver == false)
|
|
GooseSubscriber_destroy(self);
|
|
|
|
self = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
~GooseSubscriber()
|
|
{
|
|
Dispose();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
} |