// **********************************************************************************************************
//    coe.cs
//    6/1/2022
//    NGI - Next Generation Interceptor
//    
//    Contract No. HQ0856-21-C-0003/1022000209
//    
//    THIS DOCUMENT DOES NOT CONTAIN TECHNOLOGY OR TECHNICAL DATA CONTROLLED UNDER EITHER THE U.S. 
//    INTERNATIONAL TRAFFIC IN ARMS REGULATIONS OR THE U.S. EXPORT ADMINISTRATION REGULATIONS.
//    
//    RAYTHEON PROPRIETARY: THIS DOCUMENT CONTAINS DATA OR INFORMATION PROPRIETARY TO RAYTHEON 
//    COMPANY AND IS RESTRICTED TO USE ONLY BY PERSONS AUTHORIZED BY RAYTHEON COMPANY IN WRITING TO USE IT. 
//    DISCLOSURE TO UNAUTHORIZED PERSONS WOULD LIKELY CAUSE SUBSTANTIAL COMPETITIVE HARM TO RAYTHEON 
//    COMPANY'S BUSINESS POSITION. NEITHER SAID DOCUMENT NOR ITS CONTENTS SHALL BE FURNISHED OR DISCLOSED 
//    TO OR COPIED OR USED BY PERSONS OUTSIDE RAYTHEON COMPANY WITHOUT THE EXPRESS WRITTEN APPROVAL OF 
//    RAYTHEON COMPANY.
//    
//    UNPUBLISHED WORK - COPYRIGHT RAYTHEON COMPANY.
//    
//    DESTRUCTION NOTICE: FOR CLASSIFIED DOCUMENTS FOLLOW THE PROCEDURES IN DOD 5220.22-M, 
//    NATIONAL INDUSTRIAL SECURITY PROGRAM OPERATING MANUAL, FEBRUARY 2006, 
//    INCORPORATING CHANGE 1, MARCH 28, 2013, CHAPTER 5, SECTION 7, OR DODM 5200.01-VOLUME 3, 
//    DOD INFORMATION SECURITY PROGRAM: PROTECTION OF CLASSIFIED INFORMATION, ENCLOSURE 3, 
//    SECTION 17. FOR CONTROLLED UNCLASSIFIED INFORMATION FOLLOW THE PROCEDURES IN DODM 5200.01-VOLUME 4, 
//    INFORMATION SECURITY PROGRAM: CONTROLLED UNCLASSIFIED INFORMATION.
//    
//    CONTROLLED BY: MISSILE DEFENSE AGENCY
//    CONTROLLED BY: GROUND-BASED MIDCOURSE DEFENSE PROGRAM OFFICE
//    CUI CATEGORY: CTI 
//    DISTRIBUTION/DISSEMINATION CONTROL: F 
//    POC: Alex Kravchenko (1118268)
// **********************************************************************************************************
//\\<Unclassified>
//----------------------------------------------------------------------------//
//                                 UNCLASSIFIED                               //
//----------------------------------------------------------------------------//
//\\<\Unclassified>

//\\<UnlimitedRights>
//----------------------------------------------------------------------------//
// Copyright %(copyright)s Raytheon Company.                                  //
// This software was developed pursuant to Contract Number %(contractNumber)s //
// with the U.S. government. The U.S. government's rights in and to this      //
// copyrighted software are as specified in DFARS 252.227-7014 which was made //
// part of the above contract.                                                //
//----------------------------------------------------------------------------//
//\\<\UnlimitedRights>

//\\<EximUndetermined>
//----------------------------------------------------------------------------//
// WARNING - This document contains technical data and / or technology whose  //
// export or disclosure to Non-U.S. Persons, wherever located, is restricted  //
// by the International Traffic in Arms Regulations (ITAR) (22 C.F.R.         //
// Section 120-130) or the Export Administration Regulations (EAR) (15 C.F.R. //
// Section 730-774). This document CANNOT be exported (e.g., provided to a    //
// supplier outside of the United States) or disclosed to a Non-U.S. Person,  //
// wherever located, until a final jurisdiction and classification            //
// determination has been completed and approved by Raytheon, and any         //
// required U.S. Government approvals have been obtained. Violations are      //
// subject to severe criminal penalties.                                      //
//----------------------------------------------------------------------------//
//\\<\EximUndetermined>

using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace Raytheon.Instruments.coeCSharp
{
	public class coe
	{
		public const string coeDLL = "coeWindows-shared.dll";

		private IntPtr router { get; set; }
		public IntPtr Router => router;
		public IntPtr Database { get; set; }
		public IntPtr Protocol { get; set; }
		public bool IsConnected { get; set; }

		public string ProtocolName { get; set; }
		public string ProtocolCmitName { get; set; }

		public coeEndpoint _realTimeCtrlEndpoint = null;

		public const int OE_TRUE = 1;
		public const int OE_FALSE = 0;

		public const int OE_Do_Not_Wait = 0;
		public const int OE_Wait_Forever = -1;

		internal static coeDataInterchangePackets _dataInterchangePackets = new coeDataInterchangePackets();

		public enum Status : int
		{
			ERROR = -1,
			SUCCESS = 0,
			FAILED_INSUFFICIENT_RESOURCES = 1,
			FAILED_INTERNAL_ERROR = 2,
			FAILED_INVALID_ADDRESS = 3,
			FAILED_INVALID_NAME = 4,
			FAILED_INVALID_PARAMETER = 5,
			FAILED_MESSAGE_NOT_AVAILABLE = 6,
			FAILED_MESSAGE_NOT_RETURNABLE = 7,
			FAILED_MESSAGE_TOO_LARGE = 8,
			FAILED_NAME_NOT_FOUND = 9,
			FAILED_OBJECT_DELETED = 10,
			FAILED_OBJECT_NOT_FOUND = 11,
			FAILED_OBJECT_ALREADY_EXISTS = 12,
			FAILED_REQUEST_DENIED = 13,
			FAILED_TIMEOUT = 14,
			FAILED_INVALID_ASYNCH_ID = 15,
			FAILED_OPERATION_IN_PROGRESS = 16,
			FAILED_INVALID_ASSOCIATION = 17,
			FAILED_INVALID_HOST_FOR_CREATION = 18,
			FAILED_INVALID_SCOPE = 19,
			FAILED_INVALID_TRIGGER = 20,
			FAILED_LOG_OVERFLOW = 21,
			FAILED_INVALID_INDEX = 22,
			FAILED_INVALID_CLOCK_SOURCE = 23,
			FAILED_INVALID_CONVERSION = 24,
			FAILED_INVALID_DATE = 25,
			FAILED_INVALID_MONOTONIC = 26,
			FAILED_INCOMPATIBLE_FORMAT = 27,
			FAILED_TIMER_NOT_SET = 28,
			FAILED_OBJECT_NOT_TRACEABLE = 29,
			FAILED_FILE_ALREADY_EXISTS = 30,
			FAILED_FILE_DOES_NOT_EXIST = 31,
			FAILED_FILE_OPEN = 32,
			FAILED_INVALID_HANDLE = 33,
			FAILED_INVALID_MODE = 34,
			FAILED_INVALID_OPTIONS = 35,
			FAILED_INVALID_PATH = 36,
			FAILED_NOT_IMPLEMENTED = 55,
			FAILED_NOT_ENABLED = 56,
			FAILED_NOT_SUPPORTED = 57,
			FAILED_NOT_ENCRYPTED = 58,
			FAILED_VERIFY_ERROR = 59,
			FAILED_MESSAGE_IS_SIGNED = 60,
			FAILED_MESSAGE_IS_ENCRYPTED = 61,
			FAILED_PROTOCOL_OPERATION_PENDING = 62,
			FAILED_PROTOCOL_NO_MESSAGE_AVAILABLE = 63,
			FAILED_PROTOCOL_BUSY = 64
		};

		public enum ScopeType
		{
			OE_Local = 0,
			OE_Shared = 1,
			OE_Networked = 2
		};

		public enum DatabaseRegistrationType
		{
			REFERENCE_DATABASE_TRANSMIT_ALL = 0,
			REFERENCE_DATABASE_LOCAL_REGISTRATIONS = 1,
			REFERENCE_DATABASE_REMOTE_REGISTRATIONS = 2,
		};



		public enum MessageTypes : byte
		{
			PING = 0,
			PING_RESPONSE = 1,
			REGISTRATION_QUERY = 2,
			REGISTRATION_RESPONSE = 3,
			REGISTRATION_MESSAGE = 4,
			DISCONNECT_MESSAGE = 5,
			BINDING_MESSAGE = 6,
			LABELED_MESSAGE = 8,
		};

		public enum RegistrationType : byte
		{
			NONE = 0,
			REGISTRATION = 1,
			DEREGISTRATION = 2,
		};

		enum ELEMENT_TYPE
		{
			TYPE_CHAR = 0,
			TYPE_SHORT,
			TYPE_INT,
			TYPE_LONG,
			TYPE_LONG_LONG,
			TYPE_FLOAT,
			TYPE_DOUBLE,
			TYPE_STR,
			TYPE_NULL_STR
		};

		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		public delegate void RealTimeDisplayCallback(string value);


		[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
		public delegate IntPtr CallBackDelegate(uint Label);

		#region DLLImports

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "coe_version_Get_Version")]
		private static extern IntPtr coe_version_Get_Version();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Configure")]
		private static extern Status OE_Initialization_Configure(uint stacksize,
															   int priority,
															   int affinity,
															   uint systemClockPeriod);



		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Memory_Tracker_Configure")]
		public static extern Status OE_Memory_Tracker_Configure();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Memory_Tracker_Disable")]
		public static extern Status OE_Memory_Tracker_Disable();


		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Memory_Tracker_Display_Usage")]
		public static extern void OE_Memory_Tracker_Display_Usage();



		[DllImport(coe.coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Database_Query_Open")]
		private static extern uint OE_Router_Database_Query_Open(IntPtr db,
																 IntPtr query_data,
																 uint queryType,
																 ref IntPtr handle);

		[StructLayout(LayoutKind.Sequential)]
		public struct Destination
		{
			public uint type;
			public IntPtr destination;
			public uint label;
			public uint domain;
			public uint mediaAddress;
			public uint flags;
		};

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Database_Query_Next")]
		private static extern IntPtr OE_Router_Database_Query_Next(IntPtr handle);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Database_Query_Close")]
		private static extern uint OE_Router_Database_Query_Close(IntPtr handle);






		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "initialize_coe_router")]
		private static extern IntPtr initialize_coe_router();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "shutdown_coe_router")]
		private static extern Status shutdown_coe_router(IntPtr router);


		[StructLayout(LayoutKind.Sequential)]
		public unsafe struct PARSE_DATA_SECTION
		{
			public char* section_name;
			public uint list_count;
			public uint* list;
			public uint* next;
		};

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "add_config_item")]
		public static extern int add_config_item(IntPtr sect_list,
			string section_name,
			string element_name,
			string element_value);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_element")]
		private static extern int get_element(IntPtr sect_list,	string section,	string element,	ELEMENT_TYPE dt, IntPtr data, int param);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "delete_config_database")]
		public static extern int delete_config_database(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_router_params")]
		private static extern Status configure_router_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_database_params")]
		private static extern Status configure_database_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_tcp_media_binding_params")]
		private static extern Status configure_tcp_media_binding_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "tcp_media_binding_configure")]
		private static extern IntPtr tcp_media_binding_configure(IntPtr router);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "tcp_media_binding_shutdown")]
		private static extern Status tcp_media_binding_shutdown();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_udp_buffer_pools_params")]
		private static extern Status configure_udp_buffer_pools_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_udp_router_protocol_params")]
		private static extern Status configure_udp_router_protocol_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_udp_simple_registration_protocol_params")]
		private static extern Status configure_udp_simple_registration_protocol_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_udp_media_binding_params")]
		private static extern Status configure_udp_media_binding_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "udp_media_binding_configure")]
		private static extern IntPtr udp_media_binding_configure(IntPtr router);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "udp_media_binding_shutdown")]
		private static extern Status udp_media_binding_shutdown();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "configure_serial_media_binding_params")]
		private static extern Status configure_serial_media_binding_params(IntPtr sect_list);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "serial_media_binding_configure")]
		private static extern IntPtr serial_media_binding_configure(IntPtr router);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "serial_media_binding_shutdown")]
		private static extern Status serial_media_binding_shutdown();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Register_Protocol")]
		private static extern Status OE_Router_Register_Protocol(IntPtr router, IntPtr protocol, uint flags);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Deregister_Protocol")]
		private static extern Status OE_Router_Deregister_Protocol(IntPtr router, IntPtr protocol);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Shutdown")]
		private static extern Status OE_Initialization_Shutdown();

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Create_Database")]
		private static extern uint OE_Initialization_Create_Database(uint stackID,
																	 DatabaseRegistrationType registrationType,
																	 uint numberOfInitialDatabaseObjects,
																	 uint numberOfIncrementalDatabaseObjects,
																	 uint numberOfInitialQueries,
																	 uint numberOfIncrementalQueries,
																	 int doAllLabelsRegistration);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Create_Buffers")]
		private static extern uint OE_Initialization_Create_Buffers(uint stackID,
																	uint buffersInitialCount_1,
																	uint buffersSize_1,
																	uint buffersInitialCount_2,
																	uint buffersSize_2,
																	uint buffersInitialCount_3,
																	uint buffersSize_3,
																	uint buffersInitialCount_4,
																	uint buffersSize_4);



		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_UDP_Stack")]
		private static extern uint OE_Initialization_UDP_Stack(uint numberOfInitialDatabaseObjects,
															   uint numberOfIncrementalDatabaseObjects,
															   int doAllLabelsRegistration,
															   uint routerID,
															   uint port,
															   byte[] serverAddress,
															   uint maximumPacketSize,
															   uint registrationPacketSize,
															   uint buffersInitialCount_1,
															   uint buffersSize_1,
															   uint buffersInitialCount_2,
															   uint buffersSize_2,
															   uint buffersInitialCount_3,
															   uint buffersSize_3,
															   uint buffersInitialCount_4,
															   uint buffersSize_4,
															   uint UDPreceiveBufferSize);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Get_Router")]
		internal static extern IntPtr OE_Initialization_Get_Router(uint stackID);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Get_Database")]
		internal static extern IntPtr OE_Initialization_Get_Database(uint stackID);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "get_coe_router_database")]
		internal static extern IntPtr get_coe_router_database(IntPtr router);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Get_Protocol")]
		internal static extern IntPtr OE_Initialization_Get_Protocol(uint stackID);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Initialization_Get_Media_Binding")]
		internal static extern IntPtr OE_Initialization_Get_Media_Binding(uint stackID);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Router_Protocol_Extended_Routine")]
		private static extern Status OE_Router_Protocol_Extended_Routine(IntPtr oeObject,
																			  uint routineIdentifier,
																			  IntPtr routineParameters);

		[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetModuleHandle")]
		private static extern IntPtr GetModuleHandle(byte[] dllName);

		[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "GetProcAddress")]
		private static extern IntPtr GetProcAddress(IntPtr handle,
													byte[] routineName);

		[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, EntryPoint = "ZeroMemory")]
		private static extern IntPtr ZeroMemory(IntPtr buffer, int length);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Set_Timestamp_Function")]
		private static extern void OE_Set_Timestamp_Function(IntPtr routine);


		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Enable_Logger")]
		public static extern Status OE_Enable_Logger();



		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Set_Log_Level")]
		public static extern void OE_Set_Log_Level(int Level);

		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Get_Log_Level")]
		public static extern int OE_Get_Log_Level();


		[DllImport(coeDLL, CallingConvention = CallingConvention.Cdecl, EntryPoint = "OE_Set_Log_Callback")]
		public static extern Status OE_Set_Log_Callback(
			[MarshalAs(UnmanagedType.FunctionPtr)]
			RealTimeDisplayCallback callbackPointer
			);


		[DllImport("kernel32", CharSet = CharSet.None, ExactSpelling = false, SetLastError = true)]
		private static extern bool FreeLibrary(IntPtr hModule);

		public static void UnloadImportedDll(string dllName)
		{
			foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
			{
				if (module.FileName.EndsWith(dllName))
				{
					coe.FreeLibrary(module.BaseAddress);
				}
			}
		}

		#endregion DLLImports

		public static string GetVersion()
		{
			string versionString = Marshal.PtrToStringAnsi(coe_version_Get_Version());
			return versionString;
		}

		public static Status Configure(uint clockPeriod)
		{
			return OE_Initialization_Configure(4096, 0, 0, clockPeriod);
		}

		public static void Destroy()
		{

			try
			{
				OE_Initialization_Shutdown();
			}
			catch
			{
			}
		}

		public static Status CreateDatabase(uint stackID,
											DatabaseRegistrationType registrationType,
											uint numberOfInitialDatabaseObjects,
											uint numberOfIncrementalDatabaseObjects,
											uint numberOfInitialQueries,
											uint numberOfIncrementalQueries,
											bool doAllLabelsRegistration)
		{
			return (Status)OE_Initialization_Create_Database(stackID, registrationType, numberOfInitialDatabaseObjects, numberOfIncrementalDatabaseObjects,
																 numberOfInitialQueries, numberOfIncrementalQueries, doAllLabelsRegistration == true ? 1 : 0);
		}

		public static Status CreateBuffers(uint stackID,
										   uint buffersInitialCount_1,
										   uint buffersSize_1,
										   uint buffersInitialCount_2,
										   uint buffersSize_2,
										   uint buffersInitialCount_3,
										   uint buffersSize_3,
										   uint buffersInitialCount_4,
										   uint buffersSize_4)
		{
			return (Status)OE_Initialization_Create_Buffers(stackID, buffersInitialCount_1, buffersSize_1, buffersInitialCount_2, buffersSize_2,
																buffersInitialCount_3, buffersSize_3, buffersInitialCount_4, buffersSize_4);
		}



		public static Status CreateUdpStack(uint numberOfInitialDatabaseObjects,
											uint numberOfIncrementalDatabaseObjects,
											bool doAllLabelsRegistration,
											uint routerID,
											uint port,
											string serverAddress,
											uint maximumPacketSize,
											uint registrationPacketSize,
											uint buffersInitialCount_1,
											uint buffersSize_1,
											uint buffersInitialCount_2,
											uint buffersSize_2,
											uint buffersInitialCount_3,
											uint buffersSize_3,
											uint buffersInitialCount_4,
											uint buffersSize_4,
											uint UDPreceiveBufferSize)
		{
			byte[] serverString = Encoding.ASCII.GetBytes(serverAddress + 'x');
			serverString[serverString.Length - 1] = 0;
			return (Status)OE_Initialization_UDP_Stack(numberOfInitialDatabaseObjects,
														   numberOfIncrementalDatabaseObjects,
														   doAllLabelsRegistration == true ? 1 : 0,
														   routerID,
														   port,
														   serverString,
														   maximumPacketSize,
														   registrationPacketSize,
														   buffersInitialCount_1,
														   buffersSize_1,
														   buffersInitialCount_2,
														   buffersSize_2,
														   buffersInitialCount_3,
														   buffersSize_3,
														   buffersInitialCount_4,
														   buffersSize_4,
														   UDPreceiveBufferSize);
		}

		public static uint OE_Router_Database_Query_Open_(IntPtr db,
														  IntPtr query_data,
														  uint queryType,
														  ref IntPtr handle)
		{
			return OE_Router_Database_Query_Open(db, query_data, queryType, ref handle);
		}

		public static IntPtr OE_Router_Database_Query_Next_(IntPtr handle)
		{
			return OE_Router_Database_Query_Next(handle);
		}


		public static uint OE_Router_Database_Query_Close_(IntPtr handle)
		{
			return OE_Router_Database_Query_Close(handle);
		}



		public void SetConnected(bool state)
		{
			IsConnected = state;
		}

		public Status tcp_media_binding_configure(	uint port,                 //port
													char[] serverAddress,      //serverAddress
													uint maximumPacketSize,    //maximumPacketSize
													uint UDPtransmitBufferSize,//UDPtransmitBufferSize
													uint UDPreceiveBufferSize, //UDPreceiveBufferSize
													uint TCPtransmitBufferSize,//TCPtransmitBufferSize
													uint TCPreceiveBufferSize, //TCPreceiveBufferSize
													uint TCPoptionFlags,       //TCPoptionFlags
													Dictionary<string, List<KeyValuePair<string, string>>> options
												 )
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));
			uint tmp = 32;

			if (Configure(10000) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if (configure_router_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			if (configure_database_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "LOCAL_PORT", port.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "NUM_PORTS", tmp.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "NUM_DYNAMIC_NODES", tmp.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "SERVER_ADDRESS", new string(serverAddress));

			tmp = 0;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "UDP_TX_BUFFER_SIZE", tmp.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "UDP_RX_BUFFER_SIZE", UDPreceiveBufferSize.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "TCP_TX_BUFFER_SIZE", TCPtransmitBufferSize.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "TCP_RX_BUFFER_SIZE", TCPreceiveBufferSize.ToString());
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "PACKET_SIZE", maximumPacketSize.ToString());

			tmp = 1;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "TCP_SELECT_VALUE", tmp.ToString());

			tmp = 1;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "DISABLE_NAG_DELAY", tmp.ToString());

			tmp = 1000;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "TIMER_RATE", tmp.ToString());

			tmp = 1;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "CONNECT_KA_RATE", tmp.ToString());

			tmp = 1;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "", tmp.ToString());

			tmp = 1;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "RECV_KA_RATE", tmp.ToString());

			tmp = 4096;
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_STACK_SIZE", tmp.ToString());

			tmp = 0; /*port_THREAD_MIDDLE_PRIORITY*/
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_PRIORITY", tmp.ToString());

			tmp = 0; /*OE_THREAD_NO_AFFINITY*/
			add_config_item(plist, "TCP_MEDIA_BINDING_CONFIG", "RECV_THREAD_AFFINITY", tmp.ToString());

			if (configure_tcp_media_binding_params(plist) != Status.SUCCESS)
			{
				delete_config_database(plist);
				Marshal.FreeHGlobal(plist);
				return Status.FAILED_INTERNAL_ERROR;
			}

			delete_config_database(plist);
			Marshal.FreeHGlobal(plist);
			Protocol = tcp_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)  // protocol API error returns -1
			{
				// if media binding start up fails, media binding shutdown is done
				// inside coe framework

				if (shutdown_coe_router(router) != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				if (OE_Initialization_Shutdown() != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				return Status.FAILED_INTERNAL_ERROR;
			}

			ProtocolName = "REMOTE";
			ProtocolCmitName = "" + new string(serverAddress) + ": " + port.ToString();

			if (OE_Router_Register_Protocol(router, Protocol, 0x02) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			return Status.SUCCESS;
		}

		public Status tcp_media_binding_configure(Dictionary<string, List<KeyValuePair<string, string>>> options, ILogger logger = null)
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			Status status;

			if ((status = Configure(10000)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to run OE_Initialization_Configure, status {status}");
				return status;
			}

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if ((status = configure_router_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure router params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_database_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure database params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to initialize coe router");
				return Status.FAILED_INTERNAL_ERROR;
			}

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to get coe router database");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_tcp_media_binding_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure TCP media binding params, status {status}");

				delete_config_database(plist);
				Marshal.FreeHGlobal(plist);
				return Status.FAILED_INTERNAL_ERROR;
			}

			try
			{
				delete_config_database(plist);
				Marshal.FreeHGlobal(plist);
			}
			catch (Exception ex)
			{
				logger?.Error(ex, $"Unable delete_config_database and FreeHGlobal");
			}

			Protocol = tcp_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)  // protocol API error returns -1
			{
				logger?.Error("Unable to configure TCP media binding");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			var tcpParams = options["TCP_MEDIA_BINDING_CONFIG"];
			var serverAddress = tcpParams.FirstOrDefault(l => l.Key == "SERVER_ADDRESS").Value;
			var port = tcpParams.FirstOrDefault(l => l.Key == "LOCAL_PORT").Value;

			ProtocolName = "REMOTE";
			ProtocolCmitName = $"{serverAddress}: {port}";

			if ((status = OE_Router_Register_Protocol(router, Protocol, 0x02)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to register TCP router protocol, status {status}");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			return Status.SUCCESS;
		}

		public Status TCP_media_binding_shutdown(ILogger logger = null)
		{
			Status status;
			if ((status = OE_Router_Deregister_Protocol(router, Protocol)) != Status.SUCCESS)
				logger?.Error($"TCP OE_Router_Deregister_Protocol error, status {status}");

			if ((status = tcp_media_binding_shutdown()) != Status.SUCCESS)
				logger?.Error($"TCP tcp_media_binding_shutdown error, status {status}");

			if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				logger?.Error($"TCP shutdown_coe_router error, status {status}");

			if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				logger?.Error($"TCP OE_Initialization_Shutdown error, status {status}");

			return status;
		}


		public Status udp_media_binding_configure(Dictionary<string, List<KeyValuePair<string, string>>> options, ILogger logger = null)
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			Status status;

			if ((status = Configure(10000)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to run OE_Initialization_Configure, status {status}");
				return status;
			}

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if ((status = configure_router_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure router params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_database_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure database params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to initialize coe router");
				return Status.FAILED_INTERNAL_ERROR;
			}

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to get coe router database");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_udp_buffer_pools_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure udp buffer pools params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_udp_router_protocol_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure udp router protocol params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_udp_simple_registration_protocol_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure udp simple registration protocol params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_udp_media_binding_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure UDP media binding params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			try
			{
				delete_config_database(plist);
				Marshal.FreeHGlobal(plist);
			}
			catch (Exception ex)
			{
				logger?.Error(ex, $"Unable delete_config_database and FreeHGlobal");
			}

			Protocol = udp_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)  // protocol API error returns -1
			{
				logger?.Error("Unable to configure UDP media binding");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			var udpParams = options["UDP_MEDIA_BINDING_CONFIG"];
			var remote_address = udpParams.FirstOrDefault(l => l.Key == "REMOTE_IP_ADDRESS").Value;
			var r_send_port = udpParams.FirstOrDefault(l => l.Key == "REMOTE_SEND_PORT").Value;
			var r_recv_port = udpParams.FirstOrDefault(l => l.Key == "REMOTE_RECV_PORT").Value;
			var local_address = udpParams.FirstOrDefault(l => l.Key == "LOCAL_IP_ADDRESS").Value;
			var l_send_port = udpParams.FirstOrDefault(l => l.Key == "LOCAL_SEND_PORT").Value;
			var l_recv_port = udpParams.FirstOrDefault(l => l.Key == "LOCAL_RECV_PORT").Value;

			ProtocolName = $"{remote_address}:{r_send_port}:{r_recv_port}";
			ProtocolCmitName = $"{local_address}:{l_send_port}:{l_recv_port}";

			if ((status = OE_Router_Register_Protocol(router, Protocol, 0)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to register UDP router protocol, status {status}");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			return Status.SUCCESS;
		}

		public Status udp_media_binding_configure(
			char[] local_address,
			char[] remote_address,
			uint l_send_port,
			uint l_recv_port,
			uint r_send_port,
			uint r_recv_port,
			uint recv_timeout,
			Dictionary<string, List<KeyValuePair<string, string>>> options)
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			if (Configure(10000) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if (configure_router_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			if (configure_database_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "LOCAL_IP_ADDRESS", new string(local_address));
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "REMOTE_IP_ADDRESS", new string(remote_address));
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "LOCAL_SEND_PORT", l_send_port.ToString());
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "LOCAL_RECV_PORT", l_recv_port.ToString());
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "REMOTE_SEND_PORT", r_send_port.ToString());
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "REMOTE_RECV_PORT", r_recv_port.ToString());
			add_config_item(plist, "UDP_MEDIA_BINDING_CONFIG", "RECV_TIMEOUT", recv_timeout.ToString());

			configure_udp_buffer_pools_params(plist);

			if (configure_udp_router_protocol_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			if (configure_udp_simple_registration_protocol_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			if (configure_udp_media_binding_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			delete_config_database(plist);
			Marshal.FreeHGlobal(plist);
			Protocol = udp_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)  // protocol API error returns -1
			{
				// if media binding start up fails, media binding shutdown is done
				// inside coe framework

				if (shutdown_coe_router(router) != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				if (OE_Initialization_Shutdown() != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				return Status.FAILED_INTERNAL_ERROR;
			}

			ProtocolName = "" + new string(remote_address) + ": " + r_send_port.ToString() + ":" + r_recv_port.ToString();
			ProtocolCmitName = "" + new string(local_address) + ": " + l_send_port.ToString() + ":" + l_recv_port.ToString();

			if (OE_Router_Register_Protocol(router, Protocol, 0) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			return Status.SUCCESS;
		}

		public Status UDP_media_binding_shutdown(ILogger logger = null)
		{
			Status status;

			if ((status = OE_Router_Deregister_Protocol(router, Protocol)) != Status.SUCCESS)
				logger?.Error($"UDP OE_Router_Deregister_Protocol error, status {status}");

			if ((status = udp_media_binding_shutdown()) != Status.SUCCESS)
				logger?.Error($"UDP udp_media_binding_shutdown error, status {status}");

			if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				logger?.Error($"UDP shutdown_coe_router error, status {status}");

			if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				logger?.Error($"UDP OE_Initialization_Shutdown error, status {status}");

			return Status.SUCCESS;
		}

		public Status serial_media_binding_configure(Dictionary<string, List<KeyValuePair<string, string>>> options, ILogger logger = null)
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			Status status;

			if ((status = Configure(10000)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to run OE_Initialization_Configure, status {status}");
				return status;
			}

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if ((status = configure_router_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure router params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_database_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure database params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to initialize coe router");
				return Status.FAILED_INTERNAL_ERROR;
			}

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to get coe router database");
				return Status.FAILED_INTERNAL_ERROR;
			}

			if ((status = configure_serial_media_binding_params(plist)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to configure serial media binding params, status {status}");
				return Status.FAILED_INTERNAL_ERROR;
			}

			try
			{
				delete_config_database(plist);
				Marshal.FreeHGlobal(plist);
			}
			catch (Exception ex)
			{
				logger?.Error(ex, $"Unable delete_config_database and FreeHGlobal");
			}

			Protocol = serial_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)
			{
				logger?.Error("Unable to configure serial media binding");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			var serialParams = options["SERIAL_MEDIA_BINDING_CONFIG"];
			var port = serialParams.FirstOrDefault(l => l.Key == "DEVICE_NAME").Value;

			ProtocolName = "REMOTE";
			ProtocolCmitName = $"CMIT: {port}";

			if ((status = OE_Router_Register_Protocol(router, Protocol, 0)) != Status.SUCCESS)
			{
				logger?.Error($"Unable to register router protocol, status {status}");

				// if media binding start up fails, media binding shutdown is done 
				// inside coe framework 
				if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				{
					logger?.Error($"Unable shutdown coe router, status {status}");
				}

				if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				{
					logger?.Error($"OE_Initialization_Shutdown error, status {status}");
				}

				return Status.FAILED_INTERNAL_ERROR;
			}

			return Status.SUCCESS;
		}

		public Status serial_media_binding_configure(char[] port,
															uint baudrate,
															uint databits,
															uint stopbits,
															uint parity,
															uint flowcontrol,
															uint recv_timeout,
															Dictionary<string, List<KeyValuePair<string, string>>> options)
		{
			IntPtr plist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			if (Configure(10000) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			ZeroMemory(plist, Marshal.SizeOf(typeof(PARSE_DATA_SECTION)));

			foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> s in options)
			{
				foreach (KeyValuePair<string, string> v in s.Value)
				{
					add_config_item(plist, s.Key, v.Key, v.Value);
				}
			}

			if (configure_router_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			if (configure_database_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			router = initialize_coe_router();
			if (router == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			Database = get_coe_router_database(router);
			if (Database == (IntPtr)Status.ERROR)
				return Status.FAILED_INTERNAL_ERROR;

			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "DEVICE_NAME", port.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "BAUD_RATE", baudrate.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "DATA_BITS", databits.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "STOP_BITS", stopbits.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "PARITY", parity.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "FLOW_CONTROL", flowcontrol.ToString());
			add_config_item(plist, "SERIAL_MEDIA_BINDING_CONFIG", "RECV_PROCESSING_DELAY", recv_timeout.ToString());

			if (configure_serial_media_binding_params(plist) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			delete_config_database(plist);
			Marshal.FreeHGlobal(plist);
			Protocol = serial_media_binding_configure(router);
			if (Protocol == (IntPtr)Status.ERROR)
			{
				// if media binding start up fails, media binding shutdown is done
				// inside coe framework

				if (shutdown_coe_router(router) != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				if (OE_Initialization_Shutdown() != Status.SUCCESS)
					return Status.FAILED_INTERNAL_ERROR;

				return Status.FAILED_INTERNAL_ERROR;
			}

			ProtocolName = "REMOTE";
			ProtocolCmitName = "CMIT: " + new string(port);

			if (OE_Router_Register_Protocol(router, Protocol, 0) != Status.SUCCESS)
				return Status.FAILED_INTERNAL_ERROR;

			return Status.SUCCESS;
		}

		public Status SERIAL_media_binding_shutdown(ILogger logger = null)
		{
			Status status;
			if ((status = OE_Router_Deregister_Protocol(router, Protocol)) != Status.SUCCESS)
				logger?.Error($"Serial OE_Router_Deregister_Protocol error, status {status}");

			if ((status = serial_media_binding_shutdown()) != Status.SUCCESS)
				logger?.Error($"Serial serial_media_binding_shutdown error, status {status}");

			if ((status = shutdown_coe_router(router)) != Status.SUCCESS)
				logger?.Error($"Serial shutdown_coe_router error, status {status}");

			if ((status = OE_Initialization_Shutdown()) != Status.SUCCESS)
				logger?.Error($"Serial OE_Initialization_Shutdown error, status {status}");

			return status;
		}

		struct AddressParametersStructure
		{
			internal uint mediaAddress;
			internal IntPtr bufferAddress;
			internal uint bufferSize;
		}

		public static string DiagnosticMediaAddressToString(uint stackID,
															uint mediaAddress)
		{
			if (mediaAddress == 0xFFFFFFFF)
			{
				string IPString = "ALL";
				return IPString;
			}
			else
			{
				IntPtr bufferPointer = Marshal.AllocHGlobal(32);
				AddressParametersStructure parameters;
				parameters.mediaAddress = mediaAddress;
				parameters.bufferSize = 32;
				parameters.bufferAddress = bufferPointer;
				IntPtr parametersPointer = Marshal.AllocHGlobal(Marshal.SizeOf(parameters));

				Marshal.StructureToPtr(parameters, parametersPointer, false);
				OE_Router_Protocol_Extended_Routine(OE_Initialization_Get_Media_Binding(stackID), 0, parametersPointer);
				string IPstring = Marshal.PtrToStringAnsi(bufferPointer);

				Marshal.FreeHGlobal(parametersPointer);
				Marshal.FreeHGlobal(bufferPointer);
				return IPstring;
			}
		}
		struct ConnectionsParametersStructure
		{
			internal int numberOfBuffers;
			internal IntPtr bufferAddress;
			internal int bufferSize;
		}

		public static int DiagnosticConnections(uint stackID,
												string[] results)
		{
			IntPtr bufferPointer = Marshal.AllocHGlobal(results.Length * 32);
			ConnectionsParametersStructure parameters;
			parameters.numberOfBuffers = results.Length;
			parameters.bufferSize = 32;
			parameters.bufferAddress = bufferPointer;
			IntPtr parametersPointer = Marshal.AllocHGlobal(Marshal.SizeOf(parameters));

			Marshal.StructureToPtr(parameters, parametersPointer, false);
			IntPtr mediaBinding = OE_Initialization_Get_Media_Binding(stackID);
			OE_Router_Protocol_Extended_Routine(mediaBinding, 1, parametersPointer);
			ConnectionsParametersStructure returnValues;
			returnValues = (ConnectionsParametersStructure)Marshal.PtrToStructure(parametersPointer, typeof(ConnectionsParametersStructure));
			for (int index = 0; index < returnValues.numberOfBuffers; index++)
			{
				results[index] = Marshal.PtrToStringAnsi(new IntPtr((bufferPointer.ToInt64() + (index * 32))));
			}

			Marshal.FreeHGlobal(parametersPointer);
			Marshal.FreeHGlobal(bufferPointer);
			return returnValues.numberOfBuffers;
		}
		//
		//
		//
		// Time Tagging
		//
		//
		//
		public enum TimeTaggingType { NONE, SYSTEM_TIMER_NANOSECONDS, SYSTEM_TIMER_FRACTIONS, LPT };
		public static void setTimeTagging(TimeTaggingType tagType)
		{
			IntPtr routineAddress = IntPtr.Zero;
			byte[] coeDllName = Encoding.ASCII.GetBytes(coeDLL + 'x');
			coeDllName[coeDllName.Length - 1] = 0;
			IntPtr dllHandle = GetModuleHandle(coeDllName);
			switch (tagType)
			{
				case TimeTaggingType.NONE:
					break;
				case TimeTaggingType.SYSTEM_TIMER_NANOSECONDS:
					{
						byte[] coeRoutineName = Encoding.ASCII.GetBytes("OE_Clock_Handler_Get_System_Time" + 'x');
						coeRoutineName[coeRoutineName.Length - 1] = 0;
						routineAddress = GetProcAddress(dllHandle, coeRoutineName);
					}
					break;
				case TimeTaggingType.SYSTEM_TIMER_FRACTIONS:
					{
						byte[] coeRoutineName = Encoding.ASCII.GetBytes("OE_Clock_Handler_Get_System_Time_With_Fractions" + 'x');
						coeRoutineName[coeRoutineName.Length - 1] = 0;
						routineAddress = GetProcAddress(dllHandle, coeRoutineName);
					}
					break;
				case TimeTaggingType.LPT:
					{
						byte[] coeRoutineName = Encoding.ASCII.GetBytes("Precision_Time_Get_COE_MsgHdr_Timestamp" + 'x');
						coeRoutineName[coeRoutineName.Length - 1] = 0;
						routineAddress = GetProcAddress(dllHandle, coeRoutineName);
					}
					break;
			}
			OE_Set_Timestamp_Function(routineAddress);
		}
	}
}
