/* * IEC61850ServerAPI.cs * * Copyright 2016-2022 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 System; using System.Collections.Generic; using System.Runtime.InteropServices; using IEC61850.Common; using IEC61850.TLS; // IEC 61850 API for the libiec61850 .NET wrapper library namespace IEC61850 { // IEC 61850 server API. namespace Server { /// /// Config file parser. /// public class ConfigFileParser { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ConfigFileParser_createModelFromConfigFileEx(string filename); public static IedModel CreateModelFromConfigFile(string filePath) { IntPtr retVal = ConfigFileParser_createModelFromConfigFileEx (filePath); if (retVal == IntPtr.Zero) { return null; } return new IedModel (retVal); } } /// /// Representation of the IED server data model /// public class IedModel : IDisposable { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_create(string name); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_destroy(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_getModelNodeByObjectReference(IntPtr self, string objectReference); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_getModelNodeByShortObjectReference(IntPtr self, string objectReference); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int ModelNode_getType(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedModel_setIedNameForDynamicModel(IntPtr self, string iedName); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedModel_getDeviceByInst(IntPtr self, string ldInst); internal IntPtr self = IntPtr.Zero; internal IedModel(IntPtr self) { this.self = self; } /* cache managed ModelNode instances of the IedModel */ internal Dictionary modelNodes = new Dictionary(); /// /// Initializes a new instance of the class. /// /// IED name public IedModel(string name) { self = IedModel_create(name); } ~IedModel() { Dispose(); } /// /// Releases all resource used by the object. /// public void Destroy() { Dispose(); } /// /// Releases all resource used by the object. /// /// Call when you are done using the . The /// method leaves the in an unusable state. After /// calling , you must release all references to the so /// the garbage collector can reclaim the memory that the was occupying. public void Dispose() { lock (this) { if (self != IntPtr.Zero) { IedModel_destroy(self); self = IntPtr.Zero; } } } /// /// Creates a new instance with the data model of config file. /// /// new instance /// fila name of the configuration (.cfg) file public static IedModel CreateFromFile(string filename) { return ConfigFileParser.CreateModelFromConfigFile(filename); } /// /// Get parent node. When not found create the parent node and add to modelNode list /// /// The parent node, or when not found /// the native reference of the model node private ModelNode GetParent(IntPtr nodeRef) { ModelNode parentNode = null; IntPtr parentNodeRef = ModelNode.GetNativeParent(nodeRef); if (parentNodeRef != IntPtr.Zero) { if (modelNodes.TryGetValue(parentNodeRef, out parentNode) == false) { int nodeType = ModelNode_getType(parentNodeRef); if (nodeType == 0) { parentNode = new LogicalDevice(parentNodeRef, this); } else { ModelNode parentOfParent = GetParent(parentNodeRef); if (parentOfParent != null) { switch (nodeType) { case 1: parentNode = new LogicalNode(parentNodeRef, parentOfParent); break; case 2: parentNode = new DataObject(parentNodeRef, parentOfParent); break; case 3: parentNode = new DataAttribute(parentNodeRef, parentOfParent); break; default: parentNode = new ModelNode(parentNodeRef, parentOfParent); break; } } } if (parentNode != null) { modelNodes.Add(parentNodeRef, parentNode); } } } return parentNode; } public LogicalDevice GetDeviceByInst(string ldInst) { IntPtr ldPtr = IedModel_getDeviceByInst(self, ldInst); if (ldPtr != IntPtr.Zero) { return new LogicalDevice(ldPtr, this); } else return null; } internal ModelNode GetModelNodeFromNodeRef(IntPtr nodeRef) { ModelNode modelNode = null; modelNodes.TryGetValue(nodeRef, out modelNode); if (modelNode == null) { int nodeType = ModelNode_getType(nodeRef); if (nodeType == 0) { modelNode = new LogicalDevice(nodeRef, this); } else { ModelNode parent = GetParent(nodeRef); if (parent != null) { switch (nodeType) { case 1: modelNode = new LogicalNode(nodeRef, parent); break; case 2: modelNode = new DataObject(nodeRef, parent); break; case 3: modelNode = new DataAttribute(nodeRef, parent); break; default: modelNode = new ModelNode(nodeRef, parent); break; } } } if (modelNode != null) { modelNodes.Add(nodeRef, modelNode); } } return modelNode; } /// /// Change the IED name of the data model /// /// the new IED name public void SetIedName(string iedName) { IedModel_setIedNameForDynamicModel(self, iedName); } /// /// Gets the model node by full object reference. /// /// The model node /// Full object reference including the IED name public ModelNode GetModelNodeByObjectReference(string objectReference) { IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference); if (nodeRef == IntPtr.Zero) return null; return GetModelNodeFromNodeRef (nodeRef); } /// /// Gets the model node by short object reference (without IED name) /// /// The model node /// Object reference without IED name (e.g. LD0/GGIO1.Ind1.stVal) public ModelNode GetModelNodeByShortObjectReference(string objectReference) { IntPtr nodeRef = IedModel_getModelNodeByShortObjectReference(self, objectReference); if (nodeRef == IntPtr.Zero) return null; return GetModelNodeFromNodeRef (nodeRef); } } public class LogicalDevice : ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LogicalDevice_create(string name, IntPtr parent); private IedModel iedModel = null; public IedModel IedModel { get => iedModel; } public LogicalDevice (IntPtr self, IedModel iedModel) : base (self) { this.iedModel = iedModel; } public LogicalDevice(string name, IedModel parent) { this.iedModel = parent; self = LogicalDevice_create(name, parent.self); } } public class LogicalNode : ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LogicalNode_create(string name, IntPtr parent); public LogicalNode (IntPtr self, ModelNode parent) : base(self) { this.parent = parent; } internal Dictionary rcbs = new Dictionary(); public LogicalNode(string name, LogicalDevice parent) { this.parent = parent; base.self = LogicalNode_create(name, parent.self); } internal void AddRcb(ReportControlBlock rcb) { rcbs.Add(rcb.self, rcb); } } public enum AccessPolicy { ACCESS_POLICY_ALLOW = 0, ACCESS_POLICY_DENY = 1 } public enum DataAttributeType { BOOLEAN = 0, INT8 = 1, INT16 = 2, INT32 = 3, INT64 = 4, INT128 = 5, INT8U = 6, INT16U = 7, INT24U = 8, INT32U = 9, FLOAT32 = 10, FLOAT64 = 11, ENUMERATED = 12, OCTET_STRING_64 = 13, OCTET_STRING_6 = 14, OCTET_STRING_8 = 15, VISIBLE_STRING_32 = 16, VISIBLE_STRING_64 = 17, VISIBLE_STRING_65 = 18, VISIBLE_STRING_129 = 19, VISIBLE_STRING_255 = 20, UNICODE_STRING_255 = 21, TIMESTAMP = 22, QUALITY = 23, CHECK = 24, CODEDENUM = 25, GENERIC_BITSTRING = 26, CONSTRUCTED = 27, ENTRY_TIME = 28, PHYCOMADDR = 29, CURRENCY = 30 } public enum ModeValues { ON = 1, BLOCKED = 2, TEST = 3, TEST_BLOCKED = 4, OFF = 5 } public enum HealthValues { OK = 1, WARNING = 2, ALARM = 3 } /// /// The CDC class contains helper functions to create DataObject instances for the /// most common Common Data Classes. /// public class CDC { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SPS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_DPS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_VSS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SEC_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_CMV_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SAV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ACD_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ACT_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SPG_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_VSG_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ENG_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ING_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ASG_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_WYE_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_DEL_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_HST_create(string name, IntPtr parent, uint options, ushort maxPts); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_INS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_MV_create(string name, IntPtr parent, uint options, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_INC_create(string name, IntPtr parent, uint options, uint controlOptions); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_LPL_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_DPL_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ENS_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SPC_create(string name, IntPtr parent, uint options, uint controlOptions); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_DPC_create(string name, IntPtr parent, uint options, uint controlOptions); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_BSC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool hasTransientIndicator); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_APC_create(string name, IntPtr parent, uint options, uint controlOptions, [MarshalAs(UnmanagedType.I1)] bool isIntegerNotFloat); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_BCR_create(string name, IntPtr parent, uint options); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ENC_create(string name, IntPtr parent, uint options, uint controlOptions); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_SPV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasChaManRs); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_STV_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_CMD_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus, [MarshalAs(UnmanagedType.I1)] bool hasCmTm, [MarshalAs(UnmanagedType.I1)] bool hasCmCt); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_ALM_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasOldStatus); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_CTE_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr CDC_TMS_create(string name, IntPtr parent, uint options, uint controlOptions, uint wpOptions, [MarshalAs(UnmanagedType.I1)] bool hasHisRs); public const int CDC_OPTION_DESC = (1 << 2); public const int CDC_OPTION_DESC_UNICODE = (1 << 3); public const int CDC_OPTION_AC_DLNDA = (1 << 4); public const int CDC_OPTION_AC_DLN = (1 << 5); // options that are only valid for DPL CDC public const int CDC_OPTION_DPL_HWREV = (1 << 17); public const int CDC_OPTION_DPL_SWREV = (1 << 18); public const int CDC_OPTION_DPL_SERNUM = (1 << 19); public const int CDC_OPTION_DPL_MODEL = (1 << 20); public const int CDC_OPTION_DPL_LOCATION = (1 << 21); // mandatory data attributes for LLN0 (e.g. LBL configRef) public const int CDC_OPTION_AC_LN0_M = (1 << 24); public const int CDC_OPTION_AC_LN0_EX = (1 << 25); public const int CDC_OPTION_AC_DLD_M = (1 << 26); public static DataObject Create_CDC_SPS(ModelNode parent, string name, uint options) { IntPtr self = CDC_SPS_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_DPS(ModelNode parent, string name, uint options) { IntPtr self = CDC_DPS_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_VSS(ModelNode parent, string name, uint options) { IntPtr self = CDC_VSS_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_SEC(ModelNode parent, string name, uint options) { IntPtr self = CDC_SEC_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_CMV(ModelNode parent, string name, uint options) { IntPtr self = CDC_CMV_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_SAV(ModelNode parent, string name, uint options, bool isIntegerNotFloat) { IntPtr self = CDC_SAV_create(name, parent.self, options, isIntegerNotFloat); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ACD(ModelNode parent, string name, uint options) { IntPtr self = CDC_ACD_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ACT(ModelNode parent, string name, uint options) { IntPtr self = CDC_ACT_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_SPG(ModelNode parent, string name, uint options) { IntPtr self = CDC_SPG_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_VSG(ModelNode parent, string name, uint options) { IntPtr self = CDC_VSG_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ENG(ModelNode parent, string name, uint options) { IntPtr self = CDC_ENG_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ING(ModelNode parent, string name, uint options) { IntPtr self = CDC_ING_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ASG(ModelNode parent, string name, uint options, bool isIntegerNotFloat) { IntPtr self = CDC_ASG_create(name, parent.self, options, isIntegerNotFloat); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_WYE(ModelNode parent, string name, uint options) { IntPtr self = CDC_WYE_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_DEL(ModelNode parent, string name, uint options) { IntPtr self = CDC_DEL_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_HST(ModelNode parent, string name, uint options, ushort maxPts) { IntPtr self = CDC_HST_create(name, parent.self, options, maxPts); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_INS(ModelNode parent, string name, uint options) { IntPtr self = CDC_INS_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_MV(ModelNode parent, string name, uint options, bool isIntegerNotFloat) { IntPtr self = CDC_MV_create(name, parent.self, options, isIntegerNotFloat); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_INC(ModelNode parent, string name, uint options, uint controlOptions) { IntPtr self = CDC_INC_create(name, parent.self, options, controlOptions); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_LPL(ModelNode parent, string name, uint options) { IntPtr self = CDC_LPL_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_DPL(ModelNode parent, string name, uint options) { IntPtr self = CDC_DPL_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject (self, parent); else return null; } public static DataObject Create_CDC_ENS(ModelNode parent, string name, uint options) { IntPtr self = CDC_ENS_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_SPC(ModelNode parent, string name, uint options, uint controlOptions) { IntPtr self = CDC_SPC_create(name, parent.self, options, controlOptions); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_DPC(ModelNode parent, string name, uint options, uint controlOptions) { IntPtr self = CDC_DPC_create(name, parent.self, options, controlOptions); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_BSC(ModelNode parent, string name, uint options, uint controlOptions, bool hasTransientIndicator) { IntPtr self = CDC_BSC_create(name, parent.self, options, controlOptions, hasTransientIndicator); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_APC(ModelNode parent, string name, uint options, uint controlOptions, bool isIntegerNotFloat) { IntPtr self = CDC_APC_create(name, parent.self, options, controlOptions, isIntegerNotFloat); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_BCR(ModelNode parent, string name, uint options) { IntPtr self = CDC_BCR_create(name, parent.self, options); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ENC(ModelNode parent, string name, uint options, uint controlOptions) { IntPtr self = CDC_ENC_create(name, parent.self, options, controlOptions); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_SPV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasChaManRs) { IntPtr self = CDC_SPV_create(name, parent.self, options, controlOptions, wpOptions, hasChaManRs); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_STV(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus) { IntPtr self = CDC_STV_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_CMD(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus, bool hasCmTm, bool hasCmCt) { IntPtr self = CDC_CMD_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus, hasCmTm, hasCmCt); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_ALM(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasOldStatus) { IntPtr self = CDC_ALM_create(name, parent.self, options, controlOptions, wpOptions, hasOldStatus); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_CTE(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs) { IntPtr self = CDC_CTE_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } public static DataObject Create_CDC_TMS(ModelNode parent, string name, uint options, uint controlOptions, uint wpOptions, bool hasHisRs) { IntPtr self = CDC_TMS_create(name, parent.self, options, controlOptions, wpOptions, hasHisRs); if (self != IntPtr.Zero) return new DataObject(self, parent); else return null; } } public class DataObject : ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataObject_create(string name, IntPtr parent, int arrayElements); internal DataObject(IntPtr self, ModelNode parent) : base(self) { this.parent = parent; } public DataObject(string name, ModelNode parent) : this(name, parent, 0) { } public DataObject(string name, ModelNode parent, int arrayElements) { this.parent = parent; self = DataObject_create (name, parent.self, arrayElements); } } public class DataAttribute : ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataAttribute_create(string name, IntPtr parent, int type, int fc, byte triggerOptions, int arrayElements, UInt32 sAddr); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void DataAttribute_setValue(IntPtr self, IntPtr mmsValue); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int DataAttribute_getType(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern byte DataAttribute_getTrgOps(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int DataAttribute_getFC(IntPtr self); internal DataAttribute(IntPtr self, ModelNode parent) : base(self) { this.parent = parent; } /// /// Create a new data attribute and add it to a parent model node /// /// The parent model node has to be of type DataObject or DataAttribute /// the name of the data attribute (e.g. "stVal") /// the parent model node (of type DataObject or DataAttribute) /// the type of the data attribute (CONSTRUCTED if the type contains sub data attributes) /// the functional constraint (FC) of the data attribute /// the trigger options (dupd, dchg, qchg) that cause an event notification /// the number of array elements if the data attribute is an array or 0 /// an optional short address (deprecated) public DataAttribute (string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps, int arrayElements, UInt32 sAddr) { this.parent = parent; self = DataAttribute_create (name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, sAddr); } /// /// Create a new data attribute and add it to a parent model node /// /// The parent model node has to be of type DataObject or DataAttribute /// the name of the data attribute (e.g. "stVal") /// the parent model node (of type DataObject or DataAttribute) /// the type of the data attribute (CONSTRUCTED if the type contains sub data attributes) /// the functional constraint (FC) of the data attribute /// the trigger options (dupd, dchg, qchg) that cause an event notification /// the number of array elements if the data attribute is an array or 0 public DataAttribute(string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps, int arrayElements) { this.parent = parent; self = DataAttribute_create(name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, 0); } /// /// Get IEC 61850 data attribute type of the data attribute /// public DataAttributeType Type { get { return (DataAttributeType)DataAttribute_getType(self); } } /// /// The trigger options (dchg, qchg, dupd) of the data attribute /// public TriggerOptions TrgOps { get { return (TriggerOptions)DataAttribute_getTrgOps(self); } } /// /// The functional constraint (FC) of the data attribute /// public FunctionalConstraint FC { get { return (FunctionalConstraint)DataAttribute_getFC(self); } } /// /// Set the value of the data attribute (can be used to set default values before server is created) /// /// New value for the data attribute public void SetValue(MmsValue value) { DataAttribute_setValue(self, value.valueReference); } } public class ModelNode { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getChild(IntPtr self, string name); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int ModelNode_getType(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getObjectReference(IntPtr self, IntPtr objectReference); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getObjectReferenceEx(IntPtr self, IntPtr objectReference, [MarshalAs(UnmanagedType.I1)] bool withoutIedName); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getParent(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getChildren(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ModelNode_getName(IntPtr self); /**************** * LinkedList ***************/ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LinkedList_getNext(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LinkedList_getData(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void LinkedList_destroyStatic(IntPtr self); public IntPtr self; internal ModelNode parent = null; static internal IntPtr GetNativeParent(IntPtr self) { return ModelNode_getParent(self); } internal ModelNode() { self = IntPtr.Zero; } internal ModelNode(IntPtr self, ModelNode parent) { this.self = self; this.parent = parent; } internal ModelNode(IntPtr self) { this.self = self; } /// /// Gets the IedModel for this ModelNode instance /// /// the IedModel instance of this ModelNode. public IedModel GetIedModel() { if (this is LogicalDevice) { return (this as LogicalDevice).IedModel; } else { if (this.parent != null) return parent.GetIedModel(); else return null; } } /// /// Gets the name of the model node /// /// name of the model node public string GetName() { return Marshal.PtrToStringAnsi(ModelNode_getName(self)); } /// /// Gets the parent node of this model node /// /// The parent node public ModelNode GetParent() { return parent; } /// /// Get the child node of this model node with the given name /// /// The child node or null when there is no child with the given name /// name of the child node public ModelNode GetChild(string name) { IntPtr childPtr = ModelNode_getChild(self, name); if (childPtr == IntPtr.Zero) return null; ModelNode child = null; IedModel iedModel = GetIedModel(); if (iedModel != null) { iedModel.modelNodes.TryGetValue(childPtr, out child); } if (child == null) { int nodeType = ModelNode_getType(childPtr); switch (nodeType) { case 0: child = new LogicalDevice(childPtr, iedModel); break; case 1: child = new LogicalNode(childPtr, this); break; case 2: child = new DataObject(childPtr, this); break; case 3: child = new DataAttribute(childPtr, this); break; default: child = new ModelNode(childPtr, this); break; } if (child != null && iedModel != null) { iedModel.modelNodes.Add(childPtr, child); } } return child; } internal static ModelNode CreateInstance(IntPtr instPtr, ModelNode parent) { int nodeType = ModelNode_getType(instPtr); ModelNode newInstance = null; switch (nodeType) { case 1: newInstance = new LogicalNode(instPtr, parent); break; case 2: newInstance = new DataObject(instPtr, parent); break; case 3: newInstance = new DataAttribute(instPtr, parent); break; default: newInstance = new ModelNode(instPtr, parent); break; } return newInstance; } /// /// Gets the direct child nodes of this ModelNode instance /// /// List of child nodes public LinkedList GetChildren() { LinkedList children = new LinkedList(); IntPtr childListPtr = ModelNode_getChildren(self); if (childListPtr != IntPtr.Zero) { IedModel iedModel = GetIedModel(); IntPtr listElem = LinkedList_getNext(childListPtr); while (listElem != IntPtr.Zero) { IntPtr modelNodePtr = LinkedList_getData(listElem); ModelNode childNode = null; if (iedModel != null) { iedModel.modelNodes.TryGetValue(modelNodePtr, out childNode); } if (childNode == null) { childNode = ModelNode.CreateInstance(modelNodePtr, this); if ((childNode != null) && (iedModel != null)) { iedModel.modelNodes.Add(modelNodePtr, childNode); } } if (childNode != null) children.AddLast(childNode); listElem = LinkedList_getNext(listElem); } LinkedList_destroyStatic(childListPtr); } return children; } /// /// Gets the object reference of the model node /// /// the object reference /// If set to true the object reference is created without IED name. public string GetObjectReference(bool withoutIedName = false) { IntPtr nativeMemory = Marshal.AllocHGlobal(130); IntPtr objRefPtr = ModelNode_getObjectReferenceEx(self, nativeMemory, withoutIedName); if (objRefPtr != IntPtr.Zero) { string objRef = Marshal.PtrToStringAnsi(objRefPtr); Marshal.FreeHGlobal(nativeMemory); return objRef; } else { Marshal.FreeHGlobal(nativeMemory); return null; } } } public class DataSet { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataSet_create(string name, IntPtr parent); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataSet_getName(IntPtr self); public IntPtr self = IntPtr.Zero; internal DataSet(IntPtr dataSetPtr) { self = dataSetPtr; } public DataSet(string name, LogicalNode parent) { self = DataSet_create(name, parent.self); } public string Name { get { return Marshal.PtrToStringAnsi(DataSet_getName(self)); } } } public class DataSetEntry { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr DataSetEntry_create(IntPtr dataSet, string variable, int index, string component); public IntPtr self = IntPtr.Zero; public DataSetEntry(DataSet dataSet, string variable, int index, string component) { self = DataSetEntry_create(dataSet.self, variable, index, component); } } /// /// Report control block (RCB) instance for server data model /// public class ReportControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, [MarshalAs(UnmanagedType.I1)] bool isBuffered, string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ReportControlBlock_setPreconfiguredClient(IntPtr self, byte type, [Out] byte[] buf); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getName(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ReportControlBlock_getRptEna(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getRptID(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getDataSet(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 ReportControlBlock_getConfRev(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 ReportControlBlock_getOptFlds(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 ReportControlBlock_getBufTm(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt16 ReportControlBlock_getSqNum(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 ReportControlBlock_getTrgOps(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt32 ReportControlBlock_getIntgPd(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ReportControlBlock_getGI(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ReportControlBlock_getPurgeBuf(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getEntryId(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt64 ReportControlBlock_getTimeofEntry(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt16 ReportControlBlock_getResvTms(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ReportControlBlock_getResv(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getOwner(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void Memory_free(IntPtr self); public IntPtr self = IntPtr.Zero; private string name = null; private LogicalNode parent = null; public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered, string dataSetName, uint confRev, byte trgOps, byte options, uint bufTm, uint intgPd) { self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRev, trgOps, options, bufTm, intgPd); parent.AddRcb(this); this.parent = parent; } internal ReportControlBlock(IntPtr self, LogicalNode parent) { this.parent = parent; this.self = self; parent.AddRcb(this); } public void SetPreconfiguredClient(byte[] clientAddress) { if (clientAddress.Length == 4) ReportControlBlock_setPreconfiguredClient(self, 4, clientAddress); else if (clientAddress.Length == 6) ReportControlBlock_setPreconfiguredClient(self, 6, clientAddress); } public string Name { get { if (name == null) { name = Marshal.PtrToStringAnsi(ReportControlBlock_getName(self)); } return name; } } public LogicalNode Parent { get { return parent; } } public bool RptEna { get { return ReportControlBlock_getRptEna(self); } } public string RptID { get { IntPtr rptIdPtr = ReportControlBlock_getRptID(self); string rptId = Marshal.PtrToStringAnsi(rptIdPtr); Memory_free(rptIdPtr); return rptId; } } public string DataSet { get { IntPtr dataSetPtr = ReportControlBlock_getDataSet(self); string dataSet = Marshal.PtrToStringAnsi(dataSetPtr); Memory_free(dataSetPtr); return dataSet; } } public UInt32 ConfRev { get { return ReportControlBlock_getConfRev(self); } } public ReportOptions OptFlds { get { return (ReportOptions)ReportControlBlock_getOptFlds(self); } } public UInt32 BufTm { get { return ReportControlBlock_getBufTm(self); } } public UInt16 SqNum { get { return ReportControlBlock_getSqNum(self); } } public TriggerOptions TrgOps { get { return (TriggerOptions)ReportControlBlock_getTrgOps(self); } } public UInt32 IntgPd { get { return ReportControlBlock_getIntgPd(self); } } public bool GI { get { return ReportControlBlock_getGI(self); } } public bool PurgeBuf { get { return ReportControlBlock_getPurgeBuf(self); } } public byte[] EntryID { get { IntPtr entryIdPtr = ReportControlBlock_getEntryId(self); if (entryIdPtr != IntPtr.Zero) { byte[] entryId = null; MmsValue octetStringVal = new MmsValue(entryIdPtr, true); entryId = octetStringVal.getOctetString(); return entryId; } else return null; } } public UInt64 TimeofEntry { get { return ReportControlBlock_getTimeofEntry(self); } } public UInt16 ResvTms { get { return ReportControlBlock_getResvTms(self); } } public bool Resv { get { return ReportControlBlock_getResv(self); } } public byte[] Owner { get { IntPtr mmsValuePtr = ReportControlBlock_getOwner(self); if (mmsValuePtr != IntPtr.Zero) { byte[] owner = null; MmsValue octetStringVal = new MmsValue(mmsValuePtr, true); owner = octetStringVal.getOctetString(); return owner; } else return null; } } } /// /// GOOSE/GSE control block instance for server data model /// public class GSEControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr GSEControlBlock_create(string name, IntPtr parent, string appId, string dataSet, UInt32 confRev, [MarshalAs(UnmanagedType.I1)] bool fixedOffs, int minTime, int maxTime); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void GSEControlBlock_addPhyComAddress(IntPtr self, IntPtr phyComAddress); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr PhyComAddress_create(byte vlanPriority, UInt16 vlanId, UInt16 appId, [Out] byte[] buf); internal IntPtr self = IntPtr.Zero; public GSEControlBlock(string name, LogicalNode parent, string appId, string dataSetName, UInt32 confRev, bool fixedOffs, int minTime, int maxTime) { self = GSEControlBlock_create(name, parent.self, appId, dataSetName, confRev, fixedOffs, minTime, maxTime); } public void AddPhyComAddress(PhyComAddress addr) { IntPtr phyComAddrPtr = PhyComAddress_create(addr.vlanPriority, addr.vlanId, addr.appId, addr.dstAddress); if (phyComAddrPtr != IntPtr.Zero) { GSEControlBlock_addPhyComAddress(self, phyComAddrPtr); } else { Console.WriteLine("ERROR: Failed to create native PhyComAddress instance!"); } } } /// /// Log control block (LCB) instance for server data model /// public class LogControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr LogControlBlock_create(string name, IntPtr parent, string dataSetName, string logRef, byte trgOps, UInt32 intPeriod, [MarshalAs(UnmanagedType.I1)] bool logEna, [MarshalAs(UnmanagedType.I1)] bool reasonCode); internal IntPtr self = IntPtr.Zero; public LogControlBlock(string name, LogicalNode parent, string dataSet, string logRef, byte trgOps, UInt32 intPerdiod, bool logEna, bool reasonCode) { self = LogControlBlock_create(name, parent.self, dataSet, logRef, trgOps, intPerdiod, logEna, reasonCode); } } /// /// Setting group control block for server data model /// public class SettingGroupControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr SettingGroupControlBlock_create(IntPtr parent, byte actSG, byte numOfSGs); internal IntPtr self = IntPtr.Zero; public SettingGroupControlBlock(LogicalNode parent, UInt32 actSG, UInt32 numOfSGs) { self = SettingGroupControlBlock_create(parent.self, (byte) actSG, (byte) numOfSGs); } } public class ClientConnection { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientConnection_getPeerAddress(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ClientConnection_getLocalAddress(IntPtr self); internal IntPtr self; internal ClientConnection (IntPtr self) { this.self = self; } public string GetPeerAddress() { IntPtr peerAddrPtr = ClientConnection_getPeerAddress (self); if (peerAddrPtr != IntPtr.Zero) return Marshal.PtrToStringAnsi (peerAddrPtr); else return null; } public string GetLocalAddress() { IntPtr localAddrPtr = ClientConnection_getLocalAddress(self); if (localAddrPtr != IntPtr.Zero) return Marshal.PtrToStringAnsi(localAddrPtr); else return null; } } public class MmsGooseControlBlock { [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsGooseControlBlock_getName(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsGooseControlBlock_getLogicalNode(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr MmsGooseControlBlock_getDataSet(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool MmsGooseControlBlock_getGoEna(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int MmsGooseControlBlock_getMinTime(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int MmsGooseControlBlock_getMaxTime(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool MmsGooseControlBlock_getFixedOffs(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool MmsGooseControlBlock_getNdsCom(IntPtr self); private IntPtr self; private LogicalNode ln = null; private DataSet dataSet = null; internal MmsGooseControlBlock(IntPtr self, IedModel iedModel) { this.self = self; IntPtr lnPtr = MmsGooseControlBlock_getLogicalNode(self); ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr); if (lnModelNode != null && lnModelNode is LogicalNode) { this.ln = lnModelNode as LogicalNode; } } public string Name { get { return Marshal.PtrToStringAnsi(MmsGooseControlBlock_getName(self)); } } public LogicalNode LN { get { return ln; } } public DataSet DataSet { get { if (dataSet == null) dataSet = new DataSet(MmsGooseControlBlock_getDataSet(self)); return dataSet; } } public bool GoEna { get { return MmsGooseControlBlock_getGoEna(self); } } public int MinTime { get { return MmsGooseControlBlock_getMinTime(self); } } public int MaxTime { get { return MmsGooseControlBlock_getMaxTime(self); } } public bool FixedOffs { get { return MmsGooseControlBlock_getFixedOffs(self); } } public bool NdsCom { get { return MmsGooseControlBlock_getNdsCom(self); } } } /// /// Represents additional context information of the control action that caused the callback invokation /// public class ControlAction { [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ControlAction_setAddCause (IntPtr self, int addCause); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void ControlAction_setError(IntPtr self, int error); [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int ControlAction_getOrCat (IntPtr self); [DllImport ("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ControlAction_getOrIdent (IntPtr self, ref int size); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ControlAction_getClientConnection(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int ControlAction_getCtlNum(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern UInt64 ControlAction_getControlTime(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.I1)] static extern bool ControlAction_isSelect(IntPtr self); private IntPtr self; private IedServer.ControlHandlerInfo info; private IedServer iedServer; internal ControlAction (IntPtr self, IedServer.ControlHandlerInfo info, IedServer iedServer) { this.self = self; this.info = info; this.iedServer = iedServer; } /// /// Sets the error code for the next command termination or application error message. /// /// the errror code to use public void SetError(ControlLastApplError error) { ControlAction_setError(self, (int)error); } /// /// Sets the add cause for the next command termination or application error message /// /// the additional cause code public void SetAddCause (ControlAddCause addCause) { ControlAction_setAddCause (self, (int)addCause); } /// /// Gets the originator category provided by the client /// /// The or cat. public OrCat GetOrCat () { return (OrCat)ControlAction_getOrCat (self); } /// /// Get the originator identifier provided by the client /// /// The or ident. public byte [] GetOrIdent () { int size = 0; IntPtr orIdentPtr = ControlAction_getOrIdent (self, ref size); if (orIdentPtr == IntPtr.Zero) return null; byte [] orIdent = new byte [size]; Marshal.Copy (orIdentPtr, orIdent, 0, size); return orIdent; } /// /// Gets the ctlNum attribute of the control action /// /// The ctlNum value. Valid values are restricted from 0 to 255, -1 means not present public int GetCtlNum() { return ControlAction_getCtlNum(self); } /// /// Gets the control object that is subject to this action /// /// the controllable data object instance public DataObject GetControlObject () { return info.controlObject; } /// /// Gets the time of control execution, if it's a time activated control /// /// The time of control execution or 0 for immediate execution public UInt64 GetControlTime() { return ControlAction_getControlTime(self); } /// /// Gets the tome of control execution as data time offset. /// /// The control execution time as data time offset. public DateTimeOffset GetControlTimeAsDataTimeOffset() { return MmsValue.MsTimeToDateTimeOffset(GetControlTime()); } /// /// Gets the client object associated with the client that caused the control action /// /// The client connection. public ClientConnection GetClientConnection () { ClientConnection con = null; IntPtr conPtr = ControlAction_getClientConnection (self); if (conPtr != IntPtr.Zero) { iedServer.clientConnections.TryGetValue (conPtr, out con); } return con; } /// /// Cehck if the control callback is called by a select or operate command /// /// true, if select, false otherwise. public bool IsSelect() { return ControlAction_isSelect(self); } } public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); /// /// Report control block event types /// public enum RCBEventType { /// /// parameter read by client (not implemented). /// GET_PARAMETER = 0, /// /// parameter set by client. /// SET_PARAMETER = 1, /// /// reservation canceled. /// UNRESERVED = 2, /// /// reservation /// RESERVED = 3, /// /// RCB enabled /// ENABLED = 4, /// /// RCB disabled /// DISABLED = 5, /// /// GI report triggered /// GI = 6, /// /// Purge buffer procedure executed /// PURGEBUF = 7 } public delegate void RCBEventHandler(object parameter, ReportControlBlock rcb, ClientConnection con, RCBEventType eventType, string parameterName, MmsDataAccessError serviceError); public delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value, ClientConnection connection, object parameter); /// /// Reason for the select state change /// public enum SelectStateChangedReason { /// /// Control has been selected /// SELECT_STATE_REASON_SELECTED = 0, /// /// Cancel received for the control /// SELECT_STATE_REASON_CANCELED = 1, /// /// Unselected due to timeout (sboTimeout) /// SELECT_STATE_REASON_TIMEOUT = 2, /// /// Unselected due to successful operate /// SELECT_STATE_REASON_OPERATED = 3, /// /// Unselected due to failed operate /// SELECT_STATE_REASON_OPERATE_FAILED = 4, /// /// Unselected due to disconnection of selecting client /// SELECT_STATE_REASON_DISCONNECTED = 5 } public delegate void ControlSelectStateChangedHandler(ControlAction action, object parameter, bool isSelected, SelectStateChangedReason reason); /// /// Return type of ControlHandler and ControlWaitForExecutionHandler /// public enum ControlHandlerResult { /// /// check or operation failed /// FAILED = 0, /// /// check or operation was successful /// OK = 1, /// /// check or operation is in progress /// WAITING = 2 } public delegate ControlHandlerResult ControlWaitForExecutionHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool synchroCheck); public delegate ControlHandlerResult ControlHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test); public enum CheckHandlerResult { /// /// check passed /// ACCEPTED = -1, /// /// check failed due to hardware fault /// HARDWARE_FAULT = 1, /// /// control is already selected or operated /// TEMPORARILY_UNAVAILABLE = 2, /// /// check failed due to access control reason - access denied for this client or state /// OBJECT_ACCESS_DENIED = 3, /// /// object not visible in this security context ??? /// OBJECT_UNDEFINED = 4 } public delegate CheckHandlerResult CheckHandler (ControlAction action, object parameter, MmsValue ctlVal, bool test, bool interlockCheck); /// /// This class acts as the entry point for the IEC 61850 client API. It represents a single /// (MMS) connection to a server. /// public class IedServer : IDisposable { [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] static extern IntPtr IedServer_createWithConfig(IntPtr modelRef, IntPtr tlsConfiguration, IntPtr serverConfiguratio); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setLocalIpAddress(IntPtr self, string localIpAddress); [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)] static extern void IedServer_start(IntPtr self, int tcpPort); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_stop(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_destroy(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IedServer_isRunning(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern int IedServer_getNumberOfOpenConnections(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_lockDataModel(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_unlockDataModel(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateAttributeValue(IntPtr self, IntPtr DataAttribute, IntPtr MmsValue); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, [MarshalAs(UnmanagedType.I1)] bool value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateInt32AttributeValue(IntPtr self, IntPtr dataAttribute, int value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateInt64AttributeValue(IntPtr self, IntPtr dataAttribute, Int64 value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateFloatAttributeValue(IntPtr self, IntPtr dataAttribute, float value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateVisibleStringAttributeValue(IntPtr self, IntPtr dataAttribute, string value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateUTCTimeAttributeValue(IntPtr self, IntPtr dataAttribute, ulong value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateTimestampAttributeValue(IntPtr self, IntPtr dataAttribute, IntPtr timestamp); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setServerIdentity(IntPtr self, string vendor, string model, string revision); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr IedServer_getFunctionalConstrainedData(IntPtr self, IntPtr dataObject, int fc); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int InternalControlPerformCheckHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool interlockCheck); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int InternalControlWaitForExecutionHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test, [MarshalAs(UnmanagedType.I1)] bool synchoCheck); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int InternalControlHandler (IntPtr action, IntPtr parameter, IntPtr ctlVal, [MarshalAs(UnmanagedType.I1)] bool test); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalSelectStateChangedHandler(IntPtr action, IntPtr parameter, [MarshalAs(UnmanagedType.I1)] bool isSelected, int reason); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setWaitForExecutionHandler(IntPtr self, IntPtr node, InternalControlWaitForExecutionHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setPerformCheckHandler(IntPtr self, IntPtr node, InternalControlPerformCheckHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setControlHandler (IntPtr self, IntPtr node, InternalControlHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setSelectStateChangedHandler(IntPtr self, IntPtr node, InternalSelectStateChangedHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setWriteAccessPolicy(IntPtr self, FunctionalConstraint fc, AccessPolicy policy); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int InternalWriteAccessHandler (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_handleWriteAccess(IntPtr self, IntPtr dataAttribute, InternalWriteAccessHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_handleWriteAccessForComplexAttribute(IntPtr self, IntPtr dataAttribute, InternalWriteAccessHandler handler, IntPtr parameter); public delegate void ConnectionIndicationHandler(IedServer iedServer, ClientConnection clientConnection, bool connected, object parameter); private ConnectionIndicationHandler connectionHandler = null; private object connectionHandlerParameter = null; public void SetConnectionIndicationHandler(ConnectionIndicationHandler handler, object parameter) { connectionHandler = handler; connectionHandlerParameter = parameter; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalConnectionHandler (IntPtr iedServer, IntPtr clientConnection, [MarshalAs(UnmanagedType.I1)] bool connected, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setConnectionIndicationHandler(IntPtr self, InternalConnectionHandler handler, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_enableGoosePublishing(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_disableGoosePublishing(IntPtr self); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setGooseInterfaceId(IntPtr self, string interfaceId); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setGooseInterfaceIdEx(IntPtr self, IntPtr ln, string gcbName, string interfaceId); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_useGooseVlanTag(IntPtr self, IntPtr ln, string gcbName, [MarshalAs(UnmanagedType.I1)] bool useVlanTag); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalGoCBEventHandler(IntPtr goCB, int cbEvent, IntPtr parameter); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setGoCBHandler(IntPtr self, InternalGoCBEventHandler handler, IntPtr parameter); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void InternalRCBEventHandler(IntPtr paramter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; private InternalControlPerformCheckHandler internalControlPerformCheckHandlerRef = null; private InternalControlWaitForExecutionHandler internalControlWaitForExecutionHandlerRef = null; private InternalSelectStateChangedHandler internalSelectedStateChangedHandlerRef = null; internal class ControlHandlerInfo { public DataObject controlObject = null; public GCHandle handle; public ControlHandler controlHandler = null; public object controlHandlerParameter = null; public CheckHandler checkHandler = null; public object checkHandlerParameter = null; public ControlWaitForExecutionHandler waitForExecHandler = null; public object waitForExecHandlerParameter = null; public ControlSelectStateChangedHandler selectStateChangedHandler = null; public object selectStateChangedHandlerParameter = null; public ControlHandlerInfo(DataObject controlObject) { this.controlObject = controlObject; this.handle = GCHandle.Alloc(this); } ~ControlHandlerInfo() { this.handle.Free(); } } private Dictionary controlHandlers = new Dictionary (); int InternalControlHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test) { GCHandle handle = GCHandle.FromIntPtr (parameter); ControlHandlerInfo info = (ControlHandlerInfo)handle.Target; ControlAction controlAction = new ControlAction (action, info, this); if (info != null && info.controlHandler != null) return (int)info.controlHandler (controlAction, info.controlHandlerParameter, new MmsValue (ctlVal), test); else return (int)ControlHandlerResult.FAILED; } int InternalCheckHandlerImpl(IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck) { GCHandle handle = GCHandle.FromIntPtr (parameter); ControlHandlerInfo info = (ControlHandlerInfo)handle.Target; if (info != null && info.checkHandler != null) { ControlAction controlAction = new ControlAction (action, info, this); return (int)info.checkHandler (controlAction, info.checkHandlerParameter, new MmsValue (ctlVal), test, interlockCheck); } else return (int)CheckHandlerResult.OBJECT_UNDEFINED; } int InternalControlWaitForExecutionHandlerImpl (IntPtr action, IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck) { GCHandle handle = GCHandle.FromIntPtr (parameter); ControlHandlerInfo info = (ControlHandlerInfo)handle.Target; if (info != null && info.waitForExecHandler != null) { ControlAction controlAction = new ControlAction (action, info, this); return (int)info.waitForExecHandler (controlAction, info.waitForExecHandlerParameter, new MmsValue (ctlVal), test, synchoCheck); } else return (int)ControlHandlerResult.FAILED; } void InternalSelectStateChangedHandlerImpl(IntPtr action, IntPtr parameter, bool isSelected, int reason) { GCHandle handle = GCHandle.FromIntPtr(parameter); ControlHandlerInfo info = (ControlHandlerInfo)handle.Target; if (info != null && info.selectStateChangedHandler != null) { ControlAction controlAction = new ControlAction(action, info, this); info.selectStateChangedHandler(controlAction, info.selectStateChangedHandlerParameter, isSelected, (SelectStateChangedReason)reason); } } private struct WriteAccessHandlerInfo { public WriteAccessHandler handler; public InternalWriteAccessHandler internalHandler; public object parameter; public DataAttribute dataAttribute; public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da, InternalWriteAccessHandler internalHandler) { handler = h; parameter = p; dataAttribute = da; this.internalHandler = internalHandler; } } int WriteAccessHandlerImpl (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter) { WriteAccessHandlerInfo info; if (writeAccessHandlers.TryGetValue(dataAttribute, out info) == true) { ClientConnection con = null; clientConnections.TryGetValue(connection, out con); MmsValue mmsValue = new MmsValue(value); return (int)info.handler(info.dataAttribute, mmsValue.Clone(), con, info.parameter); } else { return (int) MmsDataAccessError.OBJECT_ACCESS_DENIED; } } private Dictionary writeAccessHandlers = new Dictionary (); private void ConnectionIndicationHandlerImpl (IntPtr iedServer, IntPtr clientConnection, bool connected, IntPtr parameter) { if (connected == false) { ClientConnection con = null; clientConnections.TryGetValue (clientConnection, out con); if (con != null) { if (connectionHandler != null) connectionHandler (this, con, false, connectionHandlerParameter); clientConnections.Remove (clientConnection); } } else { ClientConnection con = new ClientConnection (clientConnection); clientConnections.Add (clientConnection, con); if (connectionHandler != null) connectionHandler (this, con, true, connectionHandlerParameter); } } internal Dictionary clientConnections = new Dictionary (); /* store IedModel instance to prevent garbage collector */ private IedModel iedModel = null; public IedServer(IedModel iedModel, IedServerConfig config = null) { this.iedModel = iedModel; IntPtr nativeConfig = IntPtr.Zero; if (config != null) nativeConfig = config.self; self = IedServer_createWithConfig (iedModel.self, IntPtr.Zero, nativeConfig); } public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { this.iedModel = iedModel; IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero; if (config != null) nativeConfig = config.self; if (tlsConfig != null) nativeTLSConfig = tlsConfig.GetNativeInstance (); self = IedServer_createWithConfig (iedModel.self, nativeTLSConfig, nativeConfig); } private InternalConnectionHandler internalConnectionHandler = null; /// /// Sets the local ip address for listening /// /// Local IP address. public void SetLocalIpAddress(string localIpAddress) { IedServer_setLocalIpAddress (self, localIpAddress); } /// /// Start MMS server /// /// Local IP address. /// TCP port to use public void Start(string localIpAddress, int tcpPort) { SetLocalIpAddress (localIpAddress); Start (tcpPort); } /// Start MMS server /// TCP port to use public void Start(int tcpPort) { if (internalConnectionHandler == null) internalConnectionHandler = new InternalConnectionHandler (ConnectionIndicationHandlerImpl); IedServer_setConnectionIndicationHandler (self, internalConnectionHandler, IntPtr.Zero); IedServer_start(self, tcpPort); } /// Start MMS server public void Start () { Start(-1); } /// /// Stop the MMS server. /// /// This function will stop the server. This will close the TCP server socket and all client sockets. public void Stop() { IedServer_stop(self); internalConnectionHandler = null; } /// /// Release all server resources (same as ) /// /// This function releases all MMS server resources. public void Destroy() { Dispose(); } /// /// Releases all resource used by the object. /// /// Call when you are done using the . The /// method leaves the in an unusable state. After /// calling , you must release all references to the so /// the garbage collector can reclaim the memory that the was occupying. public void Dispose() { lock (this) { if (self != IntPtr.Zero) { IedServer_destroy(self); self = IntPtr.Zero; internalConnectionHandler = null; this.iedModel = null; } } } ~IedServer() { Dispose(); } /// /// Set the identify for the MMS identify service /// /// the IED vendor name /// the IED model name /// the IED revision/version number public void SetServerIdentity(string vendor, string model, string revision) { IedServer_setServerIdentity(self, vendor, model, revision); } /// /// Check if server is running (accepting client connections) /// /// true, if running, false otherwise. public bool IsRunning() { return IedServer_isRunning(self); } /// /// Get number of open MMS connections /// /// the number of open and accepted MMS connections public int GetNumberOfOpenConnections() { return IedServer_getNumberOfOpenConnections(self); } private ControlHandlerInfo GetControlHandlerInfo(DataObject controlObject) { ControlHandlerInfo info; controlHandlers.TryGetValue (controlObject, out info); if (info == null) { info = new ControlHandlerInfo (controlObject); controlHandlers.Add (controlObject, info); } return info; } public void SetControlHandler (DataObject controlObject, ControlHandler handler, object parameter) { ControlHandlerInfo info = GetControlHandlerInfo (controlObject); info.controlHandler = handler; info.controlHandlerParameter = parameter; if (internalControlHandlerRef == null) internalControlHandlerRef = new InternalControlHandler (InternalControlHandlerImpl); IedServer_setControlHandler(self, controlObject.self, internalControlHandlerRef, GCHandle.ToIntPtr(info.handle)); } public void SetCheckHandler (DataObject controlObject, CheckHandler handler, object parameter) { ControlHandlerInfo info = GetControlHandlerInfo (controlObject); info.checkHandler = handler; info.checkHandlerParameter = parameter; if (internalControlPerformCheckHandlerRef == null) internalControlPerformCheckHandlerRef = new InternalControlPerformCheckHandler (InternalCheckHandlerImpl); IedServer_setPerformCheckHandler(self, controlObject.self, internalControlPerformCheckHandlerRef, GCHandle.ToIntPtr(info.handle)); } public void SetWaitForExecutionHandler (DataObject controlObject, ControlWaitForExecutionHandler handler, object parameter) { ControlHandlerInfo info = GetControlHandlerInfo (controlObject); info.waitForExecHandler = handler; info.waitForExecHandlerParameter = parameter; if (internalControlWaitForExecutionHandlerRef == null) internalControlWaitForExecutionHandlerRef = new InternalControlWaitForExecutionHandler (InternalControlWaitForExecutionHandlerImpl); IedServer_setWaitForExecutionHandler(self, controlObject.self, internalControlWaitForExecutionHandlerRef, GCHandle.ToIntPtr(info.handle)); } /// /// Set a callback handler for a controllable data object to track select state changes /// /// Control object. /// Handler. /// A user provided parameter that is passed to the callback handler. public void SetSelectStateChangedHandler(DataObject controlObject, ControlSelectStateChangedHandler handler, object parameter) { ControlHandlerInfo info = GetControlHandlerInfo(controlObject); info.selectStateChangedHandler = handler; info.selectStateChangedHandlerParameter = parameter; if (internalSelectedStateChangedHandlerRef == null) internalSelectedStateChangedHandlerRef = new InternalSelectStateChangedHandler(InternalSelectStateChangedHandlerImpl); IedServer_setSelectStateChangedHandler(self, controlObject.self, internalSelectedStateChangedHandlerRef, GCHandle.ToIntPtr(info.handle)); } /// /// Install a WriteAccessHandler for a data attribute. /// /// This instructs the server to monitor write attempts by MMS clients to specific /// data attributes.If a client tries to write to the monitored data attribute the /// handler is invoked.The handler can decide if the write access will be allowed /// or denied.If a WriteAccessHandler is set for a specific data attribute - the /// default write access policy will not be performed for that data attribute. /// /// NOTE: If the data attribute has sub data attributes, the WriteAccessHandler is not /// set for the sub data attributes and will not be called when the sub data attribute is /// written directly! /// /// the data attribute to monitor /// the callback function that is invoked if a client tries to write to the monitored data attribute. /// a user provided parameter that is passed to the WriteAccessHandler when called. public void HandleWriteAccess (DataAttribute dataAttr, WriteAccessHandler handler, object parameter) { InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr, internalHandler)); IedServer_handleWriteAccess (self, dataAttr.self, internalHandler, IntPtr.Zero); } private void AddHandlerInfoForDataAttributeRecursive(DataAttribute da, WriteAccessHandler handler, object parameter, InternalWriteAccessHandler internalHandler) { writeAccessHandlers.Add(da.self, new WriteAccessHandlerInfo(handler, parameter, da, internalHandler)); foreach (ModelNode child in da.GetChildren()) { if (child is DataAttribute) { AddHandlerInfoForDataAttributeRecursive(child as DataAttribute, handler, parameter, internalHandler); } } } /// /// Install a WriteAccessHandler for a data attribute and for all sub data attributes /// /// This instructs the server to monitor write attempts by MMS clients to specific /// data attributes.If a client tries to write to the monitored data attribute the /// handler is invoked.The handler can decide if the write access will be allowed /// or denied.If a WriteAccessHandler is set for a specific data attribute - the /// default write access policy will not be performed for that data attribute. /// /// NOTE: When the data attribute is a complex attribute then the handler will also be installed /// for all sub data attributes. When the data attribute is a basic data attribute then /// this function behaves like . /// /// the data attribute to monitor /// the callback function that is invoked if a client tries to write to the monitored data attribute. /// a user provided parameter that is passed to the WriteAccessHandler when called. public void HandleWriteAccessForComplexAttribute(DataAttribute dataAttr, WriteAccessHandler handler, object parameter) { InternalWriteAccessHandler internalHandler = new InternalWriteAccessHandler(WriteAccessHandlerImpl); AddHandlerInfoForDataAttributeRecursive(dataAttr, handler, parameter, internalHandler); IedServer_handleWriteAccessForComplexAttribute(self, dataAttr.self, internalHandler, IntPtr.Zero); } /// /// Set the defualt write access policy for a specific FC. The default policy is applied when no handler is installed for a data attribute /// /// The functional constraint (FC) /// The new default access policy public void SetWriteAccessPolicy(FunctionalConstraint fc, AccessPolicy policy) { IedServer_setWriteAccessPolicy (self, fc, policy); } /// /// Locks the data model for data update. /// /// This function should be called before the data model is updated. /// After updating the data model the function should be called. /// /// /// his method should never be called inside of a library callback function. In the context of /// a library callback the data model is always already locked! Calling this function inside of a /// library callback may lead to a deadlock condition. /// public void LockDataModel() { IedServer_lockDataModel(self); } /// /// Unlocks the data model and process pending client requests. /// /// /// /// This method should never be called inside of a library callback function. In the context of /// a library callback the data model is always already locked! /// public void UnlockDataModel() { IedServer_unlockDataModel(self); } public void UpdateAttributeValue(DataAttribute dataAttr, MmsValue value) { IedServer_updateAttributeValue (self, dataAttr.self, value.valueReference); } public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool value) { IedServer_updateBooleanAttributeValue(self, dataAttr.self, value); } public void UpdateFloatAttributeValue(DataAttribute dataAttr, float value) { IedServer_updateFloatAttributeValue(self, dataAttr.self, value); } public void UpdateInt32AttributeValue(DataAttribute dataAttr, int value) { IedServer_updateInt32AttributeValue(self, dataAttr.self, value); } public void UpdateInt64AttributeValue(DataAttribute dataAttr, Int64 value) { IedServer_updateInt64AttributeValue (self, dataAttr.self, value); } public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string value) { IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, value); } public void UpdateUTCTimeAttributeValue(DataAttribute dataAttr, DateTime timestamp) { DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); DateTime timestampUTC = timestamp.ToUniversalTime(); TimeSpan timeDiff = timestampUTC - epoch; ulong timeVal = Convert.ToUInt64(timeDiff.TotalMilliseconds); IedServer_updateUTCTimeAttributeValue(self, dataAttr.self, timeVal); } public void UpdateTimestampAttributeValue(DataAttribute dataAttr, Timestamp timestamp) { IedServer_updateTimestampAttributeValue (self, dataAttr.self, timestamp.timestampRef); } public void UpdateQuality(DataAttribute dataAttr, ushort value) { IedServer_updateQuality(self, dataAttr.self, value); } public MmsValue GetAttributeValue(DataAttribute dataAttr) { IntPtr mmsValuePtr = IedServer_getAttributeValue (self, dataAttr.self); if (mmsValuePtr != IntPtr.Zero) return new MmsValue (mmsValuePtr); else return null; } /// /// Get the MmsValue object related to a functional constrained data object (FCD) /// /// the data object to specify the FCD /// the FC to specify the FCD /// FCDO corresponding MmsValue object cached by the server public MmsValue GetFunctionalConstrainedData(DataObject dataObject, FunctionalConstraint fc) { IntPtr mmsValuePtr = IedServer_getFunctionalConstrainedData(self, dataObject.self, (int)fc); if (mmsValuePtr != IntPtr.Zero) return new MmsValue(mmsValuePtr); else return null; } /// /// Enable all GOOSE control blocks. /// /// This will set the GoEna attribute of all configured GOOSE control blocks /// to true. If this method is not called at the startup or reset of the server /// then configured GOOSE control blocks keep inactive until a MMS client enables /// them by writing to the GOOSE control block. public void EnableGoosePublishing() { IedServer_enableGoosePublishing(self); } /// /// Disable all GOOSE control blocks. /// /// This will set the GoEna attribute of all configured GOOSE control blocks /// to false. This will stop GOOSE transmission. public void DisableGoosePublishing() { IedServer_disableGoosePublishing(self); } /// /// Set the Ethernet interface to be used by GOOSE publishing /// /// /// This function can be used to set the GOOSE interface ID. If not used or set to null the /// default interface ID from stack_config.h is used.Note the interface ID is operating system /// specific! /// /// the ID of the ethernet interface to be used for GOOSE publishing public void SetGooseInterfaceId(string interfaceId) { IedServer_setGooseInterfaceId(self, interfaceId); } /// /// Set the Ethernet interface to be used by GOOSE publishing /// /// /// This function can be used to set the GOOSE interface ID for all GCBs (parameter ln = null) or for /// a specific GCB specified by the logical node instance and the GCB name. /// /// ln the logical node that contains the GCB or null to set the ethernet interface ID for all GCBs /// the name (not object reference!) of the GCB /// the ID of the ethernet interface to be used for GOOSE publishing public void SetGooseInterfaceId(LogicalNode ln, string gcbName, string interfaceId) { if (ln == null) IedServer_setGooseInterfaceIdEx(self, IntPtr.Zero, gcbName, interfaceId); else IedServer_setGooseInterfaceIdEx(self, ln.self, gcbName, interfaceId); } /// /// Enable/disable the use of VLAN tags in GOOSE messages /// /// /// This function can be used to enable/disable VLAN tagging for all GCBs (parameter ln = null) or for /// a specific GCB specified by the logical node instance and the GCB name. /// /// the logical node that contains the GCB or null to enable/disable VLAN tagging for all GCBs /// the name (not object reference!) of the GCB /// true to enable VLAN tagging, false otherwise public void UseGooseVlanTag(LogicalNode ln, string gcbName, bool useVlanTag) { if (ln == null) IedServer_useGooseVlanTag(self, IntPtr.Zero, gcbName, useVlanTag); else IedServer_useGooseVlanTag(self, ln.self, gcbName, useVlanTag); } private GoCBEventHandler goCbEventHandler = null; private object goCbEventHandlerParameter = null; private InternalGoCBEventHandler internalGoCBEventHandler = null; private void InternalGoCBEventHandlerImplementation(IntPtr goCB, int cbEvent, IntPtr parameter) { if (goCbEventHandler != null) { MmsGooseControlBlock mmsGoCb = new MmsGooseControlBlock(goCB, iedModel); goCbEventHandler.Invoke(mmsGoCb, cbEvent, goCbEventHandlerParameter); } } /// /// Set a callback handler for GoCB events (enabled/disabled) /// /// the callback handler /// user provided parameter that is passed to the callback handler public void SetGoCBHandler(GoCBEventHandler handler, object parameter) { goCbEventHandler = handler; goCbEventHandlerParameter = parameter; if (internalGoCBEventHandler == null) { internalGoCBEventHandler = new InternalGoCBEventHandler(InternalGoCBEventHandlerImplementation); IedServer_setGoCBHandler(self, internalGoCBEventHandler, IntPtr.Zero); } } [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr ReportControlBlock_getParent(IntPtr self); private RCBEventHandler rcbEventHandler = null; private object rcbEventHandlerParameter = null; private InternalRCBEventHandler internalRCBEventHandler = null; private void InternalRCBEventHandlerImplementation(IntPtr parameter, IntPtr rcb, IntPtr connection, int eventType, string parameterName, int serviceError) { if (rcbEventHandler != null) { ClientConnection con = null; if (connection != IntPtr.Zero) { this.clientConnections.TryGetValue(connection, out con); } ReportControlBlock reportControlBlock = null; if (rcb != IntPtr.Zero) { IntPtr lnPtr = ReportControlBlock_getParent(rcb); if (lnPtr != IntPtr.Zero) { ModelNode lnModelNode = iedModel.GetModelNodeFromNodeRef(lnPtr); if (lnModelNode != null) { LogicalNode ln = lnModelNode as LogicalNode; if (ln.rcbs.TryGetValue(rcb, out reportControlBlock) == false) { reportControlBlock = new ReportControlBlock(rcb, ln); } } } } rcbEventHandler.Invoke(rcbEventHandlerParameter, reportControlBlock, con, (RCBEventType)eventType, parameterName, (MmsDataAccessError)serviceError); } } /// /// Set a callback handler for RCB events /// /// the callback handler /// user provided parameter that is passed to the callback handler public void SetRCBEventHandler(RCBEventHandler handler, object parameter) { rcbEventHandler = handler; rcbEventHandlerParameter = parameter; if (internalRCBEventHandler == null) { internalRCBEventHandler = new InternalRCBEventHandler(InternalRCBEventHandlerImplementation); IedServer_setRCBEventHandler(self, internalRCBEventHandler, IntPtr.Zero); } } } } }