diff --git a/demos/beaglebone/beaglebone_leds.h b/demos/beaglebone/beaglebone_leds.h
index 090932a..11e0ead 100644
--- a/demos/beaglebone/beaglebone_leds.h
+++ b/demos/beaglebone/beaglebone_leds.h
@@ -7,7 +7,7 @@
/* set to 1 if you want to run the demo on a PC */
-#define SIMULATED 0
+//#define SIMULATED
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
new file mode 100644
index 0000000..72ee6de
--- /dev/null
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -0,0 +1,955 @@
+/*
+ * IEC61850ServerAPI.cs
+ *
+ * Copyright 2016 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.Text;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using System.Collections;
+
+using IEC61850.Common;
+
+///
+/// IEC 61850 API for the libiec61850 .NET wrapper library
+///
+namespace IEC61850
+{
+ ///
+ /// IEC 61850 server API.
+ ///
+ namespace Server
+ {
+
+ public class ConfigFileParser
+ {
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr FileSystem_openFile(string filePath, bool readWrite);
+
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ConfigFileParser_createModelFromConfigFile(IntPtr fileHandle);
+
+ public static IedModel CreateModelFromConfigFile(string filePath)
+ {
+ IntPtr fileHandle = FileSystem_openFile (filePath, false);
+
+ if (fileHandle != IntPtr.Zero) {
+
+ IntPtr retVal = ConfigFileParser_createModelFromConfigFile (fileHandle);
+ if (retVal == IntPtr.Zero) {
+ return null;
+ }
+
+ return new IedModel (retVal);
+
+ } else
+ return null;
+ //TODO else throw exception
+ }
+ }
+
+ public class IedModel
+ {
+ [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);
+
+ internal IntPtr self = IntPtr.Zero;
+
+ internal IedModel(IntPtr self)
+ {
+ this.self = self;
+ }
+
+ public IedModel(string name)
+ {
+ self = IedModel_create(name);
+ }
+
+ // causes undefined behavior
+ //~IedModel()
+ //{
+ // if (self != IntPtr.Zero)
+ // {
+ // IedModel_destroy(self);
+ // }
+ //}
+
+ public void Destroy()
+ {
+ IedModel_destroy(self);
+ self = IntPtr.Zero;
+ }
+
+ public static IedModel CreateFromFile(string filePath)
+ {
+ return ConfigFileParser.CreateModelFromConfigFile(filePath);
+ }
+
+ private ModelNode getModelNodeFromNodeRef(IntPtr nodeRef)
+ {
+ int nodeType = ModelNode_getType (nodeRef);
+
+ switch (nodeType) {
+ case 0:
+ return new LogicalDevice (nodeRef);
+
+ case 1:
+ return new LogicalNode (nodeRef);
+
+ case 2:
+ return new DataObject (nodeRef);
+
+ case 3:
+ return new DataAttribute (nodeRef);
+
+ default:
+ return new ModelNode (nodeRef);
+ }
+ }
+
+ public ModelNode GetModelNodeByObjectReference(string objectReference)
+ {
+ IntPtr nodeRef = IedModel_getModelNodeByObjectReference(self, objectReference);
+
+ if (nodeRef == IntPtr.Zero)
+ return null;
+
+ return getModelNodeFromNodeRef (nodeRef);
+ }
+
+ 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);
+
+ public LogicalDevice (IntPtr self) : base (self)
+ {
+ }
+
+ public LogicalDevice(string name, 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) : base(self)
+ {
+ }
+
+ public LogicalNode(string name, LogicalDevice parent)
+ {
+ base.self = LogicalNode_create(name, parent.self);
+ }
+ }
+
+ public enum AccessPolicy {
+ ACCESS_POLICY_ALLOW = 0,
+ ACCESS_POLICY_DENY = 1
+ }
+
+ public enum MmsDataAccessError {
+ DATA_ACCESS_ERROR_NO_RESPONSE = -2, /* for server internal purposes only! */
+ DATA_ACCESS_ERROR_SUCCESS = -1,
+ DATA_ACCESS_ERROR_OBJECT_INVALIDATED = 0,
+ DATA_ACCESS_ERROR_HARDWARE_FAULT = 1,
+ DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE = 2,
+ DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED = 3,
+ DATA_ACCESS_ERROR_OBJECT_UNDEFINED = 4,
+ DATA_ACCESS_ERROR_INVALID_ADDRESS = 5,
+ DATA_ACCESS_ERROR_TYPE_UNSUPPORTED = 6,
+ DATA_ACCESS_ERROR_TYPE_INCONSISTENT = 7,
+ DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 8,
+ DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED = 9,
+ DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT = 10,
+ DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID = 11,
+ DATA_ACCESS_ERROR_UNKNOWN = 12
+ }
+
+ 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
+ }
+
+ public enum ModeValues
+ {
+ ON = 1,
+ BLOCKED = 2,
+ TEST = 3,
+ TEST_BLOCKED = 4,
+ OFF = 5
+ }
+
+ public enum HealthValues
+ {
+ OK = 1,
+ WARNING = 2,
+ ALARM = 3
+ }
+
+ 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_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, 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);
+
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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) : base(self)
+ {
+ }
+
+ public DataObject(string name, ModelNode parent) : this(name, parent, 0)
+ {
+ }
+
+ public DataObject(string name, ModelNode parent, int arrayElements)
+ {
+ 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);
+
+ internal DataAttribute(IntPtr self) : base(self)
+ {
+ }
+
+ public DataAttribute (string name, ModelNode parent, DataAttributeType type, FunctionalConstraint fc, TriggerOptions trgOps,
+ int arrayElements, UInt32 sAddr)
+ {
+ self = DataAttribute_create (name, parent.self, (int)type, (int)fc, (byte)trgOps, arrayElements, sAddr);
+ }
+
+ }
+
+ 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);
+
+ public IntPtr self;
+
+ internal ModelNode()
+ {
+ }
+
+ public ModelNode(IntPtr self)
+ {
+ this.self = self;
+ }
+
+ public ModelNode GetChild(string name)
+ {
+ IntPtr childPtr = ModelNode_getChild(self, name);
+
+ if (childPtr == IntPtr.Zero)
+ return null;
+
+ int nodeType = ModelNode_getType (childPtr);
+
+ switch (nodeType) {
+ case 0:
+ return new LogicalDevice (childPtr);
+
+ case 1:
+ return new LogicalNode (childPtr);
+
+ case 2:
+ return new DataObject (childPtr);
+
+ case 3:
+ return new DataAttribute (childPtr);
+
+ default:
+ return new ModelNode (childPtr);
+ }
+
+ }
+ }
+
+
+
+ public class DataSet
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr DataSet_create(string name, IntPtr parent);
+
+ public IntPtr self = IntPtr.Zero;
+
+ public DataSet(string name, LogicalNode parent)
+ {
+ self = DataSet_create(name, parent.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);
+ }
+ }
+
+ public class ReportControlBlock
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ReportControlBlock_create(string name, IntPtr parent, string rptId, bool isBuffered,
+ string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd);
+
+ public IntPtr self = IntPtr.Zero;
+
+ public ReportControlBlock(string name, LogicalNode parent, string rptId, bool isBuffered,
+ string dataSetName, uint confRef, byte trgOps, byte options, uint bufTm, uint intgPd)
+ {
+ self = ReportControlBlock_create(name, parent.self, rptId, isBuffered, dataSetName, confRef, trgOps, options, bufTm, intgPd);
+ }
+ }
+
+ public class ClientConnection
+ {
+
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr ClientConnection_getPeerAddress(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 delegate MmsDataAccessError WriteAccessHandler (DataAttribute dataAttr, MmsValue value,
+ ClientConnection connection, object parameter);
+
+ 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 (DataObject controlObject, object parameter, MmsValue ctlVal, bool test, bool synchroCheck);
+
+ public delegate ControlHandlerResult ControlHandler (DataObject controlObject, 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 (DataObject controlObject, object parameter, MmsValue ctlVal, bool test, bool interlockCheck,
+ ClientConnection connection);
+
+ ///
+ /// 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
+ {
+ [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
+ static extern IntPtr IedServer_create(IntPtr modelRef);
+
+ [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)]
+ static extern bool IedServer_isRunning(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_updateBooleanAttributeValue(IntPtr self, IntPtr dataAttribute, 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_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_updateQuality(IntPtr self, IntPtr dataAttribute, ushort value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr IedServer_getAttributeValue(IntPtr self, IntPtr dataAttribute);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlPerformCheckHandler (IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck, IntPtr connection);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlWaitForExecutionHandler (IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate int InternalControlHandler (IntPtr parameter, IntPtr ctlVal, bool test);
+
+ [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_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);
+
+ 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, bool connected, IntPtr parameter);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setConnectionIndicationHandler(IntPtr self, InternalConnectionHandler handler, IntPtr parameter);
+
+ private IntPtr self = IntPtr.Zero;
+
+
+
+ private 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 ControlHandlerInfo(DataObject controlObject)
+ {
+ this.controlObject = controlObject;
+ this.handle = GCHandle.Alloc(this);
+ }
+ ~ControlHandlerInfo() {
+ this.handle.Free();
+ }
+
+ }
+
+ private Dictionary controlHandlers = new Dictionary ();
+
+ int internalControlHandler (IntPtr parameter, IntPtr ctlVal, bool test)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
+
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+
+ if (info != null & info.controlHandler != null)
+ return (int)info.controlHandler (info.controlObject, info.controlHandlerParameter, new MmsValue (ctlVal), test);
+ else
+ return (int)ControlHandlerResult.FAILED;
+ }
+
+ int internalCheckHandler(IntPtr parameter, IntPtr ctlVal, bool test, bool interlockCheck, IntPtr connection)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
+
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+
+ if (info != null & info.checkHandler != null) {
+ ClientConnection con = null;
+
+ clientConnections.TryGetValue (connection, out con);
+
+ return (int)info.checkHandler (info.controlObject, info.checkHandlerParameter, new MmsValue (ctlVal), test, interlockCheck, con);
+ } else
+ return (int)CheckHandlerResult.OBJECT_UNDEFINED;
+ }
+
+ int internalControlWaitForExecutionHandler (IntPtr parameter, IntPtr ctlVal, bool test, bool synchoCheck)
+ {
+ GCHandle handle = GCHandle.FromIntPtr (parameter);
+
+ ControlHandlerInfo info = (ControlHandlerInfo)handle.Target;
+
+ if (info != null & info.waitForExecHandler != null) {
+ return (int)info.waitForExecHandler (info.controlObject, info.waitForExecHandlerParameter, new MmsValue (ctlVal), test, synchoCheck);
+ }
+ else
+ return (int)ControlHandlerResult.FAILED;
+ }
+
+ private struct WriteAccessHandlerInfo {
+ public WriteAccessHandler handler;
+ public object parameter;
+ public DataAttribute dataAttribute;
+
+ public WriteAccessHandlerInfo (WriteAccessHandler h, object p, DataAttribute da)
+ {
+ handler = h;
+ parameter = p;
+ dataAttribute = da;
+ }
+ }
+
+ int writeAccessHandler (IntPtr dataAttribute, IntPtr value, IntPtr connection, IntPtr parameter)
+ {
+ //object info = writeAccessHandlers.Item [dataAttribute];
+ WriteAccessHandlerInfo info;
+
+ writeAccessHandlers.TryGetValue (dataAttribute, out info);
+
+ ClientConnection con = null;
+
+ clientConnections.TryGetValue (connection, out con);
+
+ return (int) info.handler (info.dataAttribute, new MmsValue (value), con, info.parameter);
+ }
+
+ private Dictionary writeAccessHandlers = new Dictionary ();
+
+ private void connectionIndicationHandler (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);
+ }
+ }
+
+ private Dictionary clientConnections = new Dictionary ();
+
+ public IedServer(IedModel iedModel)
+ {
+ self = IedServer_create(iedModel.self);
+ }
+
+ // causes undefined behavior
+ //~IedServer()
+ //{
+ // if (self != IntPtr.Zero)
+ // {
+ // IedServer_destroy(self);
+ // }
+ //}
+
+
+ /// Start MMS server
+ public void Start(int tcpPort)
+ {
+ IedServer_setConnectionIndicationHandler (self, connectionIndicationHandler, IntPtr.Zero);
+
+ IedServer_start(self, tcpPort);
+ }
+
+ /// Start MMS server
+ public void Start ()
+ {
+ Start(102);
+ }
+
+ ///
+ /// 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);
+ }
+
+ ///
+ /// Release all server resources.
+ ///
+ /// This function releases all MMS server resources.
+ public void Destroy()
+ {
+ IedServer_destroy(self);
+ self = IntPtr.Zero;
+ }
+
+ public bool IsRunning()
+ {
+ return IedServer_isRunning(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;
+
+ IedServer_setControlHandler(self, controlObject.self, internalControlHandler, GCHandle.ToIntPtr(info.handle));
+ }
+
+ public void SetCheckHandler (DataObject controlObject, CheckHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
+
+ info.checkHandler = handler;
+ info.checkHandlerParameter = parameter;
+
+ IedServer_setPerformCheckHandler(self, controlObject.self, internalCheckHandler, GCHandle.ToIntPtr(info.handle));
+ }
+
+ public void SetWaitForExecutionHandler (DataObject controlObject, ControlWaitForExecutionHandler handler, object parameter)
+ {
+ ControlHandlerInfo info = GetControlHandlerInfo (controlObject);
+
+ info.waitForExecHandler = handler;
+ info.waitForExecHandlerParameter = parameter;
+
+ IedServer_setWaitForExecutionHandler(self, controlObject.self, internalControlWaitForExecutionHandler, GCHandle.ToIntPtr(info.handle));
+ }
+
+ public void HandleWriteAccess (DataAttribute dataAttr, WriteAccessHandler handler, object parameter)
+ {
+ writeAccessHandlers.Add (dataAttr.self, new WriteAccessHandlerInfo(handler, parameter, dataAttr));
+ //writeAccessHandlers.Item [dataAttr.self] = handler;
+
+ IedServer_handleWriteAccess (self, dataAttr.self, writeAccessHandler, IntPtr.Zero);
+ }
+
+ public void SetWriteAccessPolicy(FunctionalConstraint fc, AccessPolicy policy)
+ {
+ IedServer_setWriteAccessPolicy (self, fc, policy);
+ }
+
+
+ public void LockDataModel()
+ {
+ IedServer_lockDataModel(self);
+ }
+
+ public void UnlockDataModel()
+ {
+ IedServer_unlockDataModel(self);
+ }
+
+ public void UpdateBooleanAttributeValue(DataAttribute dataAttr, bool val)
+ {
+ IedServer_updateBooleanAttributeValue(self, dataAttr.self, val);
+ }
+
+ public void UpdateFloatAttributeValue(DataAttribute dataAttr, float val)
+ {
+ IedServer_updateFloatAttributeValue(self, dataAttr.self, val);
+ }
+
+ public void UpdateInt32AttributeValue(DataAttribute dataAttr, int val)
+ {
+ IedServer_updateInt32AttributeValue(self, dataAttr.self, val);
+ }
+
+ public void UpdateVisibleStringAttributeValue(DataAttribute dataAttr, string val)
+ {
+ IedServer_updateVisibleStringAttributeValue(self, dataAttr.self, val);
+ }
+
+ 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 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;
+ }
+ }
+
+ }
+}
diff --git a/dotnet/tests/Test.cs b/dotnet/tests/Test.cs
index 887ccb8..acb7efa 100644
--- a/dotnet/tests/Test.cs
+++ b/dotnet/tests/Test.cs
@@ -320,6 +320,46 @@ namespace tests
iedServer.Destroy ();
}
+ [Test()]
+ public void ControlHandler()
+ {
+ IedModel iedModel = ConfigFileParser.CreateModelFromConfigFile ("../../model.cfg");
+
+ DataObject spcso1 = (DataObject)iedModel.GetModelNodeByShortObjectReference ("GenericIO/GGIO1.SPCSO1");
+
+ Assert.IsNotNull (spcso1);
+
+ int handlerCalled = 0;
+
+ IedServer iedServer = new IedServer (iedModel);
+
+ iedServer.SetControlHandler (spcso1, delegate(DataObject controlObject, object parameter, MmsValue ctlVal, bool test) {
+ handlerCalled++;
+ return ControlHandlerResult.OK;
+ }, null);
+
+ iedServer.Start (10002);
+
+ IedConnection connection = new IedConnection ();
+
+ connection.Connect ("localhost", 10002);
+
+ ControlObject controlClient = connection.CreateControlObject ("simpleIOGenericIO/GGIO1.SPCSO1");
+
+ Assert.IsNotNull (controlClient);
+
+ controlClient.Operate (true);
+
+ connection.Abort ();
+
+ Assert.AreEqual (1, handlerCalled);
+
+ iedServer.Stop ();
+
+ iedServer.Destroy ();
+ }
+
+
[Test()]
public void ConnectionHandler()
{
diff --git a/dotnet/tests/model.cfg b/dotnet/tests/model.cfg
new file mode 100644
index 0000000..6f33242
--- /dev/null
+++ b/dotnet/tests/model.cfg
@@ -0,0 +1,237 @@
+MODEL(simpleIO){
+LD(GenericIO){
+LN(LLN0){
+DO(Mod 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+DA(ctlModel 0 12 4 0 0)=0;
+}
+DO(Beh 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Health 0){
+DA(stVal 0 3 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(NamPlt 0){
+DA(vendor 0 20 5 0 0);
+DA(swRev 0 20 5 0 0);
+DA(d 0 20 5 0 0);
+DA(configRev 0 20 5 0 0);
+DA(ldNs 0 20 11 0 0);
+}
+DS(Events){
+DE(GGIO1$ST$SPCSO1$stVal);
+DE(GGIO1$ST$SPCSO2$stVal);
+DE(GGIO1$ST$SPCSO3$stVal);
+DE(GGIO1$ST$SPCSO4$stVal);
+}
+DS(AnalogValues){
+DE(GGIO1$MX$AnIn1);
+DE(GGIO1$MX$AnIn2);
+DE(GGIO1$MX$AnIn3);
+DE(GGIO1$MX$AnIn4);
+}
+RC(EventsRCB01 Events 0 Events 1 24 111 50 1000);
+RC(AnalogValuesRCB01 AnalogValues 0 AnalogValues 1 24 111 50 1000);
+LC(EventLog Events GenericIO/LLN0$EventLog 19 0 0 1);
+LC(GeneralLog - - 19 0 0 1);
+LOG(GeneralLog);
+LOG(EventLog);
+GC(gcbEvents events Events 2 0 -1 -1 ){
+PA(4 273 4096 010ccd010001);
+}
+GC(gcbAnalogValues analog AnalogValues 2 0 -1 -1 ){
+PA(4 273 4096 010ccd010001);
+}
+}
+LN(LPHD1){
+DO(PhyNam 0){
+DA(vendor 0 20 5 0 0);
+}
+DO(PhyHealth 0){
+DA(stVal 0 3 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Proxy 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+}
+LN(GGIO1){
+DO(Mod 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+DA(ctlModel 0 12 4 0 0)=0;
+}
+DO(Beh 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Health 0){
+DA(stVal 0 3 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(NamPlt 0){
+DA(vendor 0 20 5 0 0);
+DA(swRev 0 20 5 0 0);
+DA(d 0 20 5 0 0);
+}
+DO(AnIn1 0){
+DA(mag 0 27 1 1 0){
+DA(f 0 10 1 1 0);
+}
+DA(q 0 23 1 2 0);
+DA(t 0 22 1 0 0);
+}
+DO(AnIn2 0){
+DA(mag 0 27 1 1 101){
+DA(f 0 10 1 1 0);
+}
+DA(q 0 23 1 2 0);
+DA(t 0 22 1 0 102);
+}
+DO(AnIn3 0){
+DA(mag 0 27 1 1 0){
+DA(f 0 10 1 1 0);
+}
+DA(q 0 23 1 2 0);
+DA(t 0 22 1 0 0);
+}
+DO(AnIn4 0){
+DA(mag 0 27 1 1 0){
+DA(f 0 10 1 1 0);
+}
+DA(q 0 23 1 2 0);
+DA(t 0 22 1 0 0);
+}
+DO(SPCSO1 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(Oper 0 27 12 0 0){
+DA(ctlVal 0 0 12 0 0);
+DA(origin 0 27 12 0 0){
+DA(orCat 0 12 12 0 0);
+DA(orIdent 0 13 12 0 0);
+}
+DA(ctlNum 0 6 12 0 0);
+DA(T 0 22 12 0 0);
+DA(Test 0 0 12 0 0);
+DA(Check 0 24 12 0 0);
+}
+DA(ctlModel 0 12 4 0 0)=1;
+DA(t 0 22 0 0 0);
+}
+DO(SPCSO2 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(Oper 0 27 12 0 0){
+DA(ctlVal 0 0 12 0 0);
+DA(origin 0 27 12 0 0){
+DA(orCat 0 12 12 0 0);
+DA(orIdent 0 13 12 0 0);
+}
+DA(ctlNum 0 6 12 0 0);
+DA(T 0 22 12 0 0);
+DA(Test 0 0 12 0 0);
+DA(Check 0 24 12 0 0);
+}
+DA(ctlModel 0 12 4 0 0)=1;
+DA(t 0 22 0 0 0);
+}
+DO(SPCSO3 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(Oper 0 27 12 0 0){
+DA(ctlVal 0 0 12 0 0);
+DA(origin 0 27 12 0 0){
+DA(orCat 0 12 12 0 0);
+DA(orIdent 0 13 12 0 0);
+}
+DA(ctlNum 0 6 12 0 0);
+DA(T 0 22 12 0 0);
+DA(Test 0 0 12 0 0);
+DA(Check 0 24 12 0 0);
+}
+DA(ctlModel 0 12 4 0 0)=1;
+DA(t 0 22 0 0 0);
+}
+DO(SPCSO4 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(Oper 0 27 12 0 0){
+DA(ctlVal 0 0 12 0 0);
+DA(origin 0 27 12 0 0){
+DA(orCat 0 12 12 0 0);
+DA(orIdent 0 13 12 0 0);
+}
+DA(ctlNum 0 6 12 0 0);
+DA(T 0 22 12 0 0);
+DA(Test 0 0 12 0 0);
+DA(Check 0 24 12 0 0);
+}
+DA(ctlModel 0 12 4 0 0)=1;
+DA(t 0 22 0 0 0);
+}
+DO(Ind1 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Ind2 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Ind3 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Ind4 0){
+DA(stVal 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+}
+LN(PDUP1){
+DO(Beh 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Mod 0){
+DA(stVal 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+DA(ctlModel 0 12 4 0 0)=0;
+}
+DO(Str 0){
+DA(general 0 0 0 1 0);
+DA(dirGeneral 0 12 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(Op 0){
+DA(general 0 0 0 1 0);
+DA(q 0 23 0 2 0);
+DA(t 0 22 0 0 0);
+}
+DO(OpDlTmms 0){
+DA(setVal 0 3 2 1 0);
+}
+DO(RsDlTmms 0){
+DA(setVal 0 3 2 1 0);
+}
+}
+}
+}
diff --git a/make/target_system.mk b/make/target_system.mk
index 9158d12..b27b8fd 100644
--- a/make/target_system.mk
+++ b/make/target_system.mk
@@ -1,10 +1,10 @@
UNAME := $(shell uname)
MIPSEL_TOOLCHAIN_PREFIX=mipsel-openwrt-linux-
-# ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
+ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabihf-
#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
#ARM_TOOLCHAIN_PREFIX=arm-poky-linux-gnueabi-
-ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
+#ARM_TOOLCHAIN_PREFIX=arm-linux-gnueabi-
UCLINUX_ARM_TOOLCHAIN_PREFIX=arm-uclinux-elf-
MINGW_TOOLCHAIN_PREFIX=i586-mingw32msvc-
#MINGW_TOOLCHAIN_PREFIX=x86_64-w64-mingw32-
@@ -57,7 +57,7 @@ endif
ifeq ($(TARGET), LINUX-ARM)
TOOLCHAIN_PREFIX=$(ARM_TOOLCHAIN_PREFIX)
CFLAGS += -mno-unaligned-access
-CFLAGS += -mcpu=arm926ej-s
+#CFLAGS += -mcpu=arm926ej-s
endif
ifeq ($(TARGET), UCLINUX-WAGO)