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