From b4a5292b6775d0330fb51aca33c045019e13b9d3 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 12 Sep 2016 20:04:33 +0200 Subject: [PATCH] - C# API: added servers side control handling --- demos/beaglebone/beaglebone_leds.h | 2 +- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 955 ++++++++++++++++++ dotnet/tests/Test.cs | 40 + dotnet/tests/model.cfg | 237 +++++ make/target_system.mk | 6 +- 5 files changed, 1236 insertions(+), 4 deletions(-) create mode 100644 dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs create mode 100644 dotnet/tests/model.cfg 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)