diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 4cdfbf11..38a1df17 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850ServerAPI.cs * - * Copyright 2016 Michael Zillgith + * Copyright 2016-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -331,12 +331,19 @@ namespace IEC61850 this.parent = parent; } + internal Dictionary rcbs = new Dictionary(); + public LogicalNode(string name, LogicalDevice parent) { this.parent = parent; base.self = LogicalNode_create(name, parent.self); } + + internal void AddRcb(ReportControlBlock rcb) + { + rcbs.Add(rcb.self, rcb); + } } public enum AccessPolicy { @@ -1301,12 +1308,27 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern string ReportControlBlock_getName(IntPtr self); + public IntPtr self = IntPtr.Zero; + private string name = null; + private LogicalNode parent = null; + public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered, string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd) { self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd); + parent.AddRcb(this); + this.parent = parent; + } + + internal ReportControlBlock(IntPtr self, LogicalNode parent) + { + this.parent = parent; + this.self = self; + parent.AddRcb(this); } public void SetPreconfiguredClient(byte[] clientAddress) @@ -1316,6 +1338,28 @@ namespace IEC61850 else if (clientAddress.Length == 6) ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress); } + + public string Name + { + get + { + if (name == null) + { + name = ReportControlBlock_getName(self); + } + + return name; + } + } + + public LogicalNode Parent + { + get + { + return parent; + } + } + } /// @@ -1691,6 +1735,47 @@ namespace IEC61850 public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); + /// + /// Report control block event types + /// + public enum RCBEventType + { + /// + /// parameter read by client (not implemented). + /// + GET_PARAMETER = 0, + /// + /// parameter set by client. + /// + SET_PARAMETER = 1, + /// + /// reservation canceled. + /// + UNRESERVED = 2, + /// + /// reservation + /// + RESERVED = 3, + /// + /// RCB enabled + /// + ENABLED = 4, + /// + /// RCB disabled + /// + DISABLED = 5, + /// + /// GI report triggered + /// + GI = 6, + /// + /// Purge buffer procedure executed + /// + PURGEBUF = 7 + } + + public delegate void RCBEventHandler(object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError); + public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value, ClientConnection connection, object parameter); @@ -1917,6 +2002,12 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalRCBEventHandler(IntPtr paramter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); + private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; @@ -2561,6 +2652,70 @@ namespace IEC61850 } } + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ReportControlBlock_getParent(IntPtr self); + + + private RCBEventHandler rcbEventHandler = null; + private object rcbEventHandlerParameter = null; + + private InternalRCBEventHandler internalRCBEventHandler = null; + + private void InternalRCBEventHandlerImplementation(IntPtr parameter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError) + { + if (rcbEventHandler != null) + { + ClientConnection con = null; + + if (connection != IntPtr.Zero) + { + this.clientConnections.TryGetValue(connection, out con); + } + + ReportControlBlock reportControlBlock = null; + + if (rcb != IntPtr.Zero) + { + IntPtr lnPtr = ReportControlBlock_getParent(rcb); + + if (lnPtr != IntPtr.Zero) + { + ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr); + + if (lnModelNode != null) + { + LogicalNode ln = lnModelNode as LogicalNode; + + if (ln.rcbs.TryGetValue(rcb, out reportControlBlock) == false) + { + reportControlBlock = new ReportControlBlock(rcb, ln); + } + } + } + } + + rcbEventHandler.Invoke(rcbEventHandlerParameter, reportControlBlock, con, (RCBEventType)eventType, parameterName, (MmsDataAccessError)serviceError); + } + } + + /// + /// Set a callback handler for RCB events + /// + /// the callback handler + /// user provided parameter that is passed to the callback handler + public void SetRCBEventHandler(RCBEventHandler handler, object parameter) + { + rcbEventHandler = handler; + rcbEventHandlerParameter = parameter; + + if (internalRCBEventHandler == null) + { + internalRCBEventHandler = new InternalRCBEventHandler(InternalRCBEventHandlerImplementation); + + IedServer_setRCBEventHandler(self, internalRCBEventHandler, IntPtr.Zero); + } + } + } } diff --git a/dotnet/server1/Program.cs b/dotnet/server1/Program.cs index 569ba74d..c00e097b 100644 --- a/dotnet/server1/Program.cs +++ b/dotnet/server1/Program.cs @@ -67,6 +67,18 @@ namespace server1 }, null); + iedServer.SetRCBEventHandler(delegate (object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError) + { + Console.WriteLine("RCB: " + rcb.Parent.GetObjectReference() + "." + rcb.Name + " event: " + eventType.ToString()); + + if (eventType == RCBEventType.SET_PARAMETER) + { + Console.WriteLine(" param: " + parameterName); + Console.WriteLine(" result: " + serviceError.ToString()); + } + + }, null); + iedServer.Start(102); if (iedServer.IsRunning())