context: /*
   *  IEC61850ClientAPI.cs
   *
   *  Copyright 2014-2016 Michael Zillgith
   *
   *  This file is part of libIEC61850.
   *
   *  libIEC61850 is free software: you can redistribute it and/or modify
   *  it under the terms of the GNU General Public License as published by
   *  the Free Software Foundation, either version 3 of the License, or
   *  (at your option) any later version.
   *
   *  libIEC61850 is distributed in the hope that it will be useful,
   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *  GNU General Public License for more details.
   *
   *  You should have received a copy of the GNU General Public License
   *  along with libIEC61850.  If not, see <http://www.gnu.org/licenses/>.
   *
   *  See COPYING file for the complete license text.
   */
  using System;
  using System.Text;
  using System.Runtime.InteropServices;
  using System.Collections.Generic;
  using System.Collections;
  
  using IEC61850.Common;
  
  /// <summary>
  /// IEC 61850 API for the libiec61850 .NET wrapper library
  /// </summary>
  namespace IEC61850
  {
      /// <summary>
      /// IEC 61850 client API.
      /// </summary>
  	namespace Client
  	{
  
  		[StructLayout(LayoutKind.Sequential)]
  		public class MmsServerIdentity
  		{
  			public string vendorName;
  			public string modelName;
  			public string revision;
  		}
  
  		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);
  
  
  			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);
              }
  
  			public MmsServerIdentity GetServerIdentity ()
  			{
  				int mmsError;
  
  				if (self == IntPtr.Zero) {
  					throw new IedConnectionException("Pointer is Zero!");
  				}
  
  				IntPtr identity = MmsConnection_identify(self, out mmsError);
  
  				MmsServerIdentity serverIdentity = (MmsServerIdentity) 
  					Marshal.PtrToStructure(identity, typeof(MmsServerIdentity));
  
  				MmsServerIdentity_destroy(identity);
  
  				return serverIdentity;
  			}
  
  			public void SetLocalDetail(int localDetail) {
  				MmsConnection_setLocalDetail (self, localDetail);
  			}
  
  			public int GetLocalDetail() {
  				return MmsConnection_getLocalDetail (self);
  			}
  
  		}
  
  
  		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;
  			}
  
  		}
  
  		/// <summary>
  		/// Represents an entry of a log
  		/// </summary>
  		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<MmsJournalVariable> variables = null;
  
  			internal MmsJournalEntry(IntPtr self)
  			{
  				this.self = self;
  			}
  
  			public List<MmsJournalVariable> GetJournalVariables()
  			{
  				if (variables == null) {
  
  					IntPtr linkedList = MmsJournalEntry_getJournalVariables (self);
  
  					IntPtr element = LinkedList_getNext (linkedList);
  
  					variables = new List<MmsJournalVariable> ();
  
  					while (element != IntPtr.Zero) {
  						MmsJournalVariable journalVariable = new MmsJournalVariable (LinkedList_getData (element));
  
  						variables.Add (journalVariable);
  
  						element = LinkedList_getNext (element);
  					}
  				}
  
  				return variables;
  			}
  
  
  			public byte[] GetEntryID()
  			{
  				IntPtr mmsValuePtr = MmsJournalEntry_getEntryID (self);
  
  				MmsValue mmsValue = new MmsValue (mmsValuePtr);
  
  				byte[] octetString = mmsValue.getOctetString ();
  
  				return octetString;
  			}
  
  			public ulong GetOccurenceTime()
  			{
  				IntPtr mmsValuePtr = MmsJournalEntry_getOccurenceTime (self);
  
  				MmsValue mmsValue = new MmsValue (mmsValuePtr);
  
  				return mmsValue.GetBinaryTimeAsUtcMs ();
  			}
  
  			public void Dispose()
  			{
  				if (self != IntPtr.Zero) {
  					MmsJournalEntry_destroy (self);
  					self = IntPtr.Zero;
  				}
  			}
  
  			~MmsJournalEntry ()
  			{
  				Dispose ();
  			}
  		}
  
  		/// <summary>
  		/// This class acts as the entry point for the IEC 61850 client API. It represents a single
  		/// (MMS) connection to a server.
  		/// </summary>
  		public partial class IedConnection
  		{
  			/*************
              * 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 void IedConnection_destroy (IntPtr self);
  
              [DllImport ("iec61850", CallingConvention=CallingConvention.Cdecl)]
              static extern void IedConnection_setConnectTimeout(IntPtr self, UInt32 timeoutInMs);
  
  			[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_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, 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);
  
  			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 IntPtr IedConnection_createDataSet (IntPtr self, out int error, [MarshalAs(UnmanagedType.LPStr)] string dataSetReference, IntPtr dataSet);
  
  			[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
  			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 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, out bool moreFollows);
  
  			[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
  			static extern IntPtr IedConnection_queryLogByTime (IntPtr self, out int error, string logReference,
                    ulong startTime, ulong endTime, out bool moreFollows);
  
  
              /********************
              * 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;
  			private ConnectionClosedHandler userProvidedHandler = null;
  
  			public IedConnection ()
  			{
  				connection = IedConnection_create ();
  			}
  
  			/// <summary>
  			/// Releases all resource used by the <see cref="IEC61850.Client.IedConnection"/> object.
  			/// </summary>
  			/// <remarks>Call <see cref="Dispose"/> when you are finished using the <see cref="IEC61850.Client.IedConnection"/>. The
  			/// <see cref="Dispose"/> method leaves the <see cref="IEC61850.Client.IedConnection"/> in an unusable state. After
  			/// calling <see cref="Dispose"/>, you must release all references to the <see cref="IEC61850.Client.IedConnection"/>
  			/// so the garbage collector can reclaim the memory that the <see cref="IEC61850.Client.IedConnection"/> was occupying.</remarks>
  			public void Dispose()
  			{
  				if (connection != IntPtr.Zero) {
  					cleanupRCBs ();
  
  					IedConnection_destroy (connection);
  
  					connection = IntPtr.Zero;
  				}
  			}
  
              ~IedConnection ()
              {
  				if (connection != IntPtr.Zero) {
  					cleanupRCBs ();
  
  					IedConnection_destroy (connection);
  				}
              }
  
  			/// <summary>
  			/// Gets the connection parameters
  			/// </summary>
  			/// <returns>The connection parameters</returns>
  			public IsoConnectionParameters GetConnectionParameters ()
  			{
  				IntPtr mmsConnection = IedConnection_getMmsConnection(connection);
  
  				IntPtr parameters = MmsConnection_getIsoConnectionParameters(mmsConnection);
  
  				return new IsoConnectionParameters(parameters);
  			}
  
  			private void FreeHGlobaleDeleteFunction (IntPtr pointer)
  			{
  				Marshal.FreeHGlobal(pointer);
  			}
  
  
  			private UInt32 connectTimeout = 10000;
  
  			/// <summary>
  			/// Gets or sets the timeout used for connection attempts.
  			/// </summary>
  			/// <value>The connect timeout in milliseconds</value>
  			public UInt32 ConnectTimeout {
  				get {
  					return connectTimeout;
  				}
  				set {
  					connectTimeout = value;
  				}
  			}
  
  
  			public MmsConnection GetMmsConnection ()
  			{
  				IntPtr mmsConnectionPtr = IedConnection_getMmsConnection(connection);
  
  				return new MmsConnection(mmsConnectionPtr);
  			}
  
  			/// <summary>Establish an MMS connection to a server</summary>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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);
  			}
  
  			/// <summary>Establish an MMS connection to a server.</summary>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error.</exception>
  			public void Connect (string hostname)
  			{
  				Connect (hostname, 102);
  			}
  
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public ControlObject CreateControlObject (string objectReference)
  			{
  				ControlObject controlObject = new ControlObject (objectReference, connection, this);
  
  				return controlObject;
  			}
  
  
  		
  
  
  
  			/// <summary>
  			/// Updates the device model by quering the server.
  			/// </summary>
              public void UpdateDeviceModel()
              {
                  int error;
  
                  IedConnection_getDeviceModelFromServer(connection, out error);
  
                  if (error != 0)
                      throw new IedConnectionException("UpdateDeviceModel failed", error);
              }
  
  
  			/// <summary>
  			/// Gets the server directory (Logical devices or file objects)
  			/// </summary>
  			/// <returns>List of logical devices or files</returns>
  			/// <param name="fileDirectory">If set to <c>true</c> the file directory is returned, otherwise the LD names</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<string> GetServerDirectory (bool fileDirectory = false)
  			{
  				int error;
  
  				IntPtr linkedList = IedConnection_getServerDirectory (connection, out error, fileDirectory);
  
  				if (error != 0)
  					throw new IedConnectionException ("GetDeviceDirectory failed", error);
  
  				List<string> newList = new List<string> ();
  
  				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;
  			}
  
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<string> 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<string> newList = new List<string> ();
  
  				while (element != IntPtr.Zero) {
  					string ln = Marshal.PtrToStringAnsi (LinkedList_getData (element));
  
  					newList.Add (ln);
  
  					element = LinkedList_getNext (element);
  				}
  
  				LinkedList_destroy (linkedList);
  
  				return newList;
  			}
  
  			/// <summary>Get the directory of a logical node (LN)</summary>
  			/// <description>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, ...</description>
  			/// <param name="logicalNodeName">The object reference of a DO, SDO, or DA.</param>
  			/// <param name="acsiClass">the ACSI class of the requested directory elements.</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<string> 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<string> newList = new List<string> ();
  
  				while (element != IntPtr.Zero) {
  					string dataObject = Marshal.PtrToStringAnsi (LinkedList_getData (element));
  
  					newList.Add (dataObject);
  
  					element = LinkedList_getNext (element);
  				}
  
  				LinkedList_destroy (linkedList);
  
  				return newList;
  			}
  
  			/// <summary>Get a list of attributes (with functional constraints) of a DO, SDO, or DA</summary>
  			/// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<string> 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<string> newList = new List<string> ();
  
  				while (element != IntPtr.Zero) {
  					string dataObject = Marshal.PtrToStringAnsi (LinkedList_getData (element));
  
  					newList.Add (dataObject);
  
  					element = LinkedList_getNext (element);
  				}
  
  				LinkedList_destroy (linkedList);
  
  				return newList;
  			}
  
              /// <summary>Get the list of attributes with the specified FC of a DO, SDO, or DA</summary>
              /// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
              /// <param name="fc">Functional constraint</param>
              /// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
              public List<string> 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<string> newList = new List<string> ();
  
                  while (element != IntPtr.Zero) {
                      string dataObject = Marshal.PtrToStringAnsi (LinkedList_getData (element));
  
                      newList.Add (dataObject);
  
                      element = LinkedList_getNext (element);
                  }
  
                  LinkedList_destroy (linkedList);
  
                  return newList;
              }
  
  			/// <summary>Get a list of attributes (with functional constraints) of a DO, SDO, or DA</summary>
  			/// <description>This function is similar to the GetDataDirectory except that the returned element names
  			/// have the functional contraint (FC) appended.</description>
  			/// <param name="dataReference">The object reference of a DO, SDO, or DA.</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<string> 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<string> newList = new List<string> ();
  
  				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<MmsJournalEntry> WrapNativeLogQueryResult(IntPtr linkedList)
  			{
  				List<MmsJournalEntry> journalEntries = new List<MmsJournalEntry> ();
  
  				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;
  			}
  
  			/// <summary>
  			/// Queries all log entries after the entry with the given entryID and timestamp
  			/// </summary>
  			/// <returns>The list of log entries contained in the response</returns>
  			/// <param name="logRef">The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")</param>
  			/// <param name="entryID">EntryID of the last received MmsJournalEntry</param>
  			/// <param name="timestamp">Timestamp of the last received MmsJournalEntry</param>
  			/// <param name="moreFollows">Indicates that more log entries are available</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<MmsJournalEntry> 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);
  			}
  
  			/// <summary>
  			/// Queries all log entries of the given time range
  			/// </summary>
  			/// <returns>The list of log entries contained in the response</returns>
  			/// <param name="logRef">The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")</param>
  			/// <param name="startTime">Start time of the time range</param>
  			/// <param name="stopTime">End time of the time range</param>
  			/// <param name="moreFollows">Indicates that more log entries are available</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<MmsJournalEntry> 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);
  
  			}
  
  			/// <summary>
  			/// Queries all log entries of the given time range
  			/// </summary>
  			/// <returns>The list of log entries contained in the response</returns>
  			/// <param name="logRef">The object reference of the log (e.g. "simpleIOGenericIO/LLN0$EventLog")</param>
  			/// <param name="startTime">Start time of the time range</param>
  			/// <param name="stopTime">End time of the time range</param>
  			/// <param name="moreFollows">Indicates that more log entries are available</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			public List<MmsJournalEntry> 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);
  			}
  
  			/// <summary>Read the variable specification (type description of a DA or FDCO</summary>
  			/// <param name="objectReference">The object reference of a DA or FCDO.</param>
  			/// <param name="fc">The functional constraint (FC) of the object</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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;
  			}
  
  			/// <summary>Read the value of a data attribute (DA) or functional constraint data object (FCDO).</summary>
  			/// <param name="objectReference">The object reference of a DA or FCDO.</param>
  			/// <param name="fc">The functional constraint (FC) of the object</param>
  			/// <returns>the received value as an MmsValue instance</returns>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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;
  			}
  
  			/// <summary>Read the value of a basic data attribute (BDA) of type boolean.</summary>
  			/// <param name="objectReference">The object reference of a BDA.</param>
  			/// <param name="fc">The functional constraint (FC) of the object</param>
  			/// <returns>the received boolean value</returns>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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;
  			}
  
  			/// <summary>Read the value of a basic data attribute (BDA) of type float.</summary>
  			/// <param name="objectReference">The object reference of a BDA.</param>
  			/// <param name="fc">The functional constraint (FC) of the object</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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;
  			}
  
  			/// <summary>Read the value of a basic data attribute (BDA) of type string (VisibleString or MmsString).</summary>
  			/// <param name="objectReference">The object reference of a BDA.</param>
  			/// <param name="fc">The functional constraint (FC) of the object</param>
  			/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
  			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);
  
  
gt: 				string returnString = Marshal.PtrToStringAnsi (ptr);