/*
* IEC61850ClientAPI.cs
*
* Copyright 2014-2023 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;
using IEC61850.TLS;
// IEC 61850 API for the libiec61850 .NET wrapper library
namespace IEC61850
{
// IEC 61850 client API.
namespace Client
{
[StructLayout(LayoutKind.Sequential)]
public class MmsServerIdentity
{
public string vendorName;
public string modelName;
public string revision;
}
///
/// Represents an MmsConnection object (a single connection to an MMS server)
///
public class MmsConnection
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr MmsConnection_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void MmsConnection_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr MmsConnection_identify(IntPtr self, out int mmsError);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void MmsServerIdentity_destroy(IntPtr self);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
private static extern void MmsConnection_setLocalDetail (IntPtr self, Int32 localDetail);
[DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
private static extern Int32 MmsConnection_getLocalDetail (IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern Int32 MmsConnection_setRequestTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr MmsConnection_readMultipleVariables(IntPtr self, out int mmsError,
string domainId, IntPtr items);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void MmsValue_delete(IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void LinkedListValueDeleteFunction(IntPtr pointer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void LinkedList_destroyDeep(IntPtr list, LinkedListValueDeleteFunction valueDeleteFunction);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr LinkedList_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void LinkedList_add(IntPtr self, IntPtr data);
private IntPtr self = IntPtr.Zero;
private bool selfDestroy = false;
public MmsConnection()
{
selfDestroy = true;
self = MmsConnection_create();
}
internal MmsConnection(IntPtr mmsConnection)
{
self = mmsConnection;
}
~MmsConnection ()
{
if (selfDestroy)
if (self != IntPtr.Zero)
MmsConnection_destroy(self);
}
private void FreeHGlobaleDeleteFunction(IntPtr pointer)
{
Marshal.FreeHGlobal(pointer);
}
///
/// Requests the server identity information
///
/// The server identity.
public MmsServerIdentity GetServerIdentity()
{
int mmsError;
if (self == IntPtr.Zero)
{
throw new IedConnectionException("Pointer is Zero!");
}
IntPtr identity = MmsConnection_identify(self, out mmsError);
if (mmsError != 0)
throw new IedConnectionException("Failed to read server identity", mmsError);
if (identity == IntPtr.Zero)
throw new IedConnectionException("Failed to read server identity");
MmsServerIdentity serverIdentity = (MmsServerIdentity)
Marshal.PtrToStructure(identity, typeof(MmsServerIdentity));
MmsServerIdentity_destroy(identity);
return serverIdentity;
}
///
/// Sets the local detail (maximum MMS PDU size)
///
/// maximum accepted MMS PDU size in bytes
public void SetLocalDetail(int localDetail)
{
MmsConnection_setLocalDetail(self, localDetail);
}
///
/// Gets the local detail (maximum MMS PDU size)
///
/// maximum accepted MMS PDU size in bytes
public int GetLocalDetail()
{
return MmsConnection_getLocalDetail(self);
}
///
/// Sets the request timeout
///
/// request timeout in milliseconds
public void SetRequestTimeout(uint timeoutMs)
{
MmsConnection_setRequestTimeout(self, timeoutMs);
}
///
/// Reads multipe MMS variables from the same domain
///
/// MmsValue of type MMS_ARRAY containing the access results for the requested variables.
/// the domain name (logical device)
/// list of variable names (in MMS notation e.g. "GGIO$ST$Ind1")
public MmsValue ReadMultipleVariables(string domainName, List variables)
{
IntPtr linkedList = LinkedList_create();
foreach (string variableName in variables)
{
IntPtr handle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(variableName);
LinkedList_add(linkedList, handle);
}
int error;
IntPtr mmsValue = MmsConnection_readMultipleVariables(self, out error, domainName, linkedList);
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
{
if (mmsValue != IntPtr.Zero)
MmsValue_delete(mmsValue);
throw new IedConnectionException("ReadMultipleVariables failed", error);
}
return new MmsValue(mmsValue, true);
}
}
///
/// Represents a variable in a log entry
///
public class MmsJournalVariable
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsJournalVariable_getTag(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsJournalVariable_getValue(IntPtr self);
private IntPtr self;
internal MmsJournalVariable(IntPtr self)
{
this.self = self;
}
public string GetTag()
{
return Marshal.PtrToStringAnsi(MmsJournalVariable_getTag(self));
}
public MmsValue GetValue()
{
MmsValue mmsValue = new MmsValue(MmsJournalVariable_getValue(self));
return mmsValue;
}
}
///
/// Represents an entry of a log
///
public class MmsJournalEntry
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsJournalEntry_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsJournalEntry_getEntryID(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsJournalEntry_getOccurenceTime(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsJournalEntry_getJournalVariables(IntPtr self);
/****************
* LinkedList
***************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_getNext(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_getData(IntPtr self);
private IntPtr self;
private List variables = null;
internal MmsJournalEntry(IntPtr self)
{
this.self = self;
}
///
/// Gets the journal variables.
///
/// The journal variables.
public List GetJournalVariables()
{
if (variables == null)
{
IntPtr linkedList = MmsJournalEntry_getJournalVariables(self);
IntPtr element = LinkedList_getNext(linkedList);
variables = new List();
while (element != IntPtr.Zero)
{
MmsJournalVariable journalVariable = new MmsJournalVariable(LinkedList_getData(element));
variables.Add(journalVariable);
element = LinkedList_getNext(element);
}
}
return variables;
}
///
/// Gets the entry identifier of the log entry
///
/// The entry identifier.
public byte[] GetEntryID()
{
IntPtr mmsValuePtr = MmsJournalEntry_getEntryID(self);
MmsValue mmsValue = new MmsValue(mmsValuePtr);
byte[] octetString = mmsValue.getOctetString();
return octetString;
}
///
/// Gets the occurence time of the log entry
///
/// The occurence time.
public ulong GetOccurenceTime()
{
IntPtr mmsValuePtr = MmsJournalEntry_getOccurenceTime(self);
MmsValue mmsValue = new MmsValue(mmsValuePtr);
return mmsValue.GetBinaryTimeAsUtcMs();
}
///
/// Releases all resource used by the object.
///
public void Dispose()
{
if (self != IntPtr.Zero)
{
MmsJournalEntry_destroy(self);
self = IntPtr.Zero;
}
}
~MmsJournalEntry ()
{
Dispose();
}
}
///
/// Asynchonous service handler for the get RCB values service
///
/// The invoke ID of the request triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
/// the report control block instance
public delegate void GetRCBValuesHandler(UInt32 invokeId,object parameter,IedClientError err,ReportControlBlock rcb);
///
/// Asynchonous service handler for the set RCB values service
///
/// The invoke ID of the request triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
/// the report control block instance
public delegate void SetRCBValuesHandler(UInt32 invokeId,object parameter,IedClientError err,ReportControlBlock rcb);
///
/// Generic asynchonous service handler - used by simple services that have only success or error result
///
/// The invoke ID of the request triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
public delegate void GenericServiceHandler(UInt32 invokeId,object parameter,IedClientError err);
///
/// This class acts as the entry point for the IEC 61850 client API. It represents a single
/// (MMS) connection to a server.
///
public partial class IedConnection : IDisposable
{
/*************
* MmsValue
*************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsValue_toString(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern float MmsValue_toFloat(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
static extern bool MmsValue_getBoolean(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 MmsValue_getBitStringAsInteger(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsValue_getType(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void MmsValue_delete(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int MmsValue_getDataAccessError(IntPtr self);
/****************
* IedConnection
***************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_createWithTlsSupport(IntPtr tlsConfig);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int Connection_getState(IedConnection self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setConnectTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setRequestTimeout(IntPtr self, UInt32 timeoutInMs);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getRequestTimeout(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setMaxOutstandingCalls(IntPtr self, int calling, int called);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abort(IntPtr self, out int error);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IedConnection_release(IntPtr self, out int error);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IedConnection_close(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_readObject(IntPtr self, out int error, string objectReference, int fc);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_writeObject(IntPtr self, out int error, string dataAttributeReference, int fc, IntPtr value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectory(IntPtr self, out int error, string dataReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectoryByFC(IntPtr self, out int error, string dataReference, FunctionalConstraint fc);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataDirectoryFC(IntPtr self, out int error, string dataReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getLogicalNodeDirectory(IntPtr self, out int error, string logicalNodeReference, int acsiClass);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getServerDirectory(IntPtr self, out int error, [MarshalAs(UnmanagedType.I1)] bool getFileNames);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_getDeviceModelFromServer(IntPtr self, out int error);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getLogicalDeviceDirectory(IntPtr self, out int error, string logicalDeviceName);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getVariableSpecification(IntPtr self, out int error, string objectReference, int fc);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalConnectionClosedHandler(IntPtr parameter,IntPtr Iedconnection);
///
/// Called when the connection is closed
///
public delegate void ConnectionClosedHandler(IedConnection connection);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installConnectionClosedHandler(IntPtr self, InternalConnectionClosedHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_readDataSetValues(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_writeDataSetValues(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr values, out IntPtr accessResults);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_createDataSet(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IedConnection_deleteDataSet(IntPtr self, out int error, string dataSetReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getDataSetDirectory(IntPtr self, out int error, string dataSetReference, [MarshalAs(UnmanagedType.I1)] out bool isDeletable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getMmsConnection(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr MmsConnection_getIsoConnectionParameters(IntPtr mmsConnection);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getFileDirectory(IntPtr self, out int error, string directoryName);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr
IedConnection_getFileDirectoryEx(IntPtr self, out int error, string directoryName, string continueAfter
, [MarshalAs(UnmanagedType.I1)] out bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_deleteFile(IntPtr self, out int error, string fileName);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_queryLogAfter(IntPtr self, out int error, string logReference,
IntPtr entryID, ulong timeStamp, [MarshalAs(UnmanagedType.I1)] out bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_queryLogByTime(IntPtr self, out int error, string logReference,
ulong startTime, ulong endTime, [MarshalAs(UnmanagedType.I1)] out bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr IedConnection_getRCBValues(IntPtr connection, out int error, string rcbReference, IntPtr updateRcb);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setRCBValues(IntPtr connection, out int error, IntPtr rcb, UInt32 parametersMask, [MarshalAs(UnmanagedType.I1)] bool singleRequest);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installReportHandler(IntPtr connection, string rcbReference, string rptId, InternalReportHandler handler,
IntPtr handlerParameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_uninstallReportHandler(IntPtr connection, string rcbReference);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern int IedConnection_getState(IntPtr connection);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void InternalStateChangedHandler(IntPtr parameter,IntPtr iedConnection,int newState);
///
/// Called when there is a change in the connection state
///
public delegate void StateChangedHandler(IedConnection connection, IedConnectionState newState);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_installStateChangedHandler(IntPtr connection, InternalStateChangedHandler handler, IntPtr parameter);
/*********************
* Async functions
*********************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connectAsync(IntPtr self, out int error, string hostname, int tcpPort);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abortAsync(IntPtr self, out int error);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern void IedConnection_releaseAsync(IntPtr self, out int error);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_ReadObjectHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr value);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_readObjectAsync(IntPtr self, out int error, string objRef, int fc,
IedConnection_ReadObjectHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_WriteObjectHandler(UInt32 invokeId,IntPtr parameter,int err);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_writeObjectAsync(IntPtr self, out int error, string objRef, int fc,
IntPtr value, IedConnection_WriteObjectHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetNameListHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr nameList,[MarshalAs(UnmanagedType.I1)] bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getServerDirectoryAsync(IntPtr self, out int error, string continueAfter, IntPtr result,
IedConnection_GetNameListHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getLogicalDeviceVariablesAsync(IntPtr self, out int error, string ldName, string continueAfter, IntPtr result,
IedConnection_GetNameListHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getLogicalDeviceDataSetsAsync(IntPtr self, out int error, string ldName, string continueAfter, IntPtr result,
IedConnection_GetNameListHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_QueryLogHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr journalEntries,[MarshalAs(UnmanagedType.I1)] bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_queryLogByTimeAsync(IntPtr self, out int error, string logReference,
UInt64 startTime, UInt64 endTime, IedConnection_QueryLogHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_queryLogAfterAsync(IntPtr self, out int error, string logReference,
IntPtr entryID, UInt64 timeStamp, IedConnection_QueryLogHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetVariableSpecificationHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr spec);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getVariableSpecificationAsync(IntPtr self, out int error, string dataAttributeReference,
int fc, IedConnection_GetVariableSpecificationHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_ReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSet);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_readDataSetValuesAsync(IntPtr self, out int error, string dataSetReference, IntPtr dataSet,
IedConnection_ReadDataSetHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_createDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet,
IedConnection_GenericServiceHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_deleteDataSetAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
IedConnection_GenericServiceHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, [MarshalAs(UnmanagedType.I1)] bool isDeletable);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getDataSetDirectoryAsync(IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GetRCBValuesHandler(UInt32 invokeId,IntPtr parameter,int err,IntPtr rcb);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getRCBValuesAsync(IntPtr self, out int error, string rcbReference, IntPtr updateRcb,
IedConnection_GetRCBValuesHandler handler, IntPtr parameter);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IedConnection_GenericServiceHandler(UInt32 invokeId,IntPtr parameter,int err);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_setRCBValuesAsync(IntPtr self, out int error, IntPtr rcb,
UInt32 parametersMask, [MarshalAs(UnmanagedType.I1)] bool singleRequest, IedConnection_GenericServiceHandler handler, IntPtr parameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_deleteFileAsync(IntPtr self, out int error, string fileName,
IedConnection_GenericServiceHandler handler, IntPtr parameter);
/********************
* FileDirectoryEntry
*********************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void FileDirectoryEntry_destroy(IntPtr self);
/****************
* LinkedList
***************/
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_getNext(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_getData(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroy(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroyStatic(IntPtr self);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void LinkedListValueDeleteFunction(IntPtr pointer);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_destroyDeep(IntPtr list, LinkedListValueDeleteFunction valueDeleteFunction);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr LinkedList_create();
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void LinkedList_add(IntPtr self, IntPtr data);
private IntPtr connection = IntPtr.Zero;
private InternalConnectionClosedHandler connectionClosedHandler = null;
private ConnectionClosedHandler userProvidedConnectionClosedHandler = null;
private InternalStateChangedHandler internalStateChangedHandler = null;
private StateChangedHandler stateChangedHandler = null;
///
/// Initializes a new instance of the class.
///
public IedConnection()
{
connection = IedConnection_create();
}
///
/// Initializes a new instance of the class.
///
/// TLS configuration to use
public IedConnection(TLSConfiguration tlsConfig)
{
connection = IedConnection_createWithTlsSupport(tlsConfig.GetNativeInstance());
}
///
/// Releases all resource used by the object.
///
/// Call when you are finished using the . The
/// method leaves the in an unusable state. After
/// calling , you must release all references to the
/// so the garbage collector can reclaim the memory that the was occupying.
public void Dispose()
{
if (connection != IntPtr.Zero)
{
IedConnection_destroy(connection);
connection = IntPtr.Zero;
}
}
~IedConnection ()
{
Dispose();
}
private IsoConnectionParameters isoConnectionParameters = null;
///
/// Gets the connection parameters
///
/// The connection parameters
public IsoConnectionParameters GetConnectionParameters()
{
if (isoConnectionParameters == null)
{
IntPtr mmsConnection = IedConnection_getMmsConnection(connection);
IntPtr parameters = MmsConnection_getIsoConnectionParameters(mmsConnection);
isoConnectionParameters = new IsoConnectionParameters(parameters);
}
return isoConnectionParameters;
}
private void FreeHGlobaleDeleteFunction(IntPtr pointer)
{
Marshal.FreeHGlobal(pointer);
}
private UInt32 connectTimeout = 10000;
///
/// Gets or sets the timeout used for connection attempts.
///
/// The connect timeout in milliseconds
public UInt32 ConnectTimeout
{
get
{
return connectTimeout;
}
set
{
connectTimeout = value;
}
}
///
/// Gets or sets the request timeout for this connection
///
/// The request timeout in milliseconds
public UInt32 RequestTimeout
{
get
{
return IedConnection_getRequestTimeout(connection);
}
set
{
IedConnection_setRequestTimeout(connection, value);
}
}
///
/// Set the maximum number outstanding calls allowed for this connection
///
/// the maximum outstanding calls allowed by the caller (client)
/// the maximum outstanding calls allowed by the called endpoint (server)
public void SetMaxOutstandingCalls(int calling, int called)
{
IedConnection_setMaxOutstandingCalls(connection, calling, called);
}
///
/// Gets or sets the maximum size if a PDU (has to be set before calling connect!).
///
/// The maximum allowed size of an MMS PDU.
public int MaxPduSize
{
get
{
return GetMmsConnection().GetLocalDetail();
}
set
{
GetMmsConnection().SetLocalDetail(value);
}
}
///
/// Set the time quality for all timestamps generated by this IedConnection instance
///
/// set or unset leap seconds known flag
/// set or unset clock failure flag
/// set or unset clock not synchronized flag
/// set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
{
IedConnection_setTimeQuality(connection, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision);
}
///
/// Gets the underlying MmsConnection instance.
///
/// The mms connection.
public MmsConnection GetMmsConnection()
{
IntPtr mmsConnectionPtr = IedConnection_getMmsConnection(connection);
return new MmsConnection(mmsConnectionPtr);
}
/// Establish an MMS connection to a server
/// This exception is thrown if there is a connection or service error
public void Connect(string hostname, int tcpPort)
{
int error;
IedConnection_setConnectTimeout(connection, connectTimeout);
IedConnection_connect(connection, out error, hostname, tcpPort);
if (error != 0)
throw new IedConnectionException("Connect to " + hostname + ":" + tcpPort + " failed", error);
}
public void ConnectAsync(string hostname, int tcpPort)
{
int error;
IedConnection_setConnectTimeout(connection, connectTimeout);
IedConnection_connectAsync(connection, out error, hostname, tcpPort);
if (error != 0)
throw new IedConnectionException("Connect to " + hostname + ":" + tcpPort + " failed", error);
}
///
/// Gets the current state of the connection
///
/// The current connection state
public IedConnectionState GetState()
{
return (IedConnectionState)IedConnection_getState(connection);
}
/// Establish an MMS connection to a server.
/// This exception is thrown if there is a connection or service error.
public void Connect(string hostname)
{
Connect(hostname, -1);
}
///
/// Set the local IP address and port to be used by the client
///
/// the local IP address or hostname
/// the local TCP port to use. When 0 the OS will chose the TCP port to use.
public void SetLocalAddress(string localIpAddress, int localPort)
{
IedConnection_setLocalAddress(connection, localIpAddress, localPort);
}
///
/// Set the local IP address to be used by the client
///
/// the local IP address or hostname
public void SetLocalAddress(string localIpAddress)
{
IedConnection_setLocalAddress(connection, localIpAddress, 0);
}
/// This exception is thrown if there is a connection or service error
public ControlObject CreateControlObject(string objectReference)
{
ControlObject controlObject = new ControlObject(objectReference, connection, this);
return controlObject;
}
///
/// Creates a new SampledValuesControlBlock instance.
///
/// >
/// This function will also read the SVCB values from the server.
///
/// The new SVCB instance
/// The object reference of the SVCB
public SampledValuesControlBlock GetSvControlBlock(string svcbObjectReference)
{
return new SampledValuesControlBlock(connection, svcbObjectReference);
}
///
/// Creates a new SampledValuesControlBlock instance.
///
/// The new GoCB instance
/// The object reference of the GoCB (e.g. "simpleIOGenericIO/LLN0.gcbAnalogValues")
public GooseControlBlock GetGooseControlBlock(string gocbObjectReference)
{
return new GooseControlBlock(gocbObjectReference, connection);
}
///
/// Updates the device model by quering the server.
///
public void UpdateDeviceModel()
{
int error;
IedConnection_getDeviceModelFromServer(connection, out error);
if (error != 0)
throw new IedConnectionException("UpdateDeviceModel failed", error);
}
///
/// Gets the server directory (Logical devices or file objects)
///
/// List of logical devices or files
/// If set to true the file directory is returned, otherwise the LD names
/// This exception is thrown if there is a connection or service error
public List GetServerDirectory(bool fileDirectory = false)
{
int error;
IntPtr linkedList = IedConnection_getServerDirectory(connection, out error, fileDirectory);
if (error != 0)
throw new IedConnectionException("GetDeviceDirectory failed", error);
List newList = new List();
if (fileDirectory == false)
{
IntPtr element = LinkedList_getNext(linkedList);
while (element != IntPtr.Zero)
{
string ld = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(ld);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
}
else
{
IntPtr element = LinkedList_getNext(linkedList);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
FileDirectoryEntry entry = new FileDirectoryEntry(elementData);
newList.Add(entry.GetFileName());
FileDirectoryEntry_destroy(elementData);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(linkedList);
}
return newList;
}
/// This exception is thrown if there is a connection or service error
public List GetLogicalDeviceDirectory(string logicalDeviceName)
{
int error;
IntPtr linkedList = IedConnection_getLogicalDeviceDirectory(connection, out error, logicalDeviceName);
if (error != 0)
throw new IedConnectionException("GetLogicalDeviceDirectory failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string ln = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(ln);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
/// Get the directory of a logical node (LN)
/// This function returns the directory contents of a LN. Depending on the provided ACSI class
/// The function returns either data object references, or references of other objects like data sets,
/// report control blocks, ...
/// The object reference of a DO, SDO, or DA.
/// the ACSI class of the requested directory elements.
/// This exception is thrown if there is a connection or service error
public List GetLogicalNodeDirectory(string logicalNodeName, ACSIClass acsiClass)
{
int error;
IntPtr linkedList = IedConnection_getLogicalNodeDirectory(connection, out error, logicalNodeName, (int)acsiClass);
if (error != 0)
throw new IedConnectionException("GetLogicalNodeDirectory failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
/// Get a list of attributes (with functional constraints) of a DO, SDO, or DA
/// The object reference of a DO, SDO, or DA.
/// This exception is thrown if there is a connection or service error
public List GetDataDirectory(string dataReference)
{
int error;
IntPtr linkedList = IedConnection_getDataDirectory(connection, out error, dataReference);
if (error != 0)
throw new IedConnectionException("GetDataDirectory failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
/// Get the list of attributes with the specified FC of a DO, SDO, or DA
/// The object reference of a DO, SDO, or DA.
/// Functional constraint
/// This exception is thrown if there is a connection or service error
public List GetDataDirectory(string dataReference, FunctionalConstraint fc)
{
int error;
IntPtr linkedList = IedConnection_getDataDirectoryByFC(connection, out error, dataReference, fc);
if (error != 0)
throw new IedConnectionException("GetDataDirectory failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
/// Get a list of attributes (with functional constraints) of a DO, SDO, or DA
/// This function is similar to the GetDataDirectory except that the returned element names
/// have the functional contraint (FC) appended.
/// The object reference of a DO, SDO, or DA.
/// This exception is thrown if there is a connection or service error
public List GetDataDirectoryFC(string dataReference)
{
int error;
IntPtr linkedList = IedConnection_getDataDirectoryFC(connection, out error, dataReference);
if (error != 0)
throw new IedConnectionException("GetDataDirectoryFC failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
private static List WrapNativeLogQueryResult(IntPtr linkedList)
{
if (linkedList == IntPtr.Zero)
return null;
List journalEntries = new List();
IntPtr element = LinkedList_getNext(linkedList);
while (element != IntPtr.Zero)
{
MmsJournalEntry journalEntry = new MmsJournalEntry(LinkedList_getData(element));
journalEntries.Add(journalEntry);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(linkedList);
return journalEntries;
}
///
/// Queries all log entries after the entry with the given entryID and timestamp
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// EntryID of the last received MmsJournalEntry
/// Timestamp of the last received MmsJournalEntry
/// Indicates that more log entries are available
/// This exception is thrown if there is a connection or service error
public List QueryLogAfter(string logRef, byte[] entryID, ulong timestamp, out bool moreFollows)
{
int error;
bool moreFollowsVal;
MmsValue entryIdValue = new MmsValue(entryID);
IntPtr linkedList = IedConnection_queryLogAfter(connection, out error, logRef, entryIdValue.valueReference, timestamp, out moreFollowsVal);
if (error != 0)
throw new IedConnectionException("QueryLogAfter failed", error);
moreFollows = moreFollowsVal;
return WrapNativeLogQueryResult(linkedList);
}
///
/// Queries all log entries of the given time range
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// Start time of the time range
/// End time of the time range
/// Indicates that more log entries are available
/// This exception is thrown if there is a connection or service error
public List QueryLogByTime(string logRef, ulong startTime, ulong stopTime, out bool moreFollows)
{
int error;
bool moreFollowsVal;
IntPtr linkedList = IedConnection_queryLogByTime(connection, out error, logRef, startTime, stopTime, out moreFollowsVal);
if (error != 0)
throw new IedConnectionException("QueryLogByTime failed", error);
moreFollows = moreFollowsVal;
return WrapNativeLogQueryResult(linkedList);
}
///
/// Queries all log entries of the given time range
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// Start time of the time range
/// End time of the time range
/// Indicates that more log entries are available
/// This exception is thrown if there is a connection or service error
public List QueryLogByTime(string logRef, DateTime startTime, DateTime stopTime, out bool moreFollows)
{
ulong startTimeMs = LibIEC61850.DateTimeToMsTimestamp(startTime);
ulong stopTimeMs = LibIEC61850.DateTimeToMsTimestamp(stopTime);
return QueryLogByTime(logRef, startTimeMs, stopTimeMs, out moreFollows);
}
/// Read the variable specification (type description of a DA or FCDO
/// The object reference of a DA or FCDO.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public MmsVariableSpecification GetVariableSpecification(string objectReference, FunctionalConstraint fc)
{
int error;
IntPtr varSpecPtr = IedConnection_getVariableSpecification(connection, out error, objectReference, (int)fc);
if (error != 0)
throw new IedConnectionException("GetVariableSpecification failed", error);
return new MmsVariableSpecification(varSpecPtr, true);
}
private IntPtr readObjectInternal(string objectReference, FunctionalConstraint fc)
{
int error;
IntPtr mmsValue = IedConnection_readObject(connection, out error, objectReference, (int)fc);
if (error != 0)
throw new IedConnectionException("Reading value failed", error);
if (mmsValue == IntPtr.Zero)
throw new IedConnectionException("Variable not found on server", error);
return mmsValue;
}
/// Read the value of a data attribute (DA) or functional constraint data object (FCDO).
/// The object reference of a DA or FCDO.
/// The functional constraint (FC) of the object
/// the received value as an MmsValue instance
/// This exception is thrown if there is a connection or service error
public MmsValue ReadValue(String objectReference, FunctionalConstraint fc)
{
var value = readObjectInternal(objectReference, fc);
return new MmsValue(value, true);
}
private IntPtr readObjectInternalAndCheckDataAccessError(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternal(objectReference, fc);
if (MmsValue_getType(mmsValue) == (int)MmsType.MMS_DATA_ACCESS_ERROR)
{
int dataAccessError = MmsValue_getDataAccessError(mmsValue);
MmsValue_delete(mmsValue);
throw new IedConnectionException("Data access error", dataAccessError);
}
return mmsValue;
}
/// Read the value of a basic data attribute (BDA) of type boolean.
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// the received boolean value
/// This exception is thrown if there is a connection or service error
public bool ReadBooleanValue(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternalAndCheckDataAccessError(objectReference, fc);
if (MmsValue_getType(mmsValue) != (int)MmsType.MMS_BOOLEAN)
{
MmsValue_delete(mmsValue);
throw new IedConnectionException("Result is not of type boolean (MMS_BOOLEAN)", 0);
}
bool value = MmsValue_getBoolean(mmsValue);
MmsValue_delete(mmsValue);
return value;
}
/// Read the value of a basic data attribute (BDA) of type float.
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public float ReadFloatValue(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternalAndCheckDataAccessError(objectReference, fc);
if (MmsValue_getType(mmsValue) != (int)MmsType.MMS_FLOAT)
{
MmsValue_delete(mmsValue);
throw new IedConnectionException("Result is not of type float (MMS_FLOAT)", 0);
}
float value = MmsValue_toFloat(mmsValue);
MmsValue_delete(mmsValue);
return value;
}
/// Read the value of a basic data attribute (BDA) of type string (VisibleString or MmsString).
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public string ReadStringValue(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternalAndCheckDataAccessError(objectReference, fc);
if (!((MmsValue_getType(mmsValue) == (int)MmsType.MMS_VISIBLE_STRING) || (MmsValue_getType(mmsValue) == (int)MmsType.MMS_STRING)))
{
MmsValue_delete(mmsValue);
throw new IedConnectionException("Result is not of type string", 0);
}
IntPtr ptr = MmsValue_toString(mmsValue);
string returnString = Marshal.PtrToStringAnsi(ptr);
MmsValue_delete(mmsValue);
return returnString;
}
/// Read the value of a basic data attribute (BDA) of type quality.
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public Quality ReadQualityValue(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternalAndCheckDataAccessError(objectReference, fc);
if (MmsValue_getType(mmsValue) == (int)MmsType.MMS_BIT_STRING)
{
int bitStringValue = (int)MmsValue_getBitStringAsInteger(mmsValue);
MmsValue_delete(mmsValue);
return new Quality(bitStringValue);
}
else
{
MmsValue_delete(mmsValue);
throw new IedConnectionException("Result is not of type bit string(Quality)", 0);
}
}
/// Read the value of a basic data attribute (BDA) of type bit string.
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public int ReadBitStringValue(string objectReference, FunctionalConstraint fc)
{
IntPtr mmsValue = readObjectInternalAndCheckDataAccessError(objectReference, fc);
if (MmsValue_getType(mmsValue) == (int)MmsType.MMS_BIT_STRING)
{
int bitStringValue = (int)MmsValue_getBitStringAsInteger(mmsValue);
MmsValue_delete(mmsValue);
return bitStringValue;
}
else
{
MmsValue_delete(mmsValue);
throw new IedConnectionException("Result is not of type bit string", 0);
}
}
/// Read the value of a basic data attribute (BDA) of type timestamp (MMS_UTC_TIME).
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public Timestamp ReadTimestampValue(string objectReference, FunctionalConstraint fc)
{
var mmsValuePtr = readObjectInternalAndCheckDataAccessError(objectReference, fc);
var mmsValue = new MmsValue(mmsValuePtr, true);
if (mmsValue.GetType() == MmsType.MMS_UTC_TIME)
return new Timestamp(mmsValue);
else
throw new IedConnectionException("Result is not of type timestamp (MMS_UTC_TIME)", 0);
}
/// Read the value of a basic data attribute (BDA) of type integer (MMS_INTEGER).
/// This function should also be used if enumerations are beeing read. Because
/// enumerations are mapped to integer types for the MMS mapping
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// This exception is thrown if there is a connection or service error
public Int64 ReadIntegerValue(string objectReference, FunctionalConstraint fc)
{
var mmsValuePtr = readObjectInternalAndCheckDataAccessError(objectReference, fc);
var mmsValue = new MmsValue(mmsValuePtr, true);
if (mmsValue.GetType() == MmsType.MMS_INTEGER)
return mmsValue.ToInt64();
else
throw new IedConnectionException("Result is not of type integer (MMS_INTEGER)", 0);
}
/// Write the value of a data attribute (DA) or functional constraint data object (FCDO).
/// This function can be used to write simple or complex variables (setpoints, parameters, descriptive values...)
/// of the server.
/// The object reference of a BDA.
/// The functional constraint (FC) of the object
/// MmsValue object representing asimple or complex variable data
/// This exception is thrown if there is a connection or service error
public void WriteValue(string objectReference, FunctionalConstraint fc, MmsValue value)
{
int error;
IedConnection_writeObject(connection, out error, objectReference, (int)fc, value.valueReference);
if (error != 0)
throw new IedConnectionException("Write value failed", error);
}
/// Delete file
/// The name of the file.
/// This exception is thrown if there is a connection or service error
public void DeleteFile(string fileName)
{
int error;
IedConnection_deleteFile(connection, out error, fileName);
if (error != 0)
throw new IedConnectionException("Deleting file " + fileName + " failed", error);
}
private IedConnection_GenericServiceHandler internalGenericServiceHandler = null;
private void nativeGenericServiceHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
GenericServiceHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
handler(invokeId, handlerParameter, clientError);
}
/// Delete file- asynchronous version
/// The name of the file.
/// This exception is thrown if there is a connection or service error
public UInt32 DeleteFileAsync(string filename, GenericServiceHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_deleteFileAsync(connection, out error, filename, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get file failed", error);
}
return invokeId;
}
/// Read the content of a file directory.
/// The name of the directory.
/// This exception is thrown if there is a connection or service error
public List GetFileDirectory(string directoryName)
{
int error;
IntPtr fileEntryList = IedConnection_getFileDirectory(connection, out error, directoryName);
if (error != 0)
throw new IedConnectionException("Reading file directory failed", error);
List fileDirectory = new List();
IntPtr element = LinkedList_getNext(fileEntryList);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
FileDirectoryEntry entry = new FileDirectoryEntry(elementData);
fileDirectory.Add(entry);
FileDirectoryEntry_destroy(elementData);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(fileEntryList);
return fileDirectory;
}
/// Read the content of a file directory. - single request version
/// The name of the directory.
/// the filename that defines the continuation point, or null for the first request
/// true, when more files are available, false otherwise
/// list of file directory entries
/// This exception is thrown if there is a connection or service error
public List GetFileDirectoryEx(string directoryName, string continueAfter, out bool moreFollows)
{
int error;
IntPtr fileEntryList = IedConnection_getFileDirectoryEx(connection, out error, directoryName, continueAfter, out moreFollows);
if (error != 0)
throw new IedConnectionException("Reading file directory failed", error);
List fileDirectory = new List();
IntPtr element = LinkedList_getNext(fileEntryList);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
FileDirectoryEntry entry = new FileDirectoryEntry(elementData);
fileDirectory.Add(entry);
FileDirectoryEntry_destroy(elementData);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(fileEntryList);
return fileDirectory;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool InternalIedClientGetFileHandler(IntPtr parameter,IntPtr buffer,UInt32 bytesRead);
private bool iedClientGetFileHandler(IntPtr parameter, IntPtr buffer, UInt32 bytesRead)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
GetFileCallback getFileCallback = (GetFileCallback)handle.Target;
byte[] bytes = new byte[bytesRead];
Marshal.Copy(buffer, bytes, 0, (int)bytesRead);
return getFileCallback.handler(getFileCallback.parameter, bytes);
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32 IedConnection_getFile(IntPtr self, out int error, string fileName, InternalIedClientGetFileHandler handler,
IntPtr handlerParameter);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setFile(IntPtr self, out int error, string sourceFilename, string destinationFilename);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_setFilestoreBasepath(IntPtr self, string fileName);
public delegate bool GetFileHandler(object parameter,byte[] data);
private class GetFileCallback
{
public GetFileCallback(GetFileHandler handler, object parameter)
{
this.handler = handler;
this.parameter = parameter;
}
public GetFileHandler handler;
public object parameter;
}
///
/// Download a file from the server.
///
///
/// File name of the file (full path)
///
///
/// Callback handler that is invoked for each chunk of the file received
///
///
/// User provided parameter that is passed to the callback handler
///
/// This exception is thrown if there is a connection or service error
public void GetFile(string fileName, GetFileHandler handler, object parameter)
{
int error;
GetFileCallback getFileCallback = new GetFileCallback(handler, parameter);
GCHandle handle = GCHandle.Alloc(getFileCallback);
IedConnection_getFile(connection, out error, fileName, new InternalIedClientGetFileHandler(iedClientGetFileHandler),
GCHandle.ToIntPtr(handle));
if (error != 0)
throw new IedConnectionException("Error reading file", error);
handle.Free();
}
///
/// Set the virtual filestore basepath for the setFile service
///
/// The new virtual filestore basepath
public void SetFilestoreBasepath(string basepath)
{
IedConnection_setFilestoreBasepath(connection, basepath);
}
///
/// Upload a file to the server.
///
/// The filename of the local (client side) file
/// The filename of the remote (service side) file
public void SetFile(string sourceFilename, string destinationFilename)
{
int error;
IedConnection_setFile(connection, out error, sourceFilename, destinationFilename);
if (error != 0)
throw new IedConnectionException("Error uploading file", error);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool IedConnection_GetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId,
IntPtr buffer, UInt32 bytesRead, [MarshalAs(UnmanagedType.I1)] bool moreFollows);
///
/// Callback handler for the asynchronous get file service. Will be invoked for each chunk of received data
///
/// The invoke ID of the reqeust triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
/// the invokeId of the first (file open) request
/// the file data received with the last response, or null if no file data available
/// indicates that more file data follows
/// true, continue the file download when moreFollows is true, false, stop file download
public delegate bool GetFileAsyncHandler(UInt32 invokeId, object parameter, IedClientError err, UInt32 originalInvokeId, byte[] buffer, bool moreFollows);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern UInt32
IedConnection_getFileAsync(IntPtr self, out int error, string fileName, IedConnection_GetFileAsyncHandler handler,
IntPtr parameter);
private IedConnection_GetFileAsyncHandler internalGetFileAsyncHandler = null;
private bool nativeGetFileAsyncHandler(UInt32 invokeId, IntPtr parameter, int err, UInt32 originalInvokeId,
IntPtr buffer, UInt32 bytesRead, bool moreFollows)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
GetFileAsyncHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
IedClientError clientError = (IedClientError)err;
byte[] bytes = null;
if (clientError == IedClientError.IED_ERROR_OK)
{
bytes = new byte[bytesRead];
Marshal.Copy(buffer, bytes, 0, (int)bytesRead);
}
bool retVal = handler(invokeId, handlerParameter, clientError, originalInvokeId, bytes, moreFollows);
if (clientError != IedClientError.IED_ERROR_OK)
{
handle.Free();
}
else
{
if (moreFollows == false)
{
handle.Free();
}
else
{
if (retVal == false)
{
handle.Free();
}
}
}
return retVal;
}
///
/// Download a file from the server.
///
///
/// File name of the file (full path)
///
///
/// Callback handler that is invoked for each chunk of the file received
///
///
/// User provided parameter that is passed to the callback handler
///
/// invoke ID of the request
/// This exception is thrown if there is a connection or service error
public UInt32 GetFileAsync(string fileName, GetFileAsyncHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetFileAsyncHandler == null)
internalGetFileAsyncHandler = new IedConnection_GetFileAsyncHandler(nativeGetFileAsyncHandler);
UInt32 invokeId = IedConnection_getFileAsync(connection, out error, fileName, internalGetFileAsyncHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get file failed", error);
}
return invokeId;
}
///
/// Abort (close) the connection.
///
/// This function will send an abort request to the server. This will immediately interrupt the
/// connection.
/// This exception is thrown if there is a connection or service error
public void Abort()
{
int error;
IedConnection_abort(connection, out error);
if (error != 0)
throw new IedConnectionException("Abort failed", error);
}
///
/// Abort (close) the connection - asynchronous version
///
/// This function will send an abort request to the server. This will immediately interrupt the
/// connection.
/// This exception is thrown if there is a connection or service error
public void AbortAsync()
{
int error;
IedConnection_abortAsync(connection, out error);
if (error != 0)
throw new IedConnectionException("Abort failed", error);
}
///
/// Release (close) the connection.
///
/// This function will send an release request to the server. The function will block until the
/// connection is released or an error occured.
/// This exception is thrown if there is a connection or service error
public void Release()
{
int error;
IedConnection_release(connection, out error);
if (error != 0)
throw new IedConnectionException("Release failed", error);
}
///
/// Release (close) the connection - asynchronous version
///
/// This function will send an release request to the server. The function will block until the
/// connection is released or an error occured.
/// This exception is thrown if there is a connection or service error
public void ReleaseAsync()
{
int error;
IedConnection_releaseAsync(connection, out error);
if (error != 0)
throw new IedConnectionException("Release failed", error);
}
///
/// Immediately close the connection.
///
/// This function will close the connnection to the server by closing the TCP connection.
/// The client will not send an abort or release request as required by the specification!
/// This exception is thrown if there is a connection or service error
public void Close()
{
IedConnection_close(connection);
}
private void MyConnectionClosedHandler(IntPtr parameter, IntPtr self)
{
if (userProvidedConnectionClosedHandler != null)
userProvidedConnectionClosedHandler(this);
}
///
/// Install a callback handler that will be invoked if the connection is closed.
///
/// The handler is called when the connection is closed no matter if the connection was closed
/// by the client or by the server. Any new call to this function will replace the callback handler installed
/// by a prior function call.
/// The user provided callback handler
/// This exception is thrown if there is a connection or service error
[Obsolete("ConnectionClosedHandler is deprecated, please use StateChangedHandler instead")]
public void InstallConnectionClosedHandler(ConnectionClosedHandler handler)
{
if (connectionClosedHandler == null)
{
connectionClosedHandler = new InternalConnectionClosedHandler(MyConnectionClosedHandler);
IedConnection_installConnectionClosedHandler(connection, connectionClosedHandler, connection);
}
userProvidedConnectionClosedHandler = handler;
}
private void MyStateChangedHandler(IntPtr parameter, IntPtr IedConnection, int newState)
{
if (stateChangedHandler != null)
stateChangedHandler(this, (IedConnectionState)newState);
}
///
/// Sets the handler for StateChanged events
///
/// The state changed event handler
public void InstallStateChangedHandler(StateChangedHandler handler)
{
stateChangedHandler = handler;
if (internalStateChangedHandler == null)
{
internalStateChangedHandler = new InternalStateChangedHandler(MyStateChangedHandler);
IedConnection_installStateChangedHandler(connection, internalStateChangedHandler, IntPtr.Zero);
}
}
///
/// Sets the handler for StateChanged events
///
/// The state changed event handler
public StateChangedHandler StateChanged
{
set
{
InstallStateChangedHandler(value);
}
}
///
/// Read the values of a data set (GetDataSetValues service).
///
/// This function will invoke a readDataSetValues service and return a new DataSet value containing the
/// received values.
/// The object reference of the data set
/// This exception is thrown if there is a connection or service error
public DataSet GetDataSetValues(string dataSetReference)
{
return ReadDataSetValues(dataSetReference, null);
}
///
/// Read the values of a data set (GetDataSetValues service).
///
/// This function will invoke a readDataSetValues service and return a new DataSet value containing the
/// received values. If an existing instance of DataSet is provided to the function the existing instance will be
/// updated by the new values.
/// The object reference of the data set
/// The object reference of an existing data set instance or null
/// a DataSet instance containing the received values
/// This exception is thrown if there is a connection or service error
public DataSet ReadDataSetValues(string dataSetReference, DataSet dataSet)
{
IntPtr nativeClientDataSet = IntPtr.Zero;
if (dataSet != null)
nativeClientDataSet = dataSet.getNativeInstance();
int error;
nativeClientDataSet = IedConnection_readDataSetValues(connection, out error, dataSetReference, nativeClientDataSet);
if (error != 0)
throw new IedConnectionException("Reading data set failed", error);
if (dataSet == null)
dataSet = new DataSet(nativeClientDataSet);
return dataSet;
}
///
/// Writes the values of a data set (SetDataSetValues service).
///
/// The list of access results
/// The object reference of the data set
/// The new values for the data set members. The values have to be of the same number and type as the data set members
public List WriteDataSetValues(string dataSetReference, List values)
{
int error;
IntPtr accessResults = IntPtr.Zero;
IntPtr valueList = LinkedList_create();
foreach (MmsValue mmsValue in values)
{
LinkedList_add(valueList, mmsValue.valueReference);
}
IedConnection_writeDataSetValues(connection, out error, dataSetReference, valueList, out accessResults);
LinkedList_destroyStatic(valueList);
/* handle access results */
List accessResultList = null;
if (accessResults != IntPtr.Zero)
{
IntPtr element = LinkedList_getNext(accessResults);
while (element != IntPtr.Zero)
{
IntPtr elementData = LinkedList_getData(element);
MmsValue accessResultValue = new MmsValue(elementData, true);
MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError();
if (accessResultList == null)
accessResultList = new List();
accessResultList.Add(dataAccessError);
element = LinkedList_getNext(element);
}
LinkedList_destroyStatic(accessResults);
}
if (error != 0)
throw new IedConnectionException("Writing data set failed", error);
return accessResultList;
}
///
/// Create a new data set.
///
/// This function creates a new data set at the server. The data set consists of the members defined
/// by the list of object references provided.
/// The object reference of the data set
/// A list of object references of the data set elements
/// This exception is thrown if there is a connection or service error
public void CreateDataSet(string dataSetReference, List dataSetElements)
{
IntPtr linkedList = LinkedList_create();
foreach (string dataSetElement in dataSetElements)
{
IntPtr handle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(dataSetElement);
LinkedList_add(linkedList, handle);
}
int error;
IedConnection_createDataSet(connection, out error, dataSetReference, linkedList);
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
throw new IedConnectionException("Failed to create data set", error);
}
///
/// Create a new data set - asynchronous version.
///
/// This function creates a new data set at the server. The data set consists of the members defined
/// by the list of object references provided.
/// The object reference of the data set
/// A list of object references of the data set elements
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 CreateDataSetAsync(string dataSetReference, List dataSetElements, GenericServiceHandler handler, object parameter)
{
int error = 0;
IntPtr linkedList = LinkedList_create();
foreach (string dataSetElement in dataSetElements)
{
IntPtr dataSetElementHandle = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(dataSetElement);
LinkedList_add(linkedList, dataSetElementHandle);
}
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_createDataSetAsync(connection, out error, dataSetReference, linkedList, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
LinkedList_destroyDeep(linkedList, new LinkedListValueDeleteFunction(FreeHGlobaleDeleteFunction));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Create data set failed", error);
}
return invokeId;
}
///
/// Delete a data set.
///
/// This function will delete a data set at the server. This function may fail if the data set is not
/// deletable.
/// The object reference of the data set
/// true if data set has been deleted, false otherwise
/// This exception is thrown if there is a connection or service error
public bool DeleteDataSet(string dataSetReference)
{
int error;
bool isDeleted = IedConnection_deleteDataSet(connection, out error, dataSetReference);
if (error != 0)
throw new IedConnectionException("Failed to delete data set", error);
return isDeleted;
}
///
/// Delete a data set - asynchronous version.
///
/// This function will delete a data set at the server. This function may fail if the data set is not
/// deletable.
/// The object reference of the data set
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 DeleteDataSetAsync(string dataSetReference, GenericServiceHandler handler, object parameter)
{
int error = 0;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGenericServiceHandler == null)
internalGenericServiceHandler = new IedConnection_GenericServiceHandler(nativeGenericServiceHandler);
UInt32 invokeId = IedConnection_deleteDataSetAsync(connection, out error, dataSetReference, internalGenericServiceHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Delete data set failed", error);
}
return invokeId;
}
///
/// Get the directory of the data set.
///
/// This function returns a list of object references with appended functional constraints (FC) of the data set elemenents.
/// The object reference of the data set
/// the list of object references
/// This exception is thrown if there is a connection or service error
public List GetDataSetDirectory(string dataSetReference)
{
bool isDeletable;
return GetDataSetDirectory(dataSetReference, out isDeletable);
}
///
/// Get the directory of the data set.
///
/// This function returns a list of object references with appended functional constraints (FC) of the data set elemenents.
/// The object reference of the data set
/// Indication if this data set is permanent or deletable.
/// the list of object references
/// This exception is thrown if there is a connection or service error
public List GetDataSetDirectory(string dataSetReference, out bool isDeletable)
{
int error;
IntPtr linkedList = IedConnection_getDataSetDirectory(connection, out error, dataSetReference, out isDeletable);
if (error != 0)
throw new IedConnectionException("getDataSetDirectory failed", error);
IntPtr element = LinkedList_getNext(linkedList);
List newList = new List();
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(linkedList);
return newList;
}
///
/// Get data set directory handler.
///
/// The invoke ID of the reqeust triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
/// the list of data set entry references
/// data set can be deleted by a client (dynamic data set)
public delegate void GetDataSetDirectoryHandler(UInt32 invokeId, object parameter, IedClientError err, List dataSetDirectory, bool isDeletable);
private IedConnection_GetDataSetDirectoryHandler internalGetDataSetDirectoryHandler = null;
private void nativeGetDataSetDirectoryHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr dataSetDirectory, bool isDeletable)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
GetDataSetDirectoryHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
List newList = null;
if (dataSetDirectory != IntPtr.Zero)
{
newList = new List();
IntPtr element = LinkedList_getNext(dataSetDirectory);
while (element != IntPtr.Zero)
{
string dataObject = Marshal.PtrToStringAnsi(LinkedList_getData(element));
newList.Add(dataObject);
element = LinkedList_getNext(element);
}
LinkedList_destroy(dataSetDirectory);
}
handler.Invoke(invokeId, handlerParameter, (IedClientError)err, newList, isDeletable);
}
///
/// Read the data set directory - asynchronous version
///
/// The data set directory async.
/// Data set reference.
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 GetDataSetDirectoryAsync(string dataSetReference, GetDataSetDirectoryHandler handler, object parameter)
{
int error = 0;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetDataSetDirectoryHandler == null)
internalGetDataSetDirectoryHandler = new IedConnection_GetDataSetDirectoryHandler(nativeGetDataSetDirectoryHandler);
UInt32 invokeId = IedConnection_getDataSetDirectoryAsync(connection, out error, dataSetReference, internalGetDataSetDirectoryHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get data set directory failed", error);
}
return invokeId;
}
///
/// Read object handler.
///
/// The invoke ID of the reqeust triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
/// The read result value or null in case of an error
public delegate void ReadValueHandler(UInt32 invokeId,object parameter,IedClientError err,MmsValue value);
private IedConnection_ReadObjectHandler internalReadObjectHandler = null;
private void nativeReadObjectHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr value)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
ReadValueHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
MmsValue mmsValue = null;
if (value != IntPtr.Zero)
{
mmsValue = new MmsValue(value, true);
}
handler(invokeId, handlerParameter, clientError, mmsValue);
}
/// Asynchronously read the value of a data attribute (DA) or functional constraint data object (FCDO) - GetData service
/// The object reference of a DA or FCDO.
/// The functional constraint (FC) of the object
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 ReadValueAsync(string objectReference, FunctionalConstraint fc, ReadValueHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalReadObjectHandler == null)
internalReadObjectHandler = new IedConnection_ReadObjectHandler(nativeReadObjectHandler);
UInt32 invokeId = IedConnection_readObjectAsync(connection, out error, objectReference, (int)fc, internalReadObjectHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Reading value failed", error);
}
return invokeId;
}
private IedConnection_GetVariableSpecificationHandler internalGetVariableSpecificationHandler = null;
private void nativeGetVariableSpecifcationHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr spec)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
GetVariableSpecifcationHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
MmsVariableSpecification varSpec = null;
if (spec != IntPtr.Zero)
varSpec = new MmsVariableSpecification(spec, true);
handler(invokeId, handlerParameter, clientError, varSpec);
}
public delegate void GetVariableSpecifcationHandler(UInt32 invokeId,object parameter,IedClientError err,MmsVariableSpecification spec);
/// Read the variable specification (type description of a DA or FCDO
/// The object reference of a DA or FCDO.
/// The functional constraint (FC) of the object
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 GetVariableSpecificationAsync(string objectReference, FunctionalConstraint fc, GetVariableSpecifcationHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetVariableSpecificationHandler == null)
internalGetVariableSpecificationHandler = new IedConnection_GetVariableSpecificationHandler(nativeGetVariableSpecifcationHandler);
UInt32 invokeId = IedConnection_getVariableSpecificationAsync(connection, out error, objectReference, (int)fc, internalGetVariableSpecificationHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Reading variable specification failed", error);
}
return invokeId;
}
private IedConnection_ReadDataSetHandler internalReadDataSetHandler = null;
private void nativeReadDataSetHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nativeDataSet)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
ReadDataSetHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
DataSet dataSet = callbackInfo.Item3;
handle.Free();
IedClientError clientError = (IedClientError)err;
if (nativeDataSet != IntPtr.Zero)
{
if (dataSet == null)
dataSet = new DataSet(nativeDataSet);
}
handler(invokeId, handlerParameter, clientError, dataSet);
}
public delegate void ReadDataSetHandler(UInt32 invokeId,object parameter,IedClientError err,DataSet dataSet);
///
/// Read the values of a data set (GetDataSetValues service) - asynchronous version
///
/// This function will invoke a readDataSetValues service and in case of success returns a new DataSet value
/// containing the received values by the callback function. If an existing instance of DataSet is provided to the
/// function the existing instance will be updated by the new values.
///
/// The object reference of the data set
/// The object reference of an existing data set instance or null
/// Callback function to handle the received response or service timeout
/// User provided callback parameter. Will be passed to the callback function
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 ReadDataSetValuesAsync(string dataSetReference, DataSet dataSet, ReadDataSetHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter, dataSet);
GCHandle handle = GCHandle.Alloc(callbackInfo);
IntPtr dataSetPtr = IntPtr.Zero;
if (dataSet != null)
dataSetPtr = dataSet.getNativeInstance();
if (internalReadDataSetHandler == null)
internalReadDataSetHandler = new IedConnection_ReadDataSetHandler(nativeReadDataSetHandler);
UInt32 invokeId = IedConnection_readDataSetValuesAsync(connection, out error, dataSetReference, dataSetPtr, internalReadDataSetHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Reading data set failed", error);
}
return invokeId;
}
///
/// Write value handler.
///
/// The invoke ID of the reqeust triggering this callback
/// user provided callback parameter
/// Error code of response or timeout error in case of a response timeout
public delegate void WriteValueHandler(UInt32 invokeId,object parameter,IedClientError err);
private IedConnection_WriteObjectHandler internalWriteObjectHandler = null;
private void nativeWriteObjectHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
WriteValueHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
handler(invokeId, handlerParameter, clientError);
}
public UInt32 WriteValueAsync(string objectReference, FunctionalConstraint fc, MmsValue value, WriteValueHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalWriteObjectHandler == null)
internalWriteObjectHandler = new IedConnection_WriteObjectHandler(nativeWriteObjectHandler);
UInt32 invokeId = IedConnection_writeObjectAsync(connection, out error, objectReference, (int)fc, value.valueReference, internalWriteObjectHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Writing value failed", error);
}
return invokeId;
}
public delegate void GetNameListHandler(UInt32 invokeId,object parameter,IedClientError err,List nameList,bool moreFollows);
private IedConnection_GetNameListHandler internalGetNameListHandler = null;
private void nativeGetNameListHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr nameList, [MarshalAs(UnmanagedType.I1)] bool moreFollows)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple> callbackInfo = handle.Target as Tuple>;
GetNameListHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
List resultList = callbackInfo.Item3;
handle.Free();
IedClientError clientError = (IedClientError)err;
if (nameList != IntPtr.Zero)
{
IntPtr element = LinkedList_getNext(nameList);
if (resultList == null)
resultList = new List();
while (element != IntPtr.Zero)
{
string ld = Marshal.PtrToStringAnsi(LinkedList_getData(element));
resultList.Add(ld);
element = LinkedList_getNext(element);
}
LinkedList_destroy(nameList);
handler(invokeId, handlerParameter, clientError, resultList, moreFollows);
}
else
{
handler(invokeId, handlerParameter, clientError, null, moreFollows);
}
}
///
/// Gets the server directory (Logical devices or file objects)
///
/// list where to store the result or null to create a new list for the result
/// continuation value (last received name)
/// user provided callback function
/// user provided callback parameter
/// the invoke ID of the sent request
/// This exception is thrown if there is a connection or service error
public UInt32 GetServerDirectoryAsync(List result, string continueAfter, GetNameListHandler handler, object parameter)
{
int error;
Tuple> callbackInfo = Tuple.Create(handler, parameter, result);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getServerDirectoryAsync(connection, out error, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get server directory failed", error);
}
return invokeId;
}
public UInt32 GetLogicalDeviceVariablesAsync(string ldName, string continueAfter, GetNameListHandler handler, object parameter)
{
return GetLogicalDeviceVariablesAsync(null, ldName, continueAfter, handler, parameter);
}
public UInt32 GetLogicalDeviceVariablesAsync(List result, string ldName, string continueAfter, GetNameListHandler handler, object parameter)
{
int error;
Tuple> callbackInfo = Tuple.Create(handler, parameter, result);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getLogicalDeviceVariablesAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get logical device variables failed", error);
}
return invokeId;
}
public UInt32 GetLogicalDeviceDataSetsAsync(string ldName, string continueAfter, GetNameListHandler handler, object parameter)
{
return GetLogicalDeviceDataSetsAsync(null, ldName, continueAfter, handler, parameter);
}
public UInt32 GetLogicalDeviceDataSetsAsync(List result, string ldName, string continueAfter, GetNameListHandler handler, object parameter)
{
int error;
Tuple> callbackInfo = Tuple.Create(handler, parameter, result);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetNameListHandler == null)
internalGetNameListHandler = new IedConnection_GetNameListHandler(nativeGetNameListHandler);
UInt32 invokeId = IedConnection_getLogicalDeviceDataSetsAsync(connection, out error, ldName, continueAfter, IntPtr.Zero, internalGetNameListHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("Get logical device data sets failed", error);
}
return invokeId;
}
public delegate void QueryLogHandler(UInt32 invokeId,object parameter,IedClientError err,List journalEntries,bool moreFollows);
private IedConnection_QueryLogHandler internalQueryLogHandler = null;
private void nativeQueryLogHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr journalEntries,
[MarshalAs(UnmanagedType.I1)] bool moreFollows)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
QueryLogHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
handle.Free();
IedClientError clientError = (IedClientError)err;
var logResult = WrapNativeLogQueryResult(journalEntries);
handler(invokeId, handlerParameter, clientError, logResult, moreFollows);
}
///
/// Queries all log entries of the given time range (asynchronous version)
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// Start time of the time range
/// End time of the time range
/// user provided callback function
/// user provided callback parameter
/// This exception is thrown if there is a connection or service error
public UInt32 QueryLogByTimeAsync(string logRef, ulong startTime, ulong stopTime, QueryLogHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalQueryLogHandler == null)
internalQueryLogHandler = new IedConnection_QueryLogHandler(nativeQueryLogHandler);
UInt32 invokeId = IedConnection_queryLogByTimeAsync(connection, out error, logRef, startTime, stopTime, internalQueryLogHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("QueryLogByTime failed", error);
}
return invokeId;
}
///
/// Queries all log entries of the given time range (asynchronous version)
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// Start time of the time range
/// End time of the time range
/// user provided callback function
/// user provided callback parameter
/// This exception is thrown if there is a connection or service error
public UInt32 QueryLogByTimeAsync(string logRef, DateTime startTime, DateTime stopTime, QueryLogHandler handler, object parameter)
{
ulong startTimeMs = LibIEC61850.DateTimeToMsTimestamp(startTime);
ulong stopTimeMs = LibIEC61850.DateTimeToMsTimestamp(stopTime);
return QueryLogByTimeAsync(logRef, startTimeMs, stopTimeMs, handler, parameter);
}
///
/// Queries all log entries after the entry with the given entryID and timestamp (asynchronous version)
///
/// The list of log entries contained in the response
/// The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")
/// EntryID of the last received MmsJournalEntry
/// user provided callback function
/// user provided callback parameter
/// This exception is thrown if there is a connection or service error
public UInt32 QueryLogAfterAsync(string logRef, byte[] entryID, ulong timestamp, QueryLogHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter);
GCHandle handle = GCHandle.Alloc(callbackInfo);
MmsValue entryIdValue = new MmsValue(entryID);
if (internalQueryLogHandler == null)
internalQueryLogHandler = new IedConnection_QueryLogHandler(nativeQueryLogHandler);
UInt32 invokeId = IedConnection_queryLogAfterAsync(connection, out error, logRef, entryIdValue.valueReference, timestamp, internalQueryLogHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("QueryLogAfter failed", error);
}
return invokeId;
}
internal void UninstallReportHandler(string objectReference)
{
if (connection != IntPtr.Zero)
{
IedConnection_uninstallReportHandler(connection, objectReference);
}
}
internal void InstallReportHandler(string objectReference, string reportId, InternalReportHandler internalHandler)
{
if (connection != IntPtr.Zero)
{
IedConnection_installReportHandler(connection, objectReference, reportId, internalHandler, IntPtr.Zero);
}
}
internal void GetRCBValues(out int error, string objectReference, IntPtr updateRcb)
{
if (connection != IntPtr.Zero)
{
IedConnection_getRCBValues(connection, out error, objectReference, updateRcb);
}
else
{
error = 1; /* not connected */
}
}
internal void SetRCBValues(out int error, IntPtr rcb, UInt32 parametersMask, bool singleRequest)
{
if (connection != IntPtr.Zero)
{
IedConnection_setRCBValues(connection, out error, rcb, parametersMask, singleRequest);
}
else
{
error = 1; /* not connected */
}
}
private IedConnection_GetRCBValuesHandler internalGetRCBValuesHandler = null;
private void nativeGetRCBValuesHandler(UInt32 invokeId, IntPtr parameter, int err, IntPtr rcbPtr)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
GetRCBValuesHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
ReportControlBlock rcb = callbackInfo.Item3;
handle.Free();
IedClientError clientError = (IedClientError)err;
handler(invokeId, handlerParameter, clientError, rcb);
}
internal UInt32 GetRCBValuesAsync(string objectReference, ReportControlBlock updateRcb, GetRCBValuesHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter, updateRcb);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalGetRCBValuesHandler == null)
internalGetRCBValuesHandler = new IedConnection_GetRCBValuesHandler(nativeGetRCBValuesHandler);
UInt32 invokeId = IedConnection_getRCBValuesAsync(connection, out error, objectReference, updateRcb.self, internalGetRCBValuesHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("GetRCBValues failed", error);
}
return invokeId;
}
private IedConnection_GenericServiceHandler internalSetRcbValueHandler = null;
private void nativeSetRcbValuesHandler(UInt32 invokeId, IntPtr parameter, int err)
{
GCHandle handle = GCHandle.FromIntPtr(parameter);
Tuple callbackInfo = handle.Target as Tuple;
SetRCBValuesHandler handler = callbackInfo.Item1;
object handlerParameter = callbackInfo.Item2;
ReportControlBlock rcb = callbackInfo.Item3;
handle.Free();
IedClientError clientError = (IedClientError)err;
handler(invokeId, handlerParameter, clientError, rcb);
}
internal UInt32 SetRCBValuesAsync(ReportControlBlock rcb, UInt32 parametersMask, bool singleRequest, SetRCBValuesHandler handler, object parameter)
{
int error;
Tuple callbackInfo = Tuple.Create(handler, parameter, rcb);
GCHandle handle = GCHandle.Alloc(callbackInfo);
if (internalSetRcbValueHandler == null)
internalSetRcbValueHandler = new IedConnection_GenericServiceHandler(nativeSetRcbValuesHandler);
UInt32 invokeId = IedConnection_setRCBValuesAsync(connection, out error, rcb.self, parametersMask, singleRequest, internalSetRcbValueHandler, GCHandle.ToIntPtr(handle));
if (error != 0)
{
handle.Free();
throw new IedConnectionException("SetRCBValues failed", error);
}
return invokeId;
}
}
public class IedConnectionException : Exception
{
private int errorCode;
public IedConnectionException(string message, int errorCode)
: base(message)
{
this.errorCode = errorCode;
}
public IedConnectionException(string message)
: base(message)
{
this.errorCode = 0;
}
public int GetErrorCode()
{
return this.errorCode;
}
public IedClientError GetIedClientError()
{
return (IedClientError)this.errorCode;
}
}
public class FileDirectoryEntry
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr FileDirectoryEntry_getFileName(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt32 FileDirectoryEntry_getFileSize(IntPtr self);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
private static extern UInt64 FileDirectoryEntry_getLastModified(IntPtr self);
private string fileName;
private UInt32 fileSize;
private UInt64 lastModified;
internal FileDirectoryEntry(IntPtr nativeFileDirectoryEntry)
{
fileName = Marshal.PtrToStringAnsi(FileDirectoryEntry_getFileName(nativeFileDirectoryEntry));
fileSize = FileDirectoryEntry_getFileSize(nativeFileDirectoryEntry);
lastModified = FileDirectoryEntry_getLastModified(nativeFileDirectoryEntry);
}
public string GetFileName()
{
return fileName;
}
public UInt32 GetFileSize()
{
return fileSize;
}
public UInt64 GetLastModified()
{
return lastModified;
}
}
///
/// Connection state of an IedConnection instance
///
public enum IedConnectionState
{
///
/// The connection is closed. Requests cannot be sent.
///
IED_STATE_CLOSED = 0,
///
/// The connection is connecting. Requests cannot be sent yet.
///
IED_STATE_CONNECTING = 1,
///
/// The connection is up. Requests can be sent.
///
IED_STATE_CONNECTED = 2,
///
/// The connection is closing. Requests connect be sent.
///
IED_STATE_CLOSING = 3
}
///
/// Error codes for client side functions
///
public enum IedClientError
{
/* general errors */
/** No error occurred - service request has been successful */
IED_ERROR_OK = 0,
/** The service request can not be executed because the client is not yet connected */
IED_ERROR_NOT_CONNECTED = 1,
/** Connect service not execute because the client is already connected */
IED_ERROR_ALREADY_CONNECTED = 2,
/** The service request can not be executed caused by a loss of connection */
IED_ERROR_CONNECTION_LOST = 3,
/** The service or some given parameters are not supported by the client stack or by the server */
IED_ERROR_SERVICE_NOT_SUPPORTED = 4,
/** Connection rejected by server */
IED_ERROR_CONNECTION_REJECTED = 5,
/* client side errors */
/** API function has been called with an invalid argument */
IED_ERROR_USER_PROVIDED_INVALID_ARGUMENT = 10,
IED_ERROR_ENABLE_REPORT_FAILED_DATASET_MISMATCH = 11,
/** The object provided object reference is invalid (there is a syntactical error). */
IED_ERROR_OBJECT_REFERENCE_INVALID = 12,
/** Received object is of unexpected type */
IED_ERROR_UNEXPECTED_VALUE_RECEIVED = 13,
/* service error - error reported by server */
/** The communication to the server failed with a timeout */
IED_ERROR_TIMEOUT = 20,
/** The server rejected the access to the requested object/service due to access control */
IED_ERROR_ACCESS_DENIED = 21,
/** The server reported that the requested object does not exist */
IED_ERROR_OBJECT_DOES_NOT_EXIST = 22,
/** The server reported that the requested object already exists */
IED_ERROR_OBJECT_EXISTS = 23,
/** The server does not support the requested access method */
IED_ERROR_OBJECT_ACCESS_UNSUPPORTED = 24,
/** The server expected an object of another type */
IED_ERROR_TYPE_INCONSISTENT = 25,
/** The object or service is temporarily unavailable */
IED_ERROR_TEMPORARILY_UNAVAILABLE = 26,
/** The specified object is not defined in the server (returned by server) */
IED_ERROR_OBJECT_UNDEFINED = 27,
/** The specified address is invalid (returned by server) */
IED_ERROR_INVALID_ADDRESS = 28,
/** Service failed due to a hardware fault (returned by server) */
IED_ERROR_HARDWARE_FAULT = 29,
/** The requested data type is not supported by the server (returned by server) */
IED_ERROR_TYPE_UNSUPPORTED = 30,
/** The provided attributes are inconsistent (returned by server) */
IED_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT = 31,
/** The provided object value is invalid (returned by server) */
IED_ERROR_OBJECT_VALUE_INVALID = 32,
/** The object is invalidated (returned by server) */
IED_ERROR_OBJECT_INVALIDATED = 33,
/** Received an invalid response message from the server */
IED_ERROR_MALFORMED_MESSAGE = 34,
/** Service not implemented */
IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98,
/* unknown error */
IED_ERROR_UNKNOWN = 99
}
}
}