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.
libiec61850/dotnet/IEC61850forCSharp/SampledValuesSubscriber.cs

484 lines
19 KiB
C#

/*
* SampledValuedSubscriber.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.Runtime.InteropServices;
namespace IEC61850
{
namespace SV
{
namespace Subscriber
{
/// <summary>
/// SV receiver.
/// </summary>
/// A receiver is responsible for processing all SV message for a single Ethernet interface.
/// In order to process messages from multiple Ethernet interfaces you have to create multiple
/// instances.
public class SVReceiver : IDisposable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVReceiver_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_disableDestAddrCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_enableDestAddrCheck(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_addSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_removeSubscriber(IntPtr self, IntPtr subscriber);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_start(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_stop(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVReceiver_isRunning(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVReceiver_setInterfaceId(IntPtr self, string interfaceId);
private IntPtr self;
private bool isDisposed = false;
/// <summary>
/// Initializes a new instance of the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> class.
/// </summary>
public SVReceiver()
{
self = SVReceiver_create();
}
public void SetInterfaceId(string interfaceId)
{
SVReceiver_setInterfaceId(self, interfaceId);
}
public void DisableDestAddrCheck()
{
SVReceiver_disableDestAddrCheck(self);
}
public void EnableDestAddrCheck()
{
SVReceiver_enableDestAddrCheck(self);
}
/// <summary>
/// Add a subscriber to handle
/// </summary>
/// <param name="subscriber">Subscriber.</param>
public void AddSubscriber(SVSubscriber subscriber)
{
SVReceiver_addSubscriber(self, subscriber.self);
}
public void RemoveSubscriber(SVSubscriber subscriber)
{
SVReceiver_removeSubscriber(self, subscriber.self);
}
/// <summary>
/// Start handling SV messages
/// </summary>
public void Start()
{
SVReceiver_start(self);
}
/// <summary>
/// Stop handling SV messges
/// </summary>
public void Stop()
{
SVReceiver_stop(self);
}
public bool IsRunning()
{
return SVReceiver_isRunning(self);
}
/// <summary>
/// Releases all resource used by the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> object.
/// </summary>
/// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="IEC61850.SV.Subscriber.SVReceiver"/>. The
/// <see cref="Dispose"/> method leaves the <see cref="IEC61850.SV.Subscriber.SVReceiver"/> in an unusable state.
/// After calling <see cref="Dispose"/>, you must release all references to the
/// <see cref="IEC61850.SV.Subscriber.SVReceiver"/> so the garbage collector can reclaim the memory that the
/// <see cref="IEC61850.SV.Subscriber.SVReceiver"/> was occupying.</remarks>
public void Dispose()
{
if (isDisposed == false)
{
isDisposed = true;
SVReceiver_destroy(self);
self = IntPtr.Zero;
}
}
~SVReceiver()
{
Dispose();
}
}
/// <summary>
/// SV listener.
/// </summary>
public delegate void SVUpdateListener(SVSubscriber report, object parameter, SVSubscriberASDU asdu);
/// <summary>
/// Sampled Values (SV) Subscriber
///
/// A subscriber is an instance associated with a single stream of measurement data. It is identified
/// by the Ethernet destination address, the appID value (both are on SV message level) and the svID value
/// that is part of each ASDU.
/// </summary>
public class SVSubscriber : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalSVUpdateListener(IntPtr subscriber, IntPtr parameter, IntPtr asdu);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_create([Out] byte[] ethAddr, UInt16 appID);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_create(IntPtr ethAddr, UInt16 appID);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVSubscriber_setListener(IntPtr self, InternalSVUpdateListener listener, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void SVSubscriber_destroy(IntPtr self);
internal IntPtr self;
private bool isDisposed = false;
private SVUpdateListener listener;
private object listenerParameter = null;
private event InternalSVUpdateListener internalListener = null;
private void internalSVUpdateListener(IntPtr subscriber, IntPtr parameter, IntPtr asdu)
{
try
{
if (listener != null)
{
listener(this, listenerParameter, new SVSubscriberASDU(asdu));
}
}
catch (Exception e)
{
// older versions of mono 2.10 (for linux?) cause this exception
Console.WriteLine(e.Message);
}
}
public SVSubscriber(byte[] ethAddr, UInt16 appID)
{
if (ethAddr == null)
{
self = SVSubscriber_create(IntPtr.Zero, appID);
}
else
{
if (ethAddr.Length != 6)
throw new ArgumentException("ethAddr argument has to be of 6 byte size");
self = SVSubscriber_create(ethAddr, appID);
}
}
public void SetListener(SVUpdateListener listener, object parameter)
{
this.listener = listener;
listenerParameter = parameter;
if (internalListener == null)
{
internalListener = new InternalSVUpdateListener(internalSVUpdateListener);
SVSubscriber_setListener(self, internalListener, IntPtr.Zero);
}
}
public void Dispose()
{
if (isDisposed == false)
{
isDisposed = true;
SVSubscriber_destroy(self);
self = IntPtr.Zero;
}
}
}
public class SVSubscriberASDU
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getSmpCnt(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_ASDU_getSvId(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr SVSubscriber_ASDU_getDatSet(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 SVSubscriber_ASDU_getConfRev(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern byte SVSubscriber_ASDU_getSmpMod(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getSmpRate(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasDatSet(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasRefrTm(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasSmpMod(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool SVSubscriber_ASDU_hasSmpRate(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 SVSubscriber_ASDU_getRefrTmAsMs(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern sbyte SVSubscriber_ASDU_getINT8(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int16 SVSubscriber_ASDU_getINT16(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int32 SVSubscriber_ASDU_getINT32(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int64 SVSubscriber_ASDU_getINT64(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern byte SVSubscriber_ASDU_getINT8U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getINT16U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 SVSubscriber_ASDU_getINT32U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 SVSubscriber_ASDU_getINT64U(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern float SVSubscriber_ASDU_getFLOAT32(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern double SVSubscriber_ASDU_getFLOAT64(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt16 SVSubscriber_ASDU_getQuality(IntPtr self, int index);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern int SVSubscriber_ASDU_getDataSize(IntPtr self);
private IntPtr self;
internal SVSubscriberASDU(IntPtr self)
{
this.self = self;
}
public UInt16 GetSmpCnt()
{
return SVSubscriber_ASDU_getSmpCnt(self);
}
public string GetSvId()
{
return Marshal.PtrToStringAnsi(SVSubscriber_ASDU_getSvId(self));
}
public string GetDatSet()
{
return Marshal.PtrToStringAnsi(SVSubscriber_ASDU_getDatSet(self));
}
public UInt32 GetConfRev()
{
return SVSubscriber_ASDU_getConfRev(self);
}
public SmpMod GetSmpMod()
{
return (SmpMod)SVSubscriber_ASDU_getSmpMod(self);
}
public UInt16 GetSmpRate()
{
return SVSubscriber_ASDU_getSmpRate(self);
}
public bool HasDatSet()
{
return SVSubscriber_ASDU_hasDatSet(self);
}
public bool HasRefrRm()
{
return SVSubscriber_ASDU_hasRefrTm(self);
}
public bool HasSmpMod()
{
return SVSubscriber_ASDU_hasSmpMod(self);
}
public bool HasSmpRate()
{
return SVSubscriber_ASDU_hasSmpRate(self);
}
public UInt64 GetRefrTmAsMs()
{
return SVSubscriber_ASDU_getRefrTmAsMs(self);
}
public sbyte GetINT8(int index)
{
return SVSubscriber_ASDU_getINT8(self, index);
}
public Int16 GetINT16(int index)
{
return SVSubscriber_ASDU_getINT16(self, index);
}
public Int32 GetINT32(int index)
{
return SVSubscriber_ASDU_getINT32(self, index);
}
public Int64 GetINT64(int index)
{
return SVSubscriber_ASDU_getINT64(self, index);
}
public byte GetINT8U(int index)
{
return SVSubscriber_ASDU_getINT8U(self, index);
}
public UInt16 GetINT16U(int index)
{
return SVSubscriber_ASDU_getINT16U(self, index);
}
public UInt32 GetINT32U(int index)
{
return SVSubscriber_ASDU_getINT32U(self, index);
}
public UInt64 GetINT64U(int index)
{
return SVSubscriber_ASDU_getINT64U(self, index);
}
public float GetFLOAT32(int index)
{
return SVSubscriber_ASDU_getFLOAT32(self, index);
}
public double GetFLOAT64(int index)
{
return SVSubscriber_ASDU_getFLOAT64(self, index);
}
private struct PTimestamp
{
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 8)]
public byte[] val;
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern PTimestamp SVSubscriber_ASDU_getTimestamp(IntPtr self, int index);
public Timestamp GetTimestamp(int index)
{
PTimestamp retVal = SVSubscriber_ASDU_getTimestamp(self, index);
return new Timestamp(retVal.val);
}
public Quality GetQuality(int index)
{
UInt16 qValue = SVSubscriber_ASDU_getQuality(self, index);
return new Quality(qValue);
}
/// <summary>
/// Gets the size of the payload data in bytes. The payload comprises the data set data.
/// </summary>
/// <returns>The payload data size in byte</returns>
public int GetDataSize()
{
return SVSubscriber_ASDU_getDataSize(self);
}
}
}
}
}