diff --git a/CHANGELOG b/CHANGELOG index 0d6c7d86..87d04bdc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,11 @@ + Changes to version 1.6.1 ------------------------ New features and improvements: +- .NET API: SVControlBlock added +- .NET API: Added additional callbacks to control external access to the data model (required to implement RBAC) - .NET API: IEC61850ServerAPI -> LogStorage function wrapper added. - .NET API: MmsValue -> MmsValue_encodeMmsData and MmsValue_decodeMmsData added. - .NET API: Fixed issue of not printing log entries in log_client and log_server examples. diff --git a/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs b/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs new file mode 100644 index 00000000..9e5423f1 --- /dev/null +++ b/dotnet/IEC61850forCSharp/IEC61850Model/SVControlBlock.cs @@ -0,0 +1,95 @@ +/* + * IEC61850ServerAPI.cs + * + * Copyright 2016-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 . + * + * See COPYING file for the complete license text. + */ +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); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr SVControlBlock_getName(IntPtr self); + + /// + /// 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; + } + + /// + /// 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) + /// + /// the svcontrol instance + public SVControlBlock(IntPtr self) + { + this.self = self; + } + + public string Name + { + get + { + return Marshal.PtrToStringAnsi(SVControlBlock_getName(self)); + } + } + + } + } + +} diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 90fd5bc8..f092170f 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -22,10 +22,18 @@ */ using System; using System.Collections.Generic; +using System.Data; +using System.Data.Common; using System.Diagnostics; using System.Runtime.InteropServices; +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; using static IEC61850.Client.IedConnection; // IEC 61850 API for the libiec61850 .NET wrapper library @@ -79,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) @@ -299,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); + } + } /// @@ -312,6 +333,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LogicalDevice_createEx(string name, IntPtr parent, string ldName); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr LogicalDevice_getSettingGroupControlBlock(IntPtr self); + public IedModel IedModel { get; } public LogicalDevice (IntPtr self, IedModel iedModel) : base (self) @@ -343,6 +367,20 @@ namespace IEC61850 self = LogicalDevice_createEx(inst, parent.self, ldName); } + + /// + /// Get the setting group control block (SGCB) of the logical device + /// + /// the SGCB instance or NULL if no SGCB is available + public SettingGroupControlBlock GetSettingGroupControlBlock() + { + IntPtr sgcb = LogicalDevice_getSettingGroupControlBlock(this.self); + + if(sgcb == IntPtr.Zero) + return null; + + return new SettingGroupControlBlock(sgcb); + } } /// @@ -904,6 +942,9 @@ namespace IEC61850 { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataObject_create(string name, IntPtr parent, int arrayElements); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr ModelNode_getChildWithFc(IntPtr self, string objectReference, int fc); internal DataObject(IntPtr self, ModelNode parent) : base(self) { @@ -921,6 +962,28 @@ namespace IEC61850 self = DataObject_create (name, parent.self, arrayElements); } + /// + /// return a child model node with a given functional constraint + /// Sometimes the name is not enough to identify a model node.This is the case when + /// editable setting groups are used.In this case the setting group members have two different + /// model nodes associated that differ in their FC (SG and SE). + /// + /// the name of the child model node + /// the functional constraint of the model node + /// the model node instance or NULL if model node does not exist. + public DataAttribute GetChildWithFc(string objRef, FunctionalConstraint fc) + { + DataAttribute dataAttribute = null; + IntPtr da = ModelNode_getChildWithFc(this.self, objRef, (int)fc); + + if (da != IntPtr.Zero) + { + dataAttribute = new DataAttribute(da, this); + } + + return dataAttribute; + } + } public class DataAttribute : ModelNode @@ -1686,6 +1749,11 @@ namespace IEC61850 { self = SettingGroupControlBlock_create(parent.self, (byte) actSG, (byte) numOfSGs); } + + public SettingGroupControlBlock(IntPtr self) + { + this.self = self ; + } } public class ClientConnection @@ -2154,6 +2222,21 @@ namespace IEC61850 OBJECT_UNDEFINED = 4 } + public enum ControlBlockAccessType + { + IEC61850_CB_ACCESS_TYPE_READ, + IEC61850_CB_ACCESS_TYPE_WRITE + } + + public enum DataSetOperation + { + DATASET_CREATE, + DATASET_DELETE, + DATASET_READ, + DATASET_WRITE, + DATASET_GET_DIRECTORY + } + public delegate CheckHandlerResult CheckHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck); public static class SqliteLogStorage @@ -2389,6 +2472,177 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] public static extern void IedServer_setListObjectsAccessHandler(IntPtr self, IedServer_ListObjectsAccessHandler handler, IntPtr parameter); + /// + /// Set a handler to control read and write access to control blocks and logs + /// + /// IedServer + /// handler the callback handler to be used + /// a user provided parameter that is passed to the handler + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setControlBlockAccessHandler(IntPtr self, IedServer_ControlBlockAccessHandler handler, IntPtr parameter); + + /// + /// Install the global read access handler + /// The read access handler will be called for every read access before the server grants access to the client + /// + /// IedServer + /// the callback function that is invoked if a client tries to read a data object + /// a user provided parameter that is passed to the callback function + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setReadAccessHandler(IntPtr self, ReadAccessHandler handler, IntPtr parameter); + + /// + /// Set a handler to control access to a dataset (create, delete, read, write, list directory) + /// + /// IedServer + /// the callback handler to be used + /// a user provided parameter that is passed to the handler + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setDataSetAccessHandler(IntPtr self, IedServer_DataSetAccessHandler handler, IntPtr parameter); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setDirectoryAccessHandler(IntPtr self, IedServer_DirectoryAccessHandler handler, IntPtr parameter); + + /// + /// Set the callback handler for the SetActSG event + /// + /// the instance of IedServer to operate on + /// the handle of the setting group control block of the setting group + /// the user provided callback handler + /// a user provided parameter that is passed to the control handler + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setActiveSettingGroupChangedHandler(IntPtr self, IntPtr sgcb, ActiveSettingGroupChangedHandler handler, IntPtr parameter); + + /// + /// Get the active setting group number + /// + /// the instance of IedServer to operate on + /// the handle of the setting group control block of the setting group + /// the number of the active setting group + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern uint IedServer_getActiveSettingGroup(IntPtr self, IntPtr sgcb); + + /// + /// Set the callback handler for the SetEditSG event + /// + /// the instance of IedServer to operate on + /// the handle of the setting group control block of the setting group + /// the user provided callback handler + /// a user provided parameter that is passed to the control handler + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setEditSettingGroupChangedHandler(IntPtr self, IntPtr sgcb, EditSettingGroupChangedHandler handler, IntPtr parameter); + + /// + /// Set the callback handler for the COnfEditSG event + /// + /// the instance of IedServer to operate on + /// the handle of the setting group control block of the setting group + /// the user provided callback handler + /// a user provided parameter that is passed to the control handler + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + public static extern void IedServer_setEditSettingGroupConfirmationHandler(IntPtr self, IntPtr sgcb, EditSettingGroupConfirmationHandler handler, IntPtr parameter); + + ///// + ///// Set a handler for SVCB control block events (enable/disable) + ///// + ///// the instance of IedServer to operate on. + ///// 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); + + ///// + ///// callback handler for SVCB events + ///// + ///// the related SVCB instance + ///// event type + ///// user defined parameter + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void SVCBEventHandler(IntPtr svcb, int eventType, IntPtr parameter); + + /// + /// callback handler to control client read access to data attributes + /// User provided callback function to control MMS client read access to IEC 61850 + /// data objects.The application is to allow read access to data objects for specific clients only. + /// It can be used to implement a role based access control (RBAC). + /// + /// the logical device the client wants to access + /// the logical node the client wants to access + /// the data object the client wants to access + /// the functional constraint of the access + /// the client connection that causes the access + /// the user provided parameter + /// DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate MmsDataAccessError ReadAccessHandler(IntPtr ld, IntPtr ln, IntPtr dataObject, int fc, IntPtr connection, IntPtr parameter); + + /// + /// Callback handler that is invoked when the active setting group is about to be changed by an external client. + /// This function is called BEFORE the active setting group is changed. The user can reject to change the active setting group by returning false. + /// + /// user provided parameter + /// sgcb the setting group control block of the setting group that is about to be changed + /// newActSg the new active setting group + /// connection the client connection that requests the change + /// true if the change is accepted, false otherwise + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool ActiveSettingGroupChangedHandler(IntPtr parameter, IntPtr sgcb, uint newActSg, IntPtr connection); + + /// + /// Callback handler that is invoked when the edit setting group is about to be changed by an external client. + /// In this function the user should update all SE data attributes associated with the given SettingGroupControlBlock. + /// This function is called BEFORE the active setting group is changed.The user can reject to change the + /// edit setting group by returning false. This can be used to implement RBAC. + /// + /// user provided parameter + /// the setting group control block of the setting group that is about to be changed + /// the new edit setting group + /// the client connection that requests the change + /// true if the change is accepted, false otherwise + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool EditSettingGroupChangedHandler(IntPtr parameter, IntPtr sgcb, uint newEditSg, IntPtr connection); + + /// + /// Callback handler that is invoked when the edit setting group has been confirmed by an external client. + /// + /// user provided parameter + /// the setting group control block of the setting group that is about to be changed + /// the edit setting group that has been confirmed + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void EditSettingGroupConfirmationHandler(IntPtr parameter, IntPtr sgcb, uint editSg); + + /// + /// Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory) + /// his callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE + /// + /// user provided parameter + /// client connection that is involved + /// one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY + /// + /// true to allow operation, false to deny operation + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool IedServer_DataSetAccessHandler(IntPtr parameter, IntPtr connection, int operation, string datasetRef); + + /// + /// Callback that is called when a client is invoking a read or write service to a control block or log + /// This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs) + /// + /// user provided parameter + /// client connection that is involved + /// the ACSI class of the object + /// the logical device of the object + /// the logical node of the object + /// the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + /// the name of a sub element of an object or NULL + /// access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE) + /// true to include the object in the service response, otherwise false + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool IedServer_ControlBlockAccessHandler(IntPtr parameter, IntPtr connection, int acsiClass, IntPtr ld, IntPtr ln, string objectName, string subObjectName, int accessType); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool IedServer_DirectoryAccessHandler(IntPtr parameter, IntPtr connection, int category, IntPtr logicalDevice); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool IedServer_ListObjectsAccessHandler(IntPtr parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice ld, LogicalNode ln, string objectName, string subObjectName, FunctionalConstraint fc); @@ -2615,11 +2869,364 @@ namespace IEC61850 } } - public void SetListObjectsAccessHandler(IedServer_ListObjectsAccessHandler handler, System.IntPtr parameter) + public void SetListObjectsAccessHandler(IedServer_ListObjectsAccessHandler handler, IntPtr parameter) { IedServer_setListObjectsAccessHandler(self, handler, parameter); } + public delegate bool ControlBlockAccessHandler(object parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice ld, LogicalNode ln, string objectName, string subObjectName, ControlBlockAccessType accessType); + + private ControlBlockAccessHandler rcbControlHandler = null; + + private object rcbControlHandlerParameter = null; + + private IedServer_ControlBlockAccessHandler internalRCBControlHandler = null; + + private bool InternalRCBControlHandlerImplementation(IntPtr parameter, IntPtr connection, int acsiClass, IntPtr ld, IntPtr ln, string objectName, string subObjectName, int accessType) + { + if (rcbControlHandler != null && connection != IntPtr.Zero && ld != IntPtr.Zero && ln != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + ModelNode ldModelNode = iedModel.GetModelNodeFromNodeRef(ld); + ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(ln); + return rcbControlHandler(rcbControlHandlerParameter, con, (ACSIClass)acsiClass, ldModelNode as LogicalDevice, lnModelNode as LogicalNode, objectName, subObjectName, (ControlBlockAccessType)accessType); + } + + return false; + + } + + /// + /// Set a handler to control read and write access to control blocks and logs + /// + /// the callback handler to be used + /// a user provided parameter that is passed to the handler + public void SetControlBlockAccessHandler(ControlBlockAccessHandler handler, object parameter) + { + rcbControlHandler = handler; + rcbControlHandlerParameter = parameter; + + if (internalRCBControlHandler == null) + { + internalRCBControlHandler = new IedServer_ControlBlockAccessHandler(InternalRCBControlHandlerImplementation); + + IedServer_setControlBlockAccessHandler(self, internalRCBControlHandler, IntPtr.Zero); + } + } + + public delegate bool DataSetAccessHandler(object parameter, ClientConnection connection, DataSetOperation operation, string datasetRef); + + private DataSetAccessHandler dataSetAccessHandler = null; + + private object dataSetAccessHandlerParameter = null; + + private IedServer_DataSetAccessHandler internalDataSetAccessHandler = null; + + /// + /// Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory) + /// note This callback is called before the IedServer_RCBEventHandler and only in case of operations(RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE + /// + /// the callback handler to be used + /// a user provided parameter that is passed to the handler + public void SetDataSetAccessHandler(DataSetAccessHandler handler, object parameter) + { + dataSetAccessHandler = handler; + dataSetAccessHandlerParameter = parameter; + + if (internalDataSetAccessHandler == null) + { + internalDataSetAccessHandler = new IedServer_DataSetAccessHandler(InternalDataSetlHandlerImplementation); + + IedServer_setDataSetAccessHandler(self, internalDataSetAccessHandler, IntPtr.Zero); + } + } + + private bool InternalDataSetlHandlerImplementation (IntPtr parameter, IntPtr connection, int operation, string datasetRef) + { + if (dataSetAccessHandler != null && connection != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + return dataSetAccessHandler(dataSetAccessHandlerParameter, con, (DataSetOperation)operation, datasetRef); + } + + return false; + } + + //------------- Setting group + + public delegate bool InternalActiveSettingGroupChangedHandler(object parameter, SettingGroupControlBlock sgcb, uint newActSg, ClientConnection connection); + + private InternalActiveSettingGroupChangedHandler internalActiveSettingGroupChangedHandler = null; + + private object activeSettingGroupChangedHandlerParameter = null; + + private ActiveSettingGroupChangedHandler activeSettingGroupChangedHandler = null; + + public void SetActiveSettingGroupChangedHandler(InternalActiveSettingGroupChangedHandler handler,SettingGroupControlBlock settingGroupControlBlock, object parameter) + { + internalActiveSettingGroupChangedHandler = handler; + activeSettingGroupChangedHandlerParameter = parameter; + + if (activeSettingGroupChangedHandler == null) + { + activeSettingGroupChangedHandler = new ActiveSettingGroupChangedHandler(InternalActiveSettingGroupChangedImplementation); + + IedServer_setActiveSettingGroupChangedHandler(self, settingGroupControlBlock.self, activeSettingGroupChangedHandler, IntPtr.Zero); + } + } + + public int GetActiveSettingGroupChangedHandler(SettingGroupControlBlock settingGroupControlBlock) + { + return Convert.ToInt32(IedServer_getActiveSettingGroup(self, settingGroupControlBlock.self)); + } + + private bool InternalActiveSettingGroupChangedImplementation(IntPtr parameter, IntPtr sgcb, uint newActSg, IntPtr connection) + { + if (sgcb != IntPtr.Zero && connection != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + return internalActiveSettingGroupChangedHandler(activeSettingGroupChangedHandlerParameter, new SettingGroupControlBlock(sgcb), newActSg, con); + } + + return false; + } + + public delegate bool InternalEditSettingGroupChangedHandler(object parameter, SettingGroupControlBlock sgcb, uint newEditSg, ClientConnection connection); + + private InternalEditSettingGroupChangedHandler internalEditSettingGroupChangedHandler = null; + + private object editSettingGroupChangedHandlerParameter = null; + + private EditSettingGroupChangedHandler editSettingGroupChangedHandler = null; + + public void SetEditSettingGroupChangedHandler(InternalEditSettingGroupChangedHandler handler, SettingGroupControlBlock settingGroupControlBlock, object parameter) + { + internalEditSettingGroupChangedHandler = handler; + editSettingGroupChangedHandlerParameter = parameter; + + if (editSettingGroupChangedHandler == null) + { + editSettingGroupChangedHandler = new EditSettingGroupChangedHandler(InternalEditSettingGroupChangedImplementation); + + IedServer_setEditSettingGroupChangedHandler(self, settingGroupControlBlock.self, editSettingGroupChangedHandler, IntPtr.Zero); + } + } + + private bool InternalEditSettingGroupChangedImplementation(IntPtr parameter, IntPtr sgcb, uint newEditSg, IntPtr connection) + { + if (sgcb != IntPtr.Zero && connection != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + return internalEditSettingGroupChangedHandler(editSettingGroupChangedHandlerParameter, new SettingGroupControlBlock(sgcb), newEditSg, con); + } + + return false; + } + + public delegate void InternalEditSettingGroupConfirmationHandler(object parameter, SettingGroupControlBlock sgcb, uint editSg); + + private InternalEditSettingGroupConfirmationHandler internalEditSettingGroupConfirmationHandler = null; + + private object editSettingGroupConfirmationHandlerParameter = null; + + private EditSettingGroupConfirmationHandler editSettingGroupConfirmationHandler = null; + + public void SetEditSettingGroupConfirmationHandler(InternalEditSettingGroupConfirmationHandler handler, SettingGroupControlBlock settingGroupControlBlock, object parameter) + { + internalEditSettingGroupConfirmationHandler = handler; + editSettingGroupConfirmationHandlerParameter = parameter; + + if (editSettingGroupConfirmationHandler == null) + { + editSettingGroupConfirmationHandler = new EditSettingGroupConfirmationHandler(InternalEditSettingGroupConfirmationImplementation); + + IedServer_setEditSettingGroupConfirmationHandler(self, settingGroupControlBlock.self, editSettingGroupConfirmationHandler, IntPtr.Zero); + } + } + + private void InternalEditSettingGroupConfirmationImplementation(IntPtr parameter, IntPtr sgcb, uint editSg) + { + if (sgcb != IntPtr.Zero) + { + internalEditSettingGroupConfirmationHandler(editSettingGroupChangedHandlerParameter, new SettingGroupControlBlock(sgcb), editSg); + } + } + + + //------------ + + public delegate void InternalSVCBEventHandler(SVControlBlock sampledValuesControlBlock, SMVEvent sMVEvent, object parameter); + + private InternalSVCBEventHandler internalSVCBEventHandler = null; + + private object sVCBEventHandlerParameter = null; + + private SVCBEventHandler sVCBEventHandler = null; + + internal class SVCHandlerInfo + { + public SVControlBlock sampledValuesControlBlock = null; + public GCHandle handle; + + public InternalSVCBEventHandler internalSVCBEventHandler = null; + public object svcHandlerParameter = null; + + public SVCHandlerInfo(SVControlBlock sampledValuesControlBlock) + { + this.sampledValuesControlBlock = sampledValuesControlBlock; + this.handle = GCHandle.Alloc(this); + } + + ~SVCHandlerInfo() + { + this.handle.Free(); + } + } + + private Dictionary svcHandlers = new Dictionary(); + + private SVCHandlerInfo GetSVCHandlerInfo(SVControlBlock sampledValuesControlBlock) + { + SVCHandlerInfo info; + + svcHandlers.TryGetValue(sampledValuesControlBlock, out info); + + if (info == null) + { + info = new SVCHandlerInfo(sampledValuesControlBlock); + svcHandlers.Add(sampledValuesControlBlock, info); + } + + return info; + } + + public void SetSVCBHandler(InternalSVCBEventHandler handler, SVControlBlock sampledValuesControlBlock, object parameter) + { + SVCHandlerInfo info = GetSVCHandlerInfo(sampledValuesControlBlock); + + info.internalSVCBEventHandler = handler; + info.svcHandlerParameter = parameter; + + if (sVCBEventHandler == null) + sVCBEventHandler = new SVCBEventHandler(InternalSVCBEventHandlerImplementation); + + IedServer_setSVCBHandler(self, sampledValuesControlBlock.Self, sVCBEventHandler, GCHandle.ToIntPtr(info.handle)); + } + + + private void InternalSVCBEventHandlerImplementation(IntPtr svcb, int eventType, IntPtr parameter) + { + GCHandle handle = GCHandle.FromIntPtr(parameter); + + SVCHandlerInfo info = (SVCHandlerInfo)handle.Target; + + 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); + + private InternalReadAccessHandler internalReadAccessHandler = null; + + private object internalReadAccessHandlerParameter = null; + + private ReadAccessHandler readAccessHandler = null; + + public void SetReadAccessHandler(InternalReadAccessHandler handler, object parameter) + { + internalReadAccessHandler = handler; + internalReadAccessHandlerParameter = parameter; + + if (readAccessHandler == null) + { + readAccessHandler = new ReadAccessHandler (InternalReadHandlerImplementation); + + IedServer_setReadAccessHandler(self, readAccessHandler, IntPtr.Zero); + } + } + + private MmsDataAccessError InternalReadHandlerImplementation(IntPtr ld, IntPtr ln, IntPtr dataObject, int fc, IntPtr connection, IntPtr parameter) + { + if (internalReadAccessHandler != null && ld != IntPtr.Zero && ln != IntPtr.Zero && connection != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + ModelNode ldModelNode = iedModel.GetModelNodeFromNodeRef(ld); + ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(ln); + ModelNode doModelNode = null; + + if(dataObject != IntPtr.Zero) + doModelNode = iedModel.GetModelNodeFromNodeRef(dataObject); + + return internalReadAccessHandler(ldModelNode as LogicalDevice, lnModelNode as LogicalNode, doModelNode as DataObject, (FunctionalConstraint)fc, con, internalReadAccessHandlerParameter); + } + + return MmsDataAccessError.UNKNOWN; + } + + public enum IedServer_DirectoryCategory + { + DIRECTORY_CAT_LD_LIST, + DIRECTORY_CAT_DATA_LIST, + DIRECTORY_CAT_DATASET_LIST, + DIRECTORY_CAT_LOG_LIST + } + + public delegate bool InternalDirectoryAccessHandler(object parameter, ClientConnection connection, IedServer_DirectoryCategory category,LogicalDevice ld); + + private InternalDirectoryAccessHandler internalDirectoryAccessHandler = null; + + private object internalDirectoryAccessHandlerParameter = null; + + private IedServer_DirectoryAccessHandler directoryAccessHandler = null; + + public void SetDirectoryAccessHandler(InternalDirectoryAccessHandler handler, object parameter) + { + internalDirectoryAccessHandler = handler; + internalDirectoryAccessHandlerParameter = parameter; + + if (directoryAccessHandler == null) + { + directoryAccessHandler = new IedServer_DirectoryAccessHandler(DirectoryAccessHandler); + + IedServer_setDirectoryAccessHandler(self, directoryAccessHandler, IntPtr.Zero); + } + } + + private bool DirectoryAccessHandler(IntPtr parameter, IntPtr connection, int category, IntPtr logicalDevice) + { + if (internalDirectoryAccessHandler != null && connection != IntPtr.Zero) + { + ClientConnection con = null; + + this.clientConnections.TryGetValue(connection, out con); + + ModelNode ldModelNode = null; + + if(logicalDevice != IntPtr.Zero) + ldModelNode = iedModel.GetModelNodeFromNodeRef(logicalDevice); + + return internalDirectoryAccessHandler(internalDirectoryAccessHandlerParameter, con, (IedServer_DirectoryCategory)category, ldModelNode as LogicalDevice); + } + + return false; + } + + private Dictionary writeAccessHandlers = new Dictionary (); private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter) diff --git a/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs b/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs index dc481140..733c2fb1 100644 --- a/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs +++ b/dotnet/IEC61850forCSharp/SampledValuesControlBlock.cs @@ -96,8 +96,9 @@ namespace IEC61850 private bool isDisposed = false; + public IntPtr Self { get => self;} - internal SampledValuesControlBlock(IntPtr iedConnection, string objectReference) + internal SampledValuesControlBlock(IntPtr iedConnection, string objectReference) { self = ClientSVControlBlock_create (iedConnection, objectReference); this.objectReference = objectReference; diff --git a/dotnet/dotnet.sln b/dotnet/dotnet.sln index 359ede93..06679868 100644 --- a/dotnet/dotnet.sln +++ b/dotnet/dotnet.sln @@ -3,54 +3,56 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.10.35004.147 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IEC61850.NET", "IEC61850forCSharp\IEC61850.NET.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IEC61850.NET", "IEC61850forCSharp\IEC61850.NET.csproj", "{C35D624E-5506-4560-8074-1728F1FA1A4D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example1", "example1\example1.csproj", "{C616A6DF-831E-443C-9310-3F343A6E3D1A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "example1", "example1\example1.csproj", "{C616A6DF-831E-443C-9310-3F343A6E3D1A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "model_browsing", "model_browsing\model_browsing.csproj", "{59B85486-F48D-4978-BD35-8F5C3A8288D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "model_browsing", "model_browsing\model_browsing.csproj", "{59B85486-F48D-4978-BD35-8F5C3A8288D4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "datasets", "datasets\datasets.csproj", "{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "datasets", "datasets\datasets.csproj", "{D5C7DD38-032A-49B6-B74F-FFD9724A8AE4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "control", "control\control.csproj", "{C351CFA4-E54E-49A1-86CE-69643535541A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "control", "control\control.csproj", "{C351CFA4-E54E-49A1-86CE-69643535541A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "reporting", "reporting\reporting.csproj", "{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "reporting", "reporting\reporting.csproj", "{9E29B4CE-EE5F-4CA6-85F6-5D1FF8B27BF8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example2", "example2\example2.csproj", "{2A226B6D-1D1F-4BFE-B8CC-158116F71270}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "example2", "example2\example2.csproj", "{2A226B6D-1D1F-4BFE-B8CC-158116F71270}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "authenticate", "authenticate\authenticate.csproj", "{0BECEC77-2315-4B95-AFF9-E6007E644BBF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "authenticate", "authenticate\authenticate.csproj", "{0BECEC77-2315-4B95-AFF9-E6007E644BBF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{FBDFE530-DBEB-474B-BA54-9AB287DD57B3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "files", "files\files.csproj", "{77127456-19B9-4D1A-AEF9-40F8D1C5695E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "files", "files\files.csproj", "{77127456-19B9-4D1A-AEF9-40F8D1C5695E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "example3", "example3\example3.csproj", "{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "example3", "example3\example3.csproj", "{5E5D0FE0-DF44-48D8-A10E-1FB07D34DEA2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "report_new_dataset", "report_new_dataset\report_new_dataset.csproj", "{71485F99-2976-45E6-B73D-4946E594C15C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "report_new_dataset", "report_new_dataset\report_new_dataset.csproj", "{71485F99-2976-45E6-B73D-4946E594C15C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "log_client", "log_client\log_client.csproj", "{14C71267-2F38-460D-AA55-6803EE80AFB4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "log_client", "log_client\log_client.csproj", "{14C71267-2F38-460D-AA55-6803EE80AFB4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{0D2F61F1-A173-44E7-BFB0-B698A1D44D12}" ProjectSection(SolutionItems) = preProject .nuget\packages.config = .nuget\packages.config EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server1", "server1\server1.csproj", "{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server1", "server1\server1.csproj", "{9286D2AB-96ED-4631-AB3C-ED20FF5D6E6C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_client_example", "tls_client_example\tls_client_example.csproj", "{6734BF52-2D0D-476B-8EA2-C9C2D1D69B03}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "goose_subscriber", "goose_subscriber\goose_subscriber.csproj", "{1285372C-2E62-494A-A661-8D5D3873318C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sv_subscriber", "sv_subscriber\sv_subscriber.csproj", "{44651D2D-3252-4FD5-8B8B-5552DBE1B499}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sv_subscriber", "sv_subscriber\sv_subscriber.csproj", "{44651D2D-3252-4FD5-8B8B-5552DBE1B499}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tls_server_example", "tls_server_example\tls_server_example.csproj", "{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "tls_server_example", "tls_server_example\tls_server_example.csproj", "{B63F7A81-1D3A-4F2F-A7C2-D6F77E5BD307}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_setting_groups", "client_example_setting_groups\client_example_setting_groups.csproj", "{0DA95476-B149-450B-AC36-01CEECFC1A43}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "client_example_setting_groups", "client_example_setting_groups\client_example_setting_groups.csproj", "{0DA95476-B149-450B-AC36-01CEECFC1A43}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "client_example_async", "client_example_async\client_example_async.csproj", "{71902641-776A-47D8-9C0E-9ACBBEAC1370}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "client_example_async", "client_example_async\client_example_async.csproj", "{71902641-776A-47D8-9C0E-9ACBBEAC1370}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server_goose_publisher", "server_goose_publisher\server_goose_publisher.csproj", "{C14BB883-86B8-401C-B3D6-B655F55F3298}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "server_goose_publisher", "server_goose_publisher\server_goose_publisher.csproj", "{C14BB883-86B8-401C-B3D6-B655F55F3298}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "log_server", "log_server\log_server.csproj", "{96124F40-D38E-499B-9968-674E0D32F933}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "log_server", "log_server\log_server.csproj", "{96124F40-D38E-499B-9968-674E0D32F933}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server_example_access_control", "server_example_access_control\server_example_access_control.csproj", "{304D9146-1490-46EF-B771-20B0603084F5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -146,6 +148,10 @@ Global {96124F40-D38E-499B-9968-674E0D32F933}.Debug|Any CPU.Build.0 = Debug|Any CPU {96124F40-D38E-499B-9968-674E0D32F933}.Release|Any CPU.ActiveCfg = Release|Any CPU {96124F40-D38E-499B-9968-674E0D32F933}.Release|Any CPU.Build.0 = Release|Any CPU + {304D9146-1490-46EF-B771-20B0603084F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {304D9146-1490-46EF-B771-20B0603084F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {304D9146-1490-46EF-B771-20B0603084F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {304D9146-1490-46EF-B771-20B0603084F5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/dotnet/server_example_access_control/Program.cs b/dotnet/server_example_access_control/Program.cs new file mode 100644 index 00000000..2e6784e1 --- /dev/null +++ b/dotnet/server_example_access_control/Program.cs @@ -0,0 +1,369 @@ +/* + * server_example_access_control.cs + * + * - How to use access control mechanisms + * - How to implement RBAC features based on access control mechanisms + */ + +using System; +using IEC61850.Server; +using IEC61850.Common; +using System.Threading; +using System.Net; +using static IEC61850.Server.IedServer; +using System.Collections.Generic; +using System.Reflection.Metadata; +using IEC61850.Client; +using ReportControlBlock = IEC61850.Server.ReportControlBlock; +using IEC61850.Model; +using System.Data.Common; + +namespace server_access_control +{ + class MainClass + { + class PTOC1Settings + { + public float strVal; + public int opDlTmms; + public int rsDlTmms; + public int rstTms; + } + + public static void Main(string[] args) + { + bool running = true; + + /* run until Ctrl-C is pressed */ + Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) + { + e.Cancel = true; + running = false; + }; + + /* Create new server configuration object */ + IedServerConfig config = new IedServerConfig(); + + /* Set buffer size for buffered report control blocks to 200000 bytes */ + config.ReportBufferSize = 200000; + + /* Set stack compliance to a specific edition of the standard (WARNING: data model has also to be checked for compliance) */ + config.Edition = Iec61850Edition.EDITION_2; + + /* Set the base path for the MMS file services */ + config.FileServiceBasePath = "./vmd-filestore/"; + + /* disable MMS file service */ + config.FileServiceEnabled = false; + + /* enable dynamic data set service */ + config.DynamicDataSetServiceEnabled = true; + + /* disable log service */ + config.LogServiceEnabled = false; + + /* set maximum number of clients */ + config.MaxMmsConnections = 2; + + IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile("model.cfg"); + + IedServer iedServer = new IedServer(iedModel, config); + + iedServer.SetServerIdentity("libiec61850.com", "access control example", "1.0.0"); + + DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.SPCSO1"); + + iedServer.SetControlHandler(spcso1, delegate (ControlAction action, object parameter, MmsValue value, bool test) + { + if (test) + return ControlHandlerResult.FAILED; + + if (value.GetType() == MmsType.MMS_BOOLEAN) + { + Console.WriteLine("received binary control command: "); + + if (value.GetBoolean()) + Console.WriteLine("on\n"); + else + Console.WriteLine("off\n"); + } + else + return ControlHandlerResult.FAILED; + + return ControlHandlerResult.OK; + }, spcso1); + + void ConnectionCallBack(IedServer server, ClientConnection clientConnection, bool connected, object parameter) + { + if (connected) + Console.WriteLine("Connection opened\n"); + else + Console.WriteLine("Connection closed\n"); + } + + var connectionCallBack = new ConnectionIndicationHandler(ConnectionCallBack); + iedServer.SetConnectionIndicationHandler(connectionCallBack, "127.0.0.1"); + + + /* Install handler to log RCB events */ + + 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 (con != null) + { + Console.WriteLine(" caused by client " + con.GetPeerAddress()); + } + else + { + Console.WriteLine(" client = null"); + } + + if ((eventType == RCBEventType.SET_PARAMETER) || (eventType == RCBEventType.GET_PARAMETER)) + { + Console.WriteLine("RCB: "+rcb.Name + " event: "+ eventType .ToString()+ "\n"); + Console.WriteLine(" param: "+ parameterName + "\n"); + Console.WriteLine(" result: "+ serviceError.ToString() + "\n"); + } + + if (eventType == RCBEventType.ENABLED) + { + Console.WriteLine("RCB: "+ rcb.Name + " event: " + eventType.ToString() + "\n"); + string rptId = rcb.RptID; + Console.WriteLine(" rptID: "+ rptId+"\n"); + string dataSet =rcb.DataSet; + Console.WriteLine(" datSet:"+ dataSet+"\n"); + } + + + }, null); + + /* Install handler to control access to control blocks (RCBs, LCBs, GoCBs, SVCBs, SGCBs)*/ + bool ControlBlockAccessCallBack(object parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice ld, LogicalNode ln, string objectName, string subObjectName, ControlBlockAccessType accessType) + { + Console.WriteLine(acsiClass.ToString() + " "+ accessType.ToString() + " access " + ld.GetName() + ln.GetName() +"/"+ objectName + "." + subObjectName + "\n"); + + return true; + } + + iedServer.SetControlBlockAccessHandler(ControlBlockAccessCallBack, iedServer); + + /* By default access to variables with FC=DC and FC=CF is not allowed. + * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used + * by iec61850_client_example1. + */ + iedServer.SetWriteAccessPolicy(FunctionalConstraint.DC, AccessPolicy.ACCESS_POLICY_ALLOW); + + /* Install handler to perform access control on datasets */ + bool dataSetAccessHandler(object parameter, ClientConnection connection, DataSetOperation operation, string datasetRef) + { + Console.WriteLine("Data set access: "+ datasetRef+" operation: "+ operation.ToString() + "\n"); + + return true; + } + + iedServer.SetDataSetAccessHandler(dataSetAccessHandler, iedServer); + + /* Install handler to perform read access control on data model elements + * NOTE: when read access to a data model element is blocked this will also prevent the client + * to read the data model element in a data set or enable a RCB instance that uses a dataset + * containing the restricted data model element. + */ + MmsDataAccessError readAccessHandler(LogicalDevice ld, LogicalNode ln, DataObject dataObject, FunctionalConstraint fc, ClientConnection connection, object parameter) + { + if( dataObject!= null) + Console.WriteLine("Read access to "+ld.GetName() + "/"+ln.GetName() + "."+dataObject.GetName() + "\n"); + else + Console.WriteLine("Read access to "+ld.GetName() + "/"+ln.GetName() + "\n"); + + if (dataObject == null) + { + if (ln.GetName() == "GGIO1") + { + return MmsDataAccessError.OBJECT_ACCESS_DENIED; + } + } + else + { + if (ln.GetName() == "GGIO1" && dataObject.GetName() == "AnIn1") + { + return MmsDataAccessError.OBJECT_ACCESS_DENIED; + } + } + + return MmsDataAccessError.SUCCESS; + } + + iedServer.SetReadAccessHandler(readAccessHandler, null); + + bool directoryAccessHandler(object parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice ld) + { + switch (category) + { + case IedServer_DirectoryCategory.DIRECTORY_CAT_LD_LIST: + + Console.WriteLine("Get list of logical devices from "+ connection.GetPeerAddress()+"\n"); + break; + case IedServer_DirectoryCategory.DIRECTORY_CAT_DATASET_LIST: + Console.WriteLine("Get list of datasets for LD "+ ld.GetName() + " from "+ connection.GetPeerAddress()+"\n"); + break; + case IedServer_DirectoryCategory.DIRECTORY_CAT_DATA_LIST: + Console.WriteLine("Get list of data for LD " + ld.GetName() + " from " + connection.GetPeerAddress() + "\n"); + break; + case IedServer_DirectoryCategory.DIRECTORY_CAT_LOG_LIST: + Console.WriteLine("Get list of logs for LD" + ld.GetName() + " from " + connection.GetPeerAddress() + "\n"); + return false; + } + + return true; + } + + iedServer.SetDirectoryAccessHandler(directoryAccessHandler, null); + + /*SettingGroups*/ + + LogicalDevice logicalDevice = (LogicalDevice)iedModel.GetModelNodeByShortObjectReference("GenericIO"); ; + SettingGroupControlBlock settingGroupControlBlock = logicalDevice.GetSettingGroupControlBlock(); + + List ptoc1Settings = new List(); + ptoc1Settings.Add(new PTOC1Settings { strVal = 1.0f, opDlTmms = 500, rsDlTmms = 500, rstTms = 500 }); + ptoc1Settings.Add(new PTOC1Settings { strVal = 2.0f, opDlTmms = 1500, rsDlTmms = 2500, rstTms = 750 }); + ptoc1Settings.Add(new PTOC1Settings { strVal = 3.0f, opDlTmms = 500, rsDlTmms = 1500, rstTms = 750 }); + ptoc1Settings.Add(new PTOC1Settings { strVal = 3.5f, opDlTmms = 1250, rsDlTmms = 1750, rstTms = 500 }); + ptoc1Settings.Add(new PTOC1Settings { strVal = 3.75f, opDlTmms = 1250, rsDlTmms = 1750, rstTms = 750 }); + + void LoadActiveSgValues(int actSG) + { + + 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"); + iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].opDlTmms); + dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RsDlTmms.setVal"); + iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].rsDlTmms); + dataAttribute = (DataAttribute)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RstTms.setVal"); + iedServer.UpdateInt32AttributeValue(dataAttribute, ptoc1Settings[actSG - 1].rstTms); + } + + void LoadEditSgValues(int actSG) + { + DataObject strVal = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.StrVal"); + DataAttribute setMagF = strVal.GetChildWithFc("setMag.f", FunctionalConstraint.SE); + iedServer.UpdateFloatAttributeValue(setMagF, ptoc1Settings[actSG - 1].strVal); + + DataObject opDlTmms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.OpDlTmms"); + DataAttribute setVal = opDlTmms.GetChildWithFc("setVal", FunctionalConstraint.SE); + iedServer.UpdateInt32AttributeValue(setVal, ptoc1Settings[actSG - 1].opDlTmms); + + DataObject rsDlTmms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RsDlTmms"); + DataAttribute rsDlTmms_setVal = rsDlTmms.GetChildWithFc("setVal", FunctionalConstraint.SE); + iedServer.UpdateInt32AttributeValue(rsDlTmms_setVal, ptoc1Settings[actSG - 1].rsDlTmms); + + DataObject rstTms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RstTms"); + DataAttribute rstTms_setVal = rstTms.GetChildWithFc("setVal", FunctionalConstraint.SE); + iedServer.UpdateInt32AttributeValue(rstTms_setVal, ptoc1Settings[actSG - 1].rstTms); + } + + bool activeSGChangedHandler(object parameter, SettingGroupControlBlock sgcb, uint newActSg, ClientConnection connection) + { + Console.WriteLine("Switch to setting group "+ newActSg +"\n"); + + LoadActiveSgValues(Convert.ToInt32(newActSg)); + + return true; + } + + bool editSGChangedHandler(object parameter, SettingGroupControlBlock sgcb, uint newEditSg, ClientConnection connection) + { + Console.WriteLine("Set edit setting group to " + newEditSg + "\n"); + + LoadEditSgValues(Convert.ToInt32(newEditSg)); + + return true; + } + + void editSGConfirmationHandler(object parameter, SettingGroupControlBlock sgcb, uint editSg) + { + Console.WriteLine("Received edit sg confirm for sg " + editSg + "\n"); + + int edit = Convert.ToInt32(editSg); + + DataObject strVal = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.StrVal"); + DataAttribute setMagF = strVal.GetChildWithFc("setMag.f", FunctionalConstraint.SE); + MmsValue setMagFValue = iedServer.GetAttributeValue(setMagF); + + DataObject opDlTmms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.OpDlTmms"); + DataAttribute setVal = opDlTmms.GetChildWithFc("setVal", FunctionalConstraint.SE); + MmsValue setValValue = iedServer.GetAttributeValue(setVal); + + DataObject rsDlTmms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RsDlTmms"); + DataAttribute rsDlTmmsSetVal = rsDlTmms.GetChildWithFc("setVal", FunctionalConstraint.SE); + MmsValue rsDlTmmsSetValValue = iedServer.GetAttributeValue(rsDlTmmsSetVal); + + DataObject rstTms = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/PTOC1.RstTms"); + DataAttribute rstTmsSetVal = rstTms.GetChildWithFc("setVal", FunctionalConstraint.SE); + MmsValue rstTmsSetValVaue = iedServer.GetAttributeValue(rstTmsSetVal); + + ptoc1Settings[edit - 1].strVal = setMagFValue.ToFloat(); + ptoc1Settings[edit - 1].opDlTmms = setValValue.ToInt32(); + ptoc1Settings[edit - 1].rsDlTmms = rsDlTmmsSetValValue.ToInt32(); + ptoc1Settings[edit - 1].rstTms = rsDlTmmsSetValValue.ToInt32(); + + if (iedServer.GetActiveSettingGroupChangedHandler(sgcb) == edit) + { + LoadActiveSgValues(edit); + } + } + + iedServer.SetActiveSettingGroupChangedHandler(activeSGChangedHandler, settingGroupControlBlock, null); + iedServer.SetEditSettingGroupChangedHandler(editSGChangedHandler, settingGroupControlBlock, null); + iedServer.SetEditSettingGroupConfirmationHandler(editSGConfirmationHandler, settingGroupControlBlock, null); + + LogicalNode logicalNode = (LogicalNode)iedModel.GetModelNodeByShortObjectReference("GenericIO/LLN0"); + SVControlBlock sampledValuesControlBlock_1 = iedModel.GetSVControlBlock(logicalNode, "SMV1"); + SVControlBlock sampledValuesControlBlock_2 = iedModel.GetSVControlBlock(logicalNode, "SMV2"); + + void sVCBEventHandler(SVControlBlock sampledValuesControlBlock, SMVEvent sMVEvent, object parameter) + { + Console.WriteLine("control called " + sampledValuesControlBlock.Name + " Event: " + sMVEvent.ToString() + "\n"); + } + + iedServer.SetSVCBHandler(sVCBEventHandler, sampledValuesControlBlock_1, null); + iedServer.SetSVCBHandler(sVCBEventHandler, sampledValuesControlBlock_2, null); + + iedServer.Start(102); + + if (iedServer.IsRunning()) + { + Console.WriteLine("Server started"); + + GC.Collect(); + + DataObject ggio1AnIn1 = (DataObject)iedModel.GetModelNodeByShortObjectReference("GenericIO/GGIO1.AnIn1"); + + DataAttribute ggio1AnIn1magF = (DataAttribute)ggio1AnIn1.GetChild("mag.f"); + DataAttribute ggio1AnIn1T = (DataAttribute)ggio1AnIn1.GetChild("t"); + + float floatVal = 1.0f; + + while (running) + { + floatVal += 1f; + iedServer.UpdateTimestampAttributeValue(ggio1AnIn1T, new Timestamp(DateTime.Now)); + iedServer.UpdateFloatAttributeValue(ggio1AnIn1magF, floatVal); + Thread.Sleep(100); + } + + iedServer.Stop(); + Console.WriteLine("Server stopped"); + } + else + { + Console.WriteLine("Failed to start server"); + } + + iedServer.Destroy(); + } + } +} \ No newline at end of file diff --git a/dotnet/server_example_access_control/model.cfg b/dotnet/server_example_access_control/model.cfg new file mode 100644 index 00000000..d01cb427 --- /dev/null +++ b/dotnet/server_example_access_control/model.cfg @@ -0,0 +1,481 @@ +MODEL(simpleIO){ +LD(GenericIO){ +LN(GGIO1){ +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); +DA(swRev 0 20 5 16 0); +DA(d 0 20 5 16 0); +} +DO(AnIn1 0){ +DA(mag 0 27 1 17 0){ +DA(f 0 10 1 16 0); +} +DA(q 0 23 1 18 0); +DA(t 0 22 1 16 0); +} +DO(AnIn2 0){ +DA(mag 0 27 1 17 0){ +DA(f 0 10 1 16 0); +} +DA(q 0 23 1 18 0); +DA(t 0 22 1 16 0); +} +DO(AnIn3 0){ +DA(mag 0 27 1 17 0){ +DA(f 0 10 1 16 0); +} +DA(q 0 23 1 18 0); +DA(t 0 22 1 16 0); +} +DO(AnIn4 0){ +DA(mag 0 27 1 17 0){ +DA(f 0 10 1 16 0); +} +DA(q 0 23 1 18 0); +DA(t 0 22 1 16 0); +} +DO(SPCSO1 0){ +DA(origin 0 27 0 16 0){ +DA(orCat 0 12 0 16 0); +DA(orIdent 0 13 0 16 0); +} +DA(ctlNum 0 6 0 16 0); +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +DA(ctlModel 0 12 4 16 0)=1; +DA(Oper 0 27 12 16 0){ +DA(ctlVal 0 0 12 16 0); +DA(origin 0 27 12 16 0){ +DA(orCat 0 12 12 16 0); +DA(orIdent 0 13 12 16 0); +} +DA(ctlNum 0 6 12 16 0); +DA(T 0 22 12 16 0); +DA(Test 0 0 12 16 0); +DA(Check 0 24 12 16 0); +} +} +DO(SPCSO2 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(Oper 0 27 12 16 0){ +DA(ctlVal 0 0 12 16 0); +DA(origin 0 27 12 16 0){ +DA(orCat 0 12 12 16 0); +DA(orIdent 0 13 12 16 0); +} +DA(ctlNum 0 6 12 16 0); +DA(T 0 22 12 16 0); +DA(Test 0 0 12 16 0); +DA(Check 0 24 12 16 0); +} +DA(ctlModel 0 12 4 16 0)=1; +DA(t 0 22 0 16 0); +} +DO(SPCSO3 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(Oper 0 27 12 16 0){ +DA(ctlVal 0 0 12 16 0); +DA(origin 0 27 12 16 0){ +DA(orCat 0 12 12 16 0); +DA(orIdent 0 13 12 16 0); +} +DA(ctlNum 0 6 12 16 0); +DA(T 0 22 12 16 0); +DA(Test 0 0 12 16 0); +DA(Check 0 24 12 16 0); +} +DA(ctlModel 0 12 4 16 0)=1; +DA(t 0 22 0 16 0); +} +DO(SPCSO4 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(Oper 0 27 12 16 0){ +DA(ctlVal 0 0 12 16 0); +DA(origin 0 27 12 16 0){ +DA(orCat 0 12 12 16 0); +DA(orIdent 0 13 12 16 0); +} +DA(ctlNum 0 6 12 16 0); +DA(T 0 22 12 16 0); +DA(Test 0 0 12 16 0); +DA(Check 0 24 12 16 0); +} +DA(ctlModel 0 12 4 16 0)=1; +DA(t 0 22 0 16 0); +} +DO(Ind1 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Ind2 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Ind3 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Ind4 0){ +DA(stVal 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +} +LN(LPHD1){ +DO(PhyNam 0){ +DA(vendor 0 20 5 16 0); +} +DO(PhyHealth 0){ +DA(stVal 0 12 0 17 0)=1; +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Proxy 0){ +DA(stVal 0 0 0 17 0); +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(SMV1 xxxxMUnn01 Events 0 0 0 1 0 ){ +PA(4 123 4000 010CCD040001); +} +SMVC(SMV2 null Measurements 0 0 0 0 0 ){ +PA(4 123 4000 010CCD040001); +} +} +LN(PTOC1){ +DO(Mod 0){ +DA(stVal 0 12 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +DA(ctlModel 0 12 4 16 0); +} +DO(Beh 0){ +DA(stVal 0 12 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Str 0){ +DA(general 0 0 0 17 0); +DA(dirGeneral 0 12 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(Op 0){ +DA(general 0 0 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(StrVal 0){ +DA(setMag 0 27 7 16 0){ +DA(f 0 10 7 16 0); +} +} +DO(OpDlTmms 0){ +DA(setVal 0 3 7 17 0); +} +DO(RsDlTmms 0){ +DA(setVal 0 3 7 17 0); +} +DO(RstTms 0){ +DA(setVal 0 3 7 17 0); +} +} +LN(LTRK1){ +DO(Beh 0){ +DA(stVal 0 12 0 17 0); +DA(q 0 23 0 18 0); +DA(t 0 22 0 16 0); +} +DO(SpcTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(ctlVal 0 0 8 16 0); +DA(origin 0 27 8 16 0){ +DA(orCat 0 12 8 16 0); +DA(orIdent 0 13 8 16 0); +} +DA(ctlNum 0 6 8 16 0); +DA(T 0 22 8 16 0); +DA(Test 0 0 8 16 0); +DA(Check 0 24 8 16 0); +DA(respAddCause 0 12 8 16 0); +} +DO(DpcTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(ctlVal 0 0 8 16 0); +DA(origin 0 27 8 16 0){ +DA(orCat 0 12 8 16 0); +DA(orIdent 0 13 8 16 0); +} +DA(ctlNum 0 6 8 16 0); +DA(T 0 22 8 16 0); +DA(Test 0 0 8 16 0); +DA(Check 0 24 8 16 0); +DA(respAddCause 0 12 8 16 0); +} +DO(IncTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(ctlVal 0 3 8 16 0); +DA(origin 0 27 8 16 0){ +DA(orCat 0 12 8 16 0); +DA(orIdent 0 13 8 16 0); +} +DA(ctlNum 0 6 8 16 0); +DA(T 0 22 8 16 0); +DA(Test 0 0 8 16 0); +DA(Check 0 24 8 16 0); +DA(respAddCause 0 12 8 16 0); +} +DO(BscTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(ctlVal 0 25 8 16 0); +DA(origin 0 27 8 16 0){ +DA(orCat 0 12 8 16 0); +DA(orIdent 0 13 8 16 0); +} +DA(ctlNum 0 6 8 16 0); +DA(T 0 22 8 16 0); +DA(Test 0 0 8 16 0); +DA(Check 0 24 8 16 0); +DA(respAddCause 0 12 8 16 0); +} +DO(UrcbTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(rptID 0 19 8 16 0); +DA(rptEna 0 0 8 16 0); +DA(resv 0 0 8 16 0); +DA(datSet 0 31 8 16 0); +DA(confRev 0 9 8 16 0); +DA(optFlds 0 26 8 16 0); +DA(bufTm 0 9 8 16 0); +DA(sqNum 0 6 8 16 0); +DA(trgOps 0 26 8 16 0); +DA(intgPd 0 9 8 16 0); +DA(gi 0 0 8 16 0); +} +DO(BrcbTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(rptID 0 19 8 16 0); +DA(rptEna 0 0 8 16 0); +DA(datSet 0 31 8 16 0); +DA(confRev 0 9 8 16 0); +DA(optFlds 0 26 8 16 0); +DA(bufTm 0 9 8 16 0); +DA(sqNum 0 7 8 16 0); +DA(trgOps 0 26 8 16 0); +DA(intgPd 0 9 8 16 0); +DA(gi 0 0 8 16 0); +DA(purgeBuf 0 0 8 16 0); +DA(entryID 0 15 8 16 0); +DA(timeOfEntry 0 28 8 16 0); +DA(resvTms 0 2 8 16 0); +} +DO(GocbTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(goEna 0 0 8 16 0); +DA(goID 0 19 8 16 0); +DA(datSet 0 31 8 16 0); +DA(confRev 0 9 8 16 0); +DA(ndsCom 0 0 8 16 0); +DA(dstAddress 0 29 8 16 0); +} +DO(SgcbTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(numOfSG 0 6 8 16 0); +DA(actSG 0 6 8 16 0); +DA(editSG 0 6 8 16 0); +DA(cnfEdit 0 0 8 16 0); +DA(lActTm 0 22 8 16 0); +DA(resvTms 0 7 8 16 0); +} +DO(LocbTrk 0){ +DA(objRef 0 31 8 20 0); +DA(serviceType 0 12 8 16 0); +DA(errorCode 0 12 8 16 0); +DA(originatorID 0 13 8 16 0); +DA(t 0 22 8 16 0); +DA(d 0 20 5 16 0); +DA(dU 0 21 5 16 0); +DA(cdcNs 0 20 11 16 0); +DA(cdcName 0 20 11 16 0); +DA(dataNs 0 20 11 16 0); +DA(logEna 0 0 8 16 0); +DA(datSet 0 31 8 16 0); +DA(bufTm 0 9 8 16 0); +DA(trgOps 0 26 8 16 0); +DA(intgPd 0 9 8 16 0); +DA(logRef 0 31 8 16 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 new file mode 100644 index 00000000..941a4574 --- /dev/null +++ b/dotnet/server_example_access_control/server_example_access_control.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + Always + + + + + + + + diff --git a/dotnet/server_example_access_control/simpleIO_access_control.icd b/dotnet/server_example_access_control/simpleIO_access_control.icd new file mode 100644 index 00000000..b70d8921 --- /dev/null +++ b/dotnet/server_example_access_control/simpleIO_access_control.icd @@ -0,0 +1,660 @@ + + +
+
+ + + Station bus + 10 + +
+

0.0.0.0

+

255.255.255.0

+

192.168.2.1

+

1,3,9999,33

+

33

+

00000001

+

0001

+

0001

+

102

+
+ +
+

1

+

4

+

01-0c-cd-01-00-01

+

1000

+
+ 1000 + 3000 +
+ +
+

01-0C-CD-04-00-01

+

4000

+

4

+

123

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + ok + + + + + + + on + + + status-only + + + + + on + + + + + ok + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + direct-with-normal-security + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + + + status-only + + + + + on + + + + + ok + + + + + MZ Automation + + + 1.3.0 + + + libiec61850 server example + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unknown + Not-supported + Blocked-by-switching-hierarchy + Select-failed + Invalid-position + Position-reached + Parameter-change-in-execution + Step-limit + Blocked-by-Mode + Blocked-by-process + Blocked-by-interlocking + Blocked-by-synchrocheck + Command-already-in-execution + Blocked-by-health + 1-of-n-control + Abortion-by-cancel + Time-limit-over + Abortion-by-trip + Object-not-selected + Object-already-selected + No-access-authority + Ended-with-overshoot + Abortion-due-to-deviation + Abortion-by-communication-loss + Blocked-by-command + None + Inconsistent-parameters + Locked-by-other-client + + + on + blocked + test + test/blocked + off + + + ok + warning + alarm + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + no-error + instance-not-available + instance-in-use + access-violation + access-not-allowed-in-current-state + parameter-value-inappropriate + parameter-value-inconsistent + class-not-supported + instance-locked-by-other-client + control-must-be-selected + type-conflict + failed-due-to-communications-constraint + failed-due-to-server-constraint + + + Unknown + Associate + Abort + Release + GetServerDirectory + GetLogicalDeviceDirectory + GetAllDataValues + GetDataValues + SetDataValues + GetDataDirectory + GetDataDefinition + GetDataSetValues + SetDataSetValues + CreateDataSet + DeleteDataSet + GetDataSetDirectory + SelectActiveSG + SelectEditSG + SetEditSGValue + ConfirmEditSGValues + GetEditSGValue + GetSGCBValues + Report + GetBRCBValues + SetBRCBValues + GetURCBValues + SetURCBValues + GetLCBValues + SetLCBValues + QueryLogByTime + QueryLogAfter + GetLogStatus + SendGOOSEMessage + GetGoCBValues + SetGoCBValues + GetGoReference + GetGOOSEElementNumber + SendMSVMessage + GetMSVCBValues + SetMSVCBValues + SendUSVMessage + GetUSVCBValues + SetUSVCBValues + Select + SelectWithValue + Cancel + Operate + CommandTermination + TimeActivatedOperate + GetFile + SetFile + DeleteFile + GetFileAttributValues + TimeSynchronisation + InternalChange + + + unknown + forward + backward + both + + +
\ No newline at end of file diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index 6b13ca7b..58554d09 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -114,6 +114,7 @@ controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClas { printf("%s %s access %s/%s.%s.%s\n", ACSIClassToStr(acsiClass), accessType == IEC61850_CB_ACCESS_TYPE_WRITE ? "write" : "read", ld->name, ln->name, objectName, subObjectName); + return false; /* allow only read access to LCBs */ if (acsiClass == ACSI_CLASS_LCB) { if (accessType == IEC61850_CB_ACCESS_TYPE_READ) diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index ed6a31c4..a3b5aeff 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -468,6 +468,9 @@ LIB61850_API SVControlBlock* SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod, uint16_t smpRate, uint8_t optFlds, bool isUnicast); +LIB61850_API const char* +SVControlBlock_getName(SVControlBlock* self); + LIB61850_API void SVControlBlock_addPhyComAddress(SVControlBlock* self, PhyComAddress* phyComAddress); diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 2290918e..f3c7f3d7 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -587,6 +587,13 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c return self; } + +const char* +SVControlBlock_getName(SVControlBlock* self) +{ + return self->name; +} + void SVControlBlock_addPhyComAddress(SVControlBlock* self, PhyComAddress* phyComAddress) { 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