From 5c6ef51159fe971fb60053f2dddee669cf1ee549 Mon Sep 17 00:00:00 2001 From: Maxson Ramon dos Anjos Medeiros Date: Fri, 18 Jul 2025 11:45:18 +0200 Subject: [PATCH] ->Add ICD test file example to .net Tools; ->Add IEC61850Model/SVControlBlock.cs; ->Add SVCBHandler to .net --- .../IEC61850Model/SVControlBlock.cs | 57 +++++++ dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 123 +++++++------- .../server_example_access_control/Program.cs | 25 ++- .../server_example_access_control/model.cfg | 155 +++++++++--------- .../server_example_access_control.csproj | 6 +- .../model_generator_dotnet/Tools/Tools.csproj | 4 + 6 files changed, 221 insertions(+), 149 deletions(-) create mode 100644 dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs diff --git a/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs b/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs new file mode 100644 index 00000000..b1dce114 --- /dev/null +++ b/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs @@ -0,0 +1,57 @@ +using IEC61850.Server; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +// IEC 61850 API for the libiec61850 .NET wrapper library +namespace IEC61850 +{ + // IEC 61850 server API. + namespace Model + { + public enum SMVEvent + { + IEC61850_SVCB_EVENT_ENABLE = 1, + IEC61850_SVCB_EVENT_DISABLE = 0, + } + + public class SVControlBlock : ModelNode + { + private IntPtr self = IntPtr.Zero; + public IedModel parent { get; } + internal IntPtr Self { get => self; } + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr SVControlBlock_create(string name, IntPtr parent, string svID,string dataSet, UInt32 confRev, uint smpMod, + UInt16 smpRate, uint optFlds, bool isUnicast); + + /// + /// create a new Multicast/Unicast Sampled Value (SV) control block (SvCB) + /// Create a new Sampled Value control block(SvCB) and add it to the given logical node(LN) + /// + /// name of the SvCB relative to the parent LN + /// the parent LN + /// the application ID of the SvCB + /// the data set reference to be used by the SVCB + /// the configuration revision + /// the sampling mode used + /// the sampling rate used + /// + /// the optional element configuration + public SVControlBlock(string name, IedModel parent, string svID, string dataSet, UInt32 confRev, uint smpMod, + UInt16 smpRate, uint optFlds, bool isUnicast) + { + this.self = SVControlBlock_create(name, parent.self, svID, dataSet, confRev, smpMod, smpRate, optFlds, isUnicast); + this.parent = parent; + } + + public SVControlBlock(IntPtr self) + { + this.self = self; + } + + } + } + +} diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 98779da5..e971c547 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -30,6 +30,7 @@ using System.Security.Cryptography; using System.Xml.Linq; using IEC61850.Client; using IEC61850.Common; +using IEC61850.Model; using IEC61850.TLS; using static System.Collections.Specialized.BitVector32; using static System.Net.Mime.MediaTypeNames; @@ -86,6 +87,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_getDeviceByInst(IntPtr self, string ldInst); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr IedModel_getSVControlBlock(IntPtr self, IntPtr parentLN, string svcbName); + internal IntPtr self = IntPtr.Zero; internal IedModel(IntPtr self) @@ -306,6 +310,16 @@ namespace IEC61850 return GetModelNodeFromNodeRef (nodeRef); } + public SVControlBlock GetSVControlBlock(LogicalNode logicalNode, string svcbName) + { + IntPtr nodeRef = IedModel_getSVControlBlock(self, logicalNode.self, svcbName); + + if (nodeRef == IntPtr.Zero) + return null; + + return new SVControlBlock(nodeRef); + } + } /// @@ -2497,8 +2511,8 @@ namespace IEC61850 ///// the SVCB control block instance ///// the event handler to be used ///// user provided parameter that is passed to the handler - //[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - //public static extern void IedServer_setSVCBHandler(IntPtr self, IntPtr svcb, SVCBEventHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setSVCBHandler(IntPtr self, IntPtr svcb, SVCBEventHandler handler, IntPtr parameter); ///// ///// callback handler for SVCB events @@ -2506,8 +2520,8 @@ namespace IEC61850 ///// the related SVCB instance ///// event type ///// user defined parameter - //[UnmanagedFunctionPointer(CallingConvention.Cdecl)] - //public delegate void SVCBEventHandler(IntPtr svcb, int eventType, IntPtr parameter); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SVCBEventHandler(IntPtr svcb, int eventType, IntPtr parameter); /// /// callback handler to control client read access to data attributes @@ -3015,79 +3029,74 @@ namespace IEC61850 //------------ - //public delegate bool InternalSVCBEventHandler(SampledValuesControlBlock sampledValuesControlBlock, SMVEvent sMVEvent, object parameter); + public delegate void InternalSVCBEventHandler(SVControlBlock sampledValuesControlBlock, SMVEvent sMVEvent, object parameter); - //private InternalSVCBEventHandler internalSVCBEventHandler = null; + private InternalSVCBEventHandler internalSVCBEventHandler = null; - //private object sVCBEventHandlerParameter = null; + private object sVCBEventHandlerParameter = null; - //private SVCBEventHandler sVCBEventHandler = null; + private SVCBEventHandler sVCBEventHandler = null; - //internal class SVCHandlerInfo - //{ - // public SampledValuesControlBlock sampledValuesControlBlock = null; - // public GCHandle handle; + internal class SVCHandlerInfo + { + public SVControlBlock sampledValuesControlBlock = null; + public GCHandle handle; - // public InternalSVCBEventHandler internalSVCBEventHandler = null; - // public object svcHandlerParameter = null; + public InternalSVCBEventHandler internalSVCBEventHandler = null; + public object svcHandlerParameter = null; - // public SVCHandlerInfo(SampledValuesControlBlock sampledValuesControlBlock) - // { - // this.sampledValuesControlBlock = sampledValuesControlBlock; - // this.handle = GCHandle.Alloc(this); - // } + public SVCHandlerInfo(SVControlBlock sampledValuesControlBlock) + { + this.sampledValuesControlBlock = sampledValuesControlBlock; + this.handle = GCHandle.Alloc(this); + } - // ~SVCHandlerInfo() - // { - // this.handle.Free(); - // } - //} + ~SVCHandlerInfo() + { + this.handle.Free(); + } + } - //private Dictionary svcHandlers = new Dictionary(); + private Dictionary svcHandlers = new Dictionary(); - //private SVCHandlerInfo GetSVCHandlerInfo(SampledValuesControlBlock sampledValuesControlBlock) - //{ - // SVCHandlerInfo info; + private SVCHandlerInfo GetSVCHandlerInfo(SVControlBlock sampledValuesControlBlock) + { + SVCHandlerInfo info; - // svcHandlers.TryGetValue(sampledValuesControlBlock, out info); + svcHandlers.TryGetValue(sampledValuesControlBlock, out info); - // if (info == null) - // { - // info = new SVCHandlerInfo(sampledValuesControlBlock); - // svcHandlers.Add(sampledValuesControlBlock, info); - // } + if (info == null) + { + info = new SVCHandlerInfo(sampledValuesControlBlock); + svcHandlers.Add(sampledValuesControlBlock, info); + } - // return info; - //} + return info; + } - //public void SetSVCBHandler(InternalSVCBEventHandler handler, SampledValuesControlBlock sampledValuesControlBlock, object parameter) - //{ - // SVCHandlerInfo info = GetSVCHandlerInfo(sampledValuesControlBlock); + public void SetSVCBHandler(InternalSVCBEventHandler handler, SVControlBlock sampledValuesControlBlock, object parameter) + { + SVCHandlerInfo info = GetSVCHandlerInfo(sampledValuesControlBlock); - // info.internalSVCBEventHandler = handler; - // info.svcHandlerParameter = parameter; + info.internalSVCBEventHandler = handler; + info.svcHandlerParameter = parameter; - // if (sVCBEventHandler == null) - // sVCBEventHandler = new SVCBEventHandler(InternalSVCBEventHandlerImplementation); + if (sVCBEventHandler == null) + sVCBEventHandler = new SVCBEventHandler(InternalSVCBEventHandlerImplementation); - // IedServer_setSVCBHandler(self, sampledValuesControlBlock.Self, sVCBEventHandler, GCHandle.ToIntPtr(info.handle)); - //} + IedServer_setSVCBHandler(self, sampledValuesControlBlock.Self, sVCBEventHandler, GCHandle.ToIntPtr(info.handle)); + } - //public enum SMVEvent - //{ - // IEC61850_SVCB_EVENT_ENABLE = 1, - // IEC61850_SVCB_EVENT_DISABLE = 0, - //} - //private void InternalSVCBEventHandlerImplementation(IntPtr svcb, int eventType, IntPtr parameter) - //{ - // GCHandle handle = GCHandle.FromIntPtr(parameter); + private void InternalSVCBEventHandlerImplementation(IntPtr svcb, int eventType, IntPtr parameter) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); - // SVCHandlerInfo info = (SVCHandlerInfo)handle.Target; + SVCHandlerInfo info = (SVCHandlerInfo)handle.Target; - // if (info != null && info.internalSVCBEventHandler != null) - // info.internalSVCBEventHandler(info.sampledValuesControlBlock,(SMVEvent)eventType, info.svcHandlerParameter); - //} + if (info != null && info.internalSVCBEventHandler != null) + info.internalSVCBEventHandler(info.sampledValuesControlBlock, (SMVEvent)eventType, info.svcHandlerParameter); + } public delegate MmsDataAccessError InternalReadAccessHandler(LogicalDevice ld, LogicalNode ln, DataObject dataObject, FunctionalConstraint fc, ClientConnection connection, object parameter); diff --git a/dotnet/server_example_access_control/Program.cs b/dotnet/server_example_access_control/Program.cs index 630c437e..a936d0a2 100644 --- a/dotnet/server_example_access_control/Program.cs +++ b/dotnet/server_example_access_control/Program.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Reflection.Metadata; using IEC61850.Client; using ReportControlBlock = IEC61850.Server.ReportControlBlock; +using IEC61850.Model; namespace server_access_control { @@ -222,18 +223,16 @@ namespace server_access_control /* Handler for Sampled values control block */ - //void sVCBEventHandler(SampledValuesControlBlock svcb, SMVEvent smvEvent, object parameter) - //{ - // Console.WriteLine(svcb.GetNoASDU() + " event: "+ smvEvent.ToString() ); - //} - - //implement IedModel_getSVControlBlock && SVControlBlock - //SampledValuesControlBlock sampledValuesControlBlock = (SampledValuesControlBlock)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1"); - + void SVCBEventHandler(SVControlBlock svcb, SMVEvent smvEvent, object parameter) + { + Console.WriteLine("SVControlBlock event " + smvEvent.ToString()); + } - //iedServer.SetSVCBHandler(sVCBEventHandler,) + //LogicalNode logicalNode = (LogicalNode)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1"); + //SVControlBlock sampledValuesControlBlock = iedModel.GetSVControlBlock(logicalNode, "MSVCB01"); + //iedServer.SetSVCBHandler(SVCBEventHandler, sampledValuesControlBlock, null); - //SettingGroups + /*SettingGroups*/ LogicalDevice logicalDevice = (LogicalDevice)iedModel.GetModelNodeByShortObjectReference("GenericIO"); ; SettingGroupControlBlock settingGroupControlBlock = logicalDevice.GetSettingGroupControlBlock(); @@ -250,11 +249,11 @@ namespace server_access_control DataAttribute dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.StrVal.setMag.f"); iedServer.UpdateFloatAttributeValue(dataAttribute, ptoc1Settings[actSG - 1].strVal); - dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.opDlTmms.setVal"); + dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.OpDlTmms.setVal"); iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].opDlTmms); - dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.rsDlTmms.setVal"); + dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RsDlTmms.setVal"); iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].rsDlTmms); - dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.rstTms.setVal"); + dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RstTms.setVal"); iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].rstTms); } diff --git a/dotnet/server_example_access_control/model.cfg b/dotnet/server_example_access_control/model.cfg index b3d21dc4..ad0b6472 100644 --- a/dotnet/server_example_access_control/model.cfg +++ b/dotnet/server_example_access_control/model.cfg @@ -1,81 +1,5 @@ MODEL(simpleIO){ LD(GenericIO){ -LN(LLN0){ -SG(1 5) -DO(Mod 0){ -DA(stVal 0 12 0 17 0)=1; -DA(q 0 23 0 18 0); -DA(t 0 22 0 16 0); -DA(ctlModel 0 12 4 16 0)=0; -} -DO(Beh 0){ -DA(stVal 0 12 0 17 0)=1; -DA(q 0 23 0 18 0); -DA(t 0 22 0 16 0); -} -DO(Health 0){ -DA(stVal 0 12 0 17 0)=1; -DA(q 0 23 0 18 0); -DA(t 0 22 0 16 0); -} -DO(NamPlt 0){ -DA(vendor 0 20 5 16 0)="MZ Automation"; -DA(swRev 0 20 5 16 0)="1.3.0"; -DA(d 0 20 5 16 0)="libiec61850 server example"; -DA(configRev 0 20 5 16 0); -DA(ldNs 0 20 11 16 0); -} -DS(Events){ -DE(GGIO1$ST$SPCSO1$stVal); -DE(GGIO1$ST$SPCSO2$stVal); -DE(GGIO1$ST$SPCSO3$stVal); -DE(GGIO1$ST$SPCSO4$stVal); -} -DS(Events2){ -DE(GGIO1$ST$SPCSO1); -DE(GGIO1$ST$SPCSO2); -DE(GGIO1$ST$SPCSO3); -DE(GGIO1$ST$SPCSO4); -} -DS(Measurements){ -DE(GGIO1$MX$AnIn1$mag$f); -DE(GGIO1$MX$AnIn1$q); -DE(GGIO1$MX$AnIn2$mag$f); -DE(GGIO1$MX$AnIn2$q); -DE(GGIO1$MX$AnIn3$mag$f); -DE(GGIO1$MX$AnIn3$q); -DE(GGIO1$MX$AnIn4$mag$f); -DE(GGIO1$MX$AnIn4$q); -} -DS(ServiceTracking){ -DE(LTRK1$SR$SpcTrk); -DE(LTRK1$SR$DpcTrk); -DE(LTRK1$SR$IncTrk); -DE(LTRK1$SR$BscTrk); -DE(LTRK1$SR$UrcbTrk); -DE(LTRK1$SR$BrcbTrk); -DE(LTRK1$SR$GocbTrk); -DE(LTRK1$SR$SgcbTrk); -DE(LTRK1$SR$LocbTrk); -} -RC(EventsRCB01 Events1 0 Events2 1 24 175 50 1000); -RC(EventsIndexed01 Events2 0 Events 1 24 175 50 1000); -RC(EventsIndexed02 Events2 0 Events 1 24 175 50 1000); -RC(EventsIndexed03 Events2 0 Events 1 24 175 50 1000); -RC(Measurements01 Measurements 1 Measurements 1 16 239 50 1000); -RC(Measurements02 Measurements 1 Measurements 1 16 239 50 1000); -RC(Measurements03 Measurements 1 Measurements 1 16 239 50 1000); -RC(brcbServiceTracking01 ServiceTracking 1 ServiceTracking 1 19 228 0 0); -RC(brcbServiceTracking02 ServiceTracking 1 ServiceTracking 1 19 228 0 0); -RC(brcbServiceTracking03 ServiceTracking 1 ServiceTracking 1 19 228 0 0); -LC(EventLog Events GenericIO/LLN0$EventLog 19 0 1 0); -LC(GeneralLog - - 19 0 0 0); -LOG(); -LOG(EventLog); -GC(gcbEvents events Events 3 0 1000 3000){ -PA(4 1 1000 010CCD010001); -} -} LN(LPHD1){ DO(PhyNam 0){ DA(vendor 0 20 5 16 0); @@ -235,6 +159,85 @@ DA(q 0 23 0 18 0); DA(t 0 22 0 16 0); } } +LN(LLN0){ +SG(1 5) +DO(Mod 0){ +DA(stVal 0 12 0 17 0)=1; +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +DA(ctlModel 0 12 4 16 0)=0; +} +DO(Beh 0){ +DA(stVal 0 12 0 17 0)=1; +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Health 0){ +DA(stVal 0 12 0 17 0)=1; +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(NamPlt 0){ +DA(vendor 0 20 5 16 0)="MZ Automation"; +DA(swRev 0 20 5 16 0)="1.3.0"; +DA(d 0 20 5 16 0)="libiec61850 server example"; +DA(configRev 0 20 5 16 0); +DA(ldNs 0 20 11 16 0); +} +DS(Events){ +DE(GGIO1$ST$SPCSO1$stVal); +DE(GGIO1$ST$SPCSO2$stVal); +DE(GGIO1$ST$SPCSO3$stVal); +DE(GGIO1$ST$SPCSO4$stVal); +} +DS(Events2){ +DE(GGIO1$ST$SPCSO1); +DE(GGIO1$ST$SPCSO2); +DE(GGIO1$ST$SPCSO3); +DE(GGIO1$ST$SPCSO4); +} +DS(Measurements){ +DE(GGIO1$MX$AnIn1$mag$f); +DE(GGIO1$MX$AnIn1$q); +DE(GGIO1$MX$AnIn2$mag$f); +DE(GGIO1$MX$AnIn2$q); +DE(GGIO1$MX$AnIn3$mag$f); +DE(GGIO1$MX$AnIn3$q); +DE(GGIO1$MX$AnIn4$mag$f); +DE(GGIO1$MX$AnIn4$q); +} +DS(ServiceTracking){ +DE(LTRK1$SR$SpcTrk); +DE(LTRK1$SR$DpcTrk); +DE(LTRK1$SR$IncTrk); +DE(LTRK1$SR$BscTrk); +DE(LTRK1$SR$UrcbTrk); +DE(LTRK1$SR$BrcbTrk); +DE(LTRK1$SR$GocbTrk); +DE(LTRK1$SR$SgcbTrk); +DE(LTRK1$SR$LocbTrk); +} +RC(EventsRCB01 Events1 0 Events2 1 24 175 50 1000); +RC(EventsIndexed01 Events2 0 Events 1 24 175 50 1000); +RC(EventsIndexed02 Events2 0 Events 1 24 175 50 1000); +RC(EventsIndexed03 Events2 0 Events 1 24 175 50 1000); +RC(Measurements01 Measurements 1 Measurements 1 16 239 50 1000); +RC(Measurements02 Measurements 1 Measurements 1 16 239 50 1000); +RC(Measurements03 Measurements 1 Measurements 1 16 239 50 1000); +RC(brcbServiceTracking01 ServiceTracking 1 ServiceTracking 1 19 228 0 0); +RC(brcbServiceTracking02 ServiceTracking 1 ServiceTracking 1 19 228 0 0); +RC(brcbServiceTracking03 ServiceTracking 1 ServiceTracking 1 19 228 0 0); +LC(EventLog Events GenericIO/LLN0$EventLog 19 0 1 0); +LC(GeneralLog - - 19 0 0 0); +LOG(); +LOG(EventLog); +GC(gcbEvents events Events 3 0 1000 3000){ +PA(4 1 1000 010CCD010001); +} +SMVC(MSVCB01 xxxxMUnn01 Events 0 0 0 1 0 ){ +PA(4 123 4000 010CCD040001); +} +} LN(PTOC1){ DO(Mod 0){ DA(stVal 0 12 0 17 0); diff --git a/dotnet/server_example_access_control/server_example_access_control.csproj b/dotnet/server_example_access_control/server_example_access_control.csproj index c34022ad..618a553d 100644 --- a/dotnet/server_example_access_control/server_example_access_control.csproj +++ b/dotnet/server_example_access_control/server_example_access_control.csproj @@ -8,12 +8,12 @@ - + - - PreserveNewest + + Always diff --git a/tools/model_generator_dotnet/Tools/Tools.csproj b/tools/model_generator_dotnet/Tools/Tools.csproj index 09e31c57..10d47403 100644 --- a/tools/model_generator_dotnet/Tools/Tools.csproj +++ b/tools/model_generator_dotnet/Tools/Tools.csproj @@ -16,6 +16,7 @@ + @@ -49,6 +50,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest