- C# API: added servers side control handling

This commit is contained in:
Michael Zillgith 2016-09-12 20:04:33 +02:00
parent d81e9e1c19
commit b4a5292b67
5 changed files with 1236 additions and 4 deletions

View file

@ -7,7 +7,7 @@
/* set to 1 if you want to run the demo on a PC */
#define SIMULATED 0
//#define SIMULATED

View file

@ -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 <http://www.gnu.org/licenses/>.
*
* 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;
/// <summary>
/// IEC 61850 API for the libiec61850 .NET wrapper library
/// </summary>
namespace IEC61850
{
/// <summary>
/// IEC 61850 server API.
/// </summary>
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 {
/// <summary>
/// check or operation failed
/// </summary>
FAILED = 0,
/// <summary>
/// check or operation was successful
/// </summary>
OK = 1,
/// <summary>
/// check or operation is in progress
/// </summary>
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 {
/// <summary>
/// check passed
/// </summary>
ACCEPTED = -1,
/// <summary>
/// check failed due to hardware fault
/// </summary>
HARDWARE_FAULT = 1,
/// <summary>
/// control is already selected or operated
/// </summary>
TEMPORARILY_UNAVAILABLE = 2,
/// <summary>
/// check failed due to access control reason - access denied for this client or state
/// </summary>
OBJECT_ACCESS_DENIED = 3,
/// <summary>
/// object not visible in this security context ???
/// </summary>
OBJECT_UNDEFINED = 4
}
public delegate CheckHandlerResult CheckHandler (DataObject controlObject, object parameter, MmsValue ctlVal, bool test, bool interlockCheck,
ClientConnection connection);
/// <summary>
/// This class acts as the entry point for the IEC 61850 client API. It represents a single
/// (MMS) connection to a server.
/// </summary>
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<DataObject, ControlHandlerInfo> controlHandlers = new Dictionary<DataObject, ControlHandlerInfo> ();
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<IntPtr, WriteAccessHandlerInfo> writeAccessHandlers = new Dictionary<IntPtr, WriteAccessHandlerInfo> ();
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<IntPtr, ClientConnection> clientConnections = new Dictionary<IntPtr, ClientConnection> ();
public IedServer(IedModel iedModel)
{
self = IedServer_create(iedModel.self);
}
// causes undefined behavior
//~IedServer()
//{
// if (self != IntPtr.Zero)
// {
// IedServer_destroy(self);
// }
//}
/// <summary>Start MMS server</summary>
public void Start(int tcpPort)
{
IedServer_setConnectionIndicationHandler (self, connectionIndicationHandler, IntPtr.Zero);
IedServer_start(self, tcpPort);
}
/// <summary>Start MMS server</summary>
public void Start ()
{
Start(102);
}
/// <summary>
/// Stop the MMS server.
/// </summary>
/// <description>This function will stop the server. This will close the TCP server socket and all client sockets.</description>
public void Stop()
{
IedServer_stop(self);
}
/// <summary>
/// Release all server resources.
/// </summary>
/// <description>This function releases all MMS server resources.</description>
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;
}
}
}
}

View file

@ -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()
{

237
dotnet/tests/model.cfg Normal file
View file

@ -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);
}
}
}
}

View file

@ -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)