using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;

namespace ModbusLibrary
{
    public class ErrorEventArgs : EventArgs
    {
        UInt16 FErrorCode;
        String FMessage;
        public ErrorEventArgs(UInt16 ErrorCode, String Message)
        {
            this.FErrorCode = ErrorCode;
            this.FMessage = Message;
        }
        public UInt16 ErrorCode
        {
            get { return FErrorCode; }
        }
        public String Message
        {
            get { return FMessage; }
        }
    }

    public static class Modbus
    {
        public enum Chyby: byte{ES_ERROR_FUNCCODE = 1, ES_ERROR_ADDRESS = 2, ES_ERROR_DATA = 3, ES_ERROR_EXECUTION = 4,
            ES_NEW_PARAMETERS = 2, ES_ESTABLISH_SOCKET = 14, ES_ESTABLISH_RUNNING = 16,
            ES_ESTABLISH_NAMING = 17, ES_ESTABLISH_LISTENING = 18, ES_ESTABLISH_TREAD = 19, ES_HANDLING_ACCEPT = 20,
            ES_SEND_CONNECTION = 21, ES_SEND_PROBLEM = 22, ES_READ_CONNECTION = 23, ES_READ_PROBLEM = 24,
            ES_SHUTDOWN_STOPPED = 25, ES_RESPONSE_EXCEPTION = 26, ES_REQUEST_EXCEPTION = 27, ES_REQUEST_DISCONNECT = 28,
            EC_NEW_PARAMETERS = 1, EC_CONNECT_HOST = 4, EC_CONNECT_SOCKET = 5, EC_CONNECT_CONNECTED = 6, EC_CONNECT_PROBLEM = 7,
            EC_DISCONNECT_DISCONNECTED = 8, EC_SENDING_DISCONNECTED = 9, EC_SENDING_PROBLEM = 10, EC_READING_DISCONNECTED = 11,
            EC_READING_PROBLEM = 12, EC_REQUEST_EXCEPTION = 13, EC_ANSWER_EXCEPTION = 29, EC_ANSWER_DISCONNECT = 30,
            EC_SERVER_ERROR = 31, EC_SERVER_ERROR_FUNCCODE = EC_SERVER_ERROR + 1,
            EC_SERVER_ERROR_ADDRESS = EC_SERVER_ERROR + 2,
            EC_SERVER_ERROR_DATA = EC_SERVER_ERROR + 3,
            EC_SERVER_ERROR_EXECUTION = EC_SERVER_ERROR + 4, EC_PARSE_ERROR = 40, EL_INITIALIZE = 3, EC_CHANGEIP_ERROR = 41,
            ES_CHANGEDATA_ERROR = 42, NO_ERRORS = 0, EL_INVALID_CREATION = 100};

        [DllImport("ModbusLibrary.dll", EntryPoint = "GetErrorMessage", CharSet = CharSet.Unicode)]
        private static unsafe extern char* DllGetErrorMessage(UInt16 ErrorCode);

        [DllImport("ModbusLibrary.dll", EntryPoint = "LastClientError", CharSet = CharSet.Unicode)]
        private static extern Int32 DllLastClientError(IntPtr Client);

        [DllImport("ModbusLibrary.dll", EntryPoint = "LastServerError", CharSet = CharSet.Unicode)]
        private static extern Int32 DllLastServerError(IntPtr Server);

        public static String GetErrorMessage(UInt16 ErrorCode)
        {
            String result = "";
            try
            {
                unsafe
                {
                    result = Marshal.PtrToStringAnsi((IntPtr)DllGetErrorMessage(ErrorCode));
                }
            }
            catch (Exception e)
            {
                throw e;
            }
            return result;
        }

        public static UInt16 LastServerError(IntPtr Server)
        {
            UInt16 result = 0;
            try
            {
                result = (UInt16)DllLastServerError(Server);
            }
            catch (Exception e)
            {
                throw e;
            }
            return result;
        }
        public static UInt16 LastClientError(IntPtr Client)
        {
            UInt16 result = 0;
            try
            {
                result = (UInt16)DllLastClientError(Client);
            }
            catch (Exception e)
            {
                throw e;
            }
            return result;
        }
    }
    public class ModbusClient
    {
        [DllImport("ModbusLibrary.dll", EntryPoint = "NewClient", CharSet = CharSet.Unicode)]
        protected static extern IntPtr DllNewClient([MarshalAs(UnmanagedType.LPStr)]String IP, UInt16 Port);

        [DllImport("ModbusLibrary.dll", EntryPoint = "DestroyClient", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllDestroyClient(IntPtr Client);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ConnectClient", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllConnectClient(IntPtr Client);

        [DllImport("ModbusLibrary.dll", EntryPoint = "DisconnectClient", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllDisconnectClient(IntPtr Client);

        [DllImport("ModbusLibrary.dll", EntryPoint = "WriteCoil", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllWriteCoil(IntPtr client, UInt16 number, Boolean stateon);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadCoil", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadCoil(IntPtr client, UInt16 number, Boolean* stateon);
        
        [DllImport("ModbusLibrary.dll", EntryPoint = "WriteHoldingRegister", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllWriteHoldingRegister(IntPtr client, UInt16 number, UInt16 value);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadHoldingRegister", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadHoldingRegister(IntPtr client, UInt16 number, UInt16* value);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadCoils", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadCoils(IntPtr client, UInt16 from, UInt16 count, Boolean** stateons);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadHoldingRegisters", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadHoldingRegisters(IntPtr client, UInt16 from, UInt16 count, UInt16** values);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadDiscreteInputs", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadDiscreteInputs(IntPtr client, UInt16 from, UInt16 count, Boolean** stateons);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ReadInputRegisters", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllReadInputRegisters(IntPtr client, UInt16 from, UInt16 count, UInt16** values);

        [DllImport("ModbusLibrary.dll", EntryPoint = "WriteCoils", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllWriteCoils(IntPtr client, UInt16 from, UInt16 count, Boolean[] stateon);

        [DllImport("ModbusLibrary.dll", EntryPoint = "WriteHoldingRegisters", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllWriteHoldingRegisters(IntPtr client, UInt16 from, UInt16 count, UInt16[] values);

        [DllImport("ModbusLibrary.dll", EntryPoint = "GetRWBuffer", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllGetRWBuffer(IntPtr client, byte** rbuffer, byte** wbuffer, int* rbuffersize, int* wbuffersize);

        private IntPtr FHandle = IntPtr.Zero;
        public event EventHandler OnError = null;
        private bool FErrorEventInvalid = false;
        private Boolean FMomentumError = false;

        public bool EventInvalidCreation
        {
            get
            {
                return FErrorEventInvalid;
            }
            set
            {
                FErrorEventInvalid = value;
            }
        }

        protected virtual void DoOnError(EventArgs e)
        {
            ErrorEventArgs chyba = (ErrorEventArgs)e;
            FMomentumError = true;
            if ((FErrorEventInvalid == false) && (chyba.ErrorCode == (UInt16)Modbus.Chyby.EL_INVALID_CREATION)){}
            else if (OnError != null)
                OnError(this, e);
        }

        public ModbusClient(String IP, UInt16 Port)
        {
            UInt16 errorCode = 0;
            try
            {
                FHandle = DllNewClient(IP, Port);
                OnError = null;
                FErrorEventInvalid = false;
                errorCode = (UInt16)Modbus.LastClientError(FHandle);
            }
            catch (Exception e)
            {
                FHandle = IntPtr.Zero;
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                FHandle = IntPtr.Zero;
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public ModbusClient()
            :this("127.0.0.1", 502)
        {
        }

        ~ModbusClient()
        {
            DestroyClient();
            FHandle = IntPtr.Zero;
            OnError = null;
        }

        public void DisconnectClient()
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllDisconnectClient(FHandle);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ConnectClient()
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllConnectClient(FHandle);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        private void DestroyClient()
        {
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllDestroyClient(FHandle);
                    FHandle = IntPtr.Zero;
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
        }

        public void WriteCoil(UInt16 Number, Boolean StateOn)
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllWriteCoil(FHandle, Number, StateOn);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadCoil(UInt16 Number, ref Boolean StateOn)
        {
            UInt16 errorCode = 0;
            Boolean tmpState = false;
            StateOn = false;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        DllReadCoil(FHandle, Number, &tmpState);
                    }
                    StateOn = tmpState;
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void WriteHoldingRegister(UInt16 Number, UInt16 Value)
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllWriteHoldingRegister(FHandle, Number, Value);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadHoldingRegister(UInt16 Number, ref UInt16 Value)
        {
            UInt16 errorCode = 0;
            UInt16 tmpValue = 0;
            Value = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        DllReadHoldingRegister(FHandle, Number, &tmpValue);
                    }
                    Value = tmpValue;
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadCoils(UInt16 From, UInt16 Count, ref Boolean[] StateOns)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            if (StateOns == null)
                StateOns = new Boolean[Count];
            for (i = 0; i < Count; i++)
                StateOns[i] = false;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        bool* tmpStates = (bool*)(Marshal.AllocHGlobal(sizeof(bool) * Count)).ToPointer();
                        DllReadCoils(FHandle, From, Count, &tmpStates);
                        for (i = 0; i < Count; i++)
                            StateOns[i] = tmpStates[i];
                        Marshal.FreeHGlobal((IntPtr)tmpStates);
                    }
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadHoldingRegisters(UInt16 From, UInt16 Count, ref UInt16[] Values)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            if (Values == null)
            {
                Values = new UInt16[Count];
            }
                for (i = 0; i < Count; i++)
                {
                    Values.SetValue((UInt16)0, i);
                }
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        ushort* MMparams = (ushort*)(Marshal.AllocHGlobal(sizeof(UInt16) * 12)).ToPointer(); 
                        DllReadHoldingRegisters(FHandle, From, Count, &MMparams);
                        for (i = 0; i < Count; i++)
                            Values.SetValue((UInt16)MMparams[i], i);
                        Marshal.FreeHGlobal((IntPtr)MMparams);
                    }
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadDiscreteInputs(UInt16 From, UInt16 Count, ref Boolean[] StateOns)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            if (StateOns == null)
                StateOns = new Boolean[Count];
            for (i = 0; i < Count; i++)
                StateOns[i] = false;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        bool* tmpStates = (bool*)(Marshal.AllocHGlobal(sizeof(bool) * Count)).ToPointer();
                        DllReadDiscreteInputs(FHandle, From, Count, &tmpStates);
                        for (i = 0; i < Count; i++)
                            StateOns[i] = tmpStates[i];
                        Marshal.FreeHGlobal((IntPtr)tmpStates);
                    }
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ReadInputRegisters(UInt16 From, UInt16 Count, ref UInt16[] Values)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            if (Values == null)
                Values = new UInt16[Count];
            for (i = 0; i < Count; i++)
                Values[i] = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        ushort* tmpValues = (ushort*)(Marshal.AllocHGlobal(sizeof(UInt16) * Count)).ToPointer();
                        DllReadInputRegisters(FHandle, From, Count, &tmpValues);
                        for (i = 0; i < Count; i++)
                            Values[i] = tmpValues[i];
                        Marshal.FreeHGlobal((IntPtr)tmpValues);
                    }
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void WriteCoils(UInt16 From, UInt16 Count, Boolean[] StateOns)
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllWriteCoils(FHandle, From, Count, StateOns);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void WriteHoldingRegisters(UInt16 From, UInt16 Count, UInt16[] Values)
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllWriteHoldingRegisters(FHandle, From, Count, Values);
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }
        public Boolean RefreshMomentum(Boolean[] DigitalOutputs, UInt16[] AnalogOutputs,
            ref Boolean[] DigitalInputs, ref UInt16[] AnalogInputs)
        {
            UInt16 hodnotaVystupu = 0;
            UInt16 hodnotaVstupu = 0;
            UInt16 i = 0;
            UInt16[] values = new UInt16[12];
            FMomentumError = false;

            //for (i=0; i<12; i++)
                //values.SetValue((UInt16)0, i);

            if ((DigitalOutputs.Length != 8) || (AnalogOutputs.Length != 4) || (DigitalInputs.Length != 8)
                || (AnalogInputs.Length != 6))
                DoOnError(new ErrorEventArgs(0, "RefresMomentum: Wrong sizes of parameters."));

            for (i=0; i<8; i++)
                if ((Boolean)DigitalOutputs.GetValue(i) == true) hodnotaVystupu |= (UInt16)(1 << i);

            values.SetValue((UInt16)32768, 0);
            values.SetValue((UInt16)32768, 1);
            values.SetValue((UInt16)255, 2);
            values.SetValue((UInt16)0, 3);
            values.SetValue((UInt16)0, 4);
            values.SetValue((UInt16)0, 5);
            values.SetValue((UInt16)0, 6);
            values.SetValue((UInt16)hodnotaVystupu, 7);
            values.SetValue((UInt16)(((UInt16)AnalogOutputs.GetValue(0)) & (UInt16)32766), 8);
            values.SetValue((UInt16)(((UInt16)AnalogOutputs.GetValue(1)) & (UInt16)32766), 9);
            values.SetValue((UInt16)(((UInt16)AnalogOutputs.GetValue(2)) & (UInt16)32766), 10);
            values.SetValue((UInt16)(((UInt16)AnalogOutputs.GetValue(3)) & (UInt16)32766), 11);

            WriteHoldingRegisters(1, 12, values);
            
            //for (i=0; i<12; i++)
            //    values.SetValue((UInt16)0, i);
            ReadHoldingRegisters(1, 12, ref values);

            hodnotaVstupu = (UInt16)values.GetValue(1);
            for (i=0; i<8; i++)
                DigitalInputs.SetValue((Boolean)((hodnotaVstupu & (1 << i)) == (1 << i)), i);

            for (i=0; i<6; i++)
                AnalogInputs.SetValue((UInt16)values.GetValue(2+i), i);

            return FMomentumError;
        }

        public void GetRWBuffer(out String SendMessage, out String ReadMessage)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            UInt16 msgsize = 260;
            StringBuilder sndmessage = new StringBuilder();
            StringBuilder recvmessage = new StringBuilder();
            SendMessage = "";
            ReadMessage = "";
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        byte* rbuf = (byte*)(Marshal.AllocHGlobal(sizeof(byte) * msgsize)).ToPointer();
                        byte* wbuf = (byte*)(Marshal.AllocHGlobal(sizeof(byte) * msgsize)).ToPointer();
                        int rbufsize = 0;
                        int wbufsize = 0;
                        DllGetRWBuffer(FHandle, &rbuf, &wbuf, &rbufsize, &wbufsize);

                        for (i = 0; i < rbufsize; i++)
                            recvmessage.Append("0x"+(rbuf[i]).ToString("X2") + " ");
                        for (i = 0; i < wbufsize; i++)
                            sndmessage.Append("0x"+(wbuf[i]).ToString("X2")+ " ");
                        SendMessage = sndmessage.ToString();
                        ReadMessage = recvmessage.ToString();
                        Marshal.FreeHGlobal((IntPtr)rbuf);
                        Marshal.FreeHGlobal((IntPtr)wbuf);
                    }
                    errorCode = (UInt16)Modbus.LastClientError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Client is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }
    }
    public class ModbusServer
    {
        [DllImport("ModbusLibrary.dll", EntryPoint = "NewServer", CharSet = CharSet.Unicode)]
        protected static extern IntPtr DllNewServer(UInt16 Port, UInt16 MaxClients, UInt16 nDiscreteInputs,
            UInt16 nCoils, UInt16 nInputRegisters, UInt16 nHoldingRegisters);

        [DllImport("ModbusLibrary.dll", EntryPoint = "EstablishServer", CharSet = CharSet.Unicode)]
        protected static extern UInt16 DllEstablishServer(IntPtr Server);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ShutdownServer", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllShutdownServer(IntPtr Server);

        [DllImport("ModbusLibrary.dll", EntryPoint = "DestroyServer", CharSet = CharSet.Unicode)]
        protected static extern Int32 DllDestroyServer(IntPtr Server);

        [DllImport("ModbusLibrary.dll", EntryPoint = "ChangeData", CharSet = CharSet.Unicode)]
        protected static unsafe extern Int32 DllChangeData(IntPtr server, bool* discreteInputs, UInt16[] inputRegisters, Boolean** coils, UInt16** holdingRegisters);

        private IntPtr FHandle;
        public event EventHandler OnError;
        private bool FErrorEventInvalid;
        private UInt16 nDiscreteInputs;
        private UInt16 nCoils;
        private UInt16 nInputRegisters;
        private UInt16 nHoldingRegisters;

        public bool EventInvalidCreation
        {
            get
            {
                return FErrorEventInvalid;
            }
            set
            {
                FErrorEventInvalid = value;
            }
        }

        protected virtual void DoOnError(EventArgs e)
        {
            ErrorEventArgs chyba = (ErrorEventArgs)e;
            if ((FErrorEventInvalid == false) && (chyba.ErrorCode == (UInt16)Modbus.Chyby.EL_INVALID_CREATION)) { }
            else if (OnError != null)
                OnError(this, e);
        }

        public ModbusServer(UInt16 Port, UInt16 MaxClients, UInt16 nDiscreteInputs, UInt16 nCoils, UInt16 nInputRegisters, UInt16 nHoldingRegisters)
        {
            UInt16 errorCode = 0;
            try
            {
                FHandle = DllNewServer(Port, MaxClients, nDiscreteInputs, nCoils, nInputRegisters, nHoldingRegisters);
                OnError = null;
                FErrorEventInvalid = false;
                this.nDiscreteInputs = nDiscreteInputs;
                this.nCoils = nCoils;
                this.nInputRegisters = nInputRegisters;
                this.nHoldingRegisters = nHoldingRegisters;
                errorCode = (UInt16)Modbus.LastServerError(FHandle);
                if (errorCode == (UInt16)Modbus.Chyby.NO_ERRORS)
                {
                    DllEstablishServer(FHandle);
                    errorCode = (UInt16)Modbus.LastServerError(FHandle);
                }
            }
            catch (Exception e)
            {
                FHandle = IntPtr.Zero;
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                FHandle = IntPtr.Zero;
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public ModbusServer(UInt16 nDiscreteInputs, UInt16 nCoils, UInt16 nInputRegisters, UInt16 nHoldingRegisters)
            : this(502, 5, nDiscreteInputs, nCoils, nInputRegisters, nHoldingRegisters)
        {
        }

        public ModbusServer()
            : this(502, 5, 16, 16, 8, 8)
        {
        }

        ~ModbusServer()
        {
            DestroyServer();
            FHandle = IntPtr.Zero;
            OnError = null;
        }

        public void EstablishServer()
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllEstablishServer(FHandle);
                    errorCode = (UInt16)Modbus.LastServerError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Server is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        public void ShutdownServer()
        {
            UInt16 errorCode = 0;
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllShutdownServer(FHandle);
                    errorCode = (UInt16)Modbus.LastServerError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Server is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }

        private void DestroyServer()
        {
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    DllDestroyServer(FHandle);
                    FHandle = IntPtr.Zero;
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Server is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
        }

        public void ChangeData(ref Boolean[] Coils, Boolean[] DiscreteInputs,
            ref UInt16[] HoldingRegisters, UInt16[] InputRegisters)
        {
            UInt16 errorCode = 0;
            UInt16 i = 0;
            if (Coils == null)
            {
                Coils = new Boolean[nCoils];
            }
            for (i = 0; i < nCoils; i++)
            {
                Coils.SetValue(false, i);
            }
            if (HoldingRegisters == null)
            {
                HoldingRegisters = new UInt16[nHoldingRegisters];
            }
            for (i = 0; i < nHoldingRegisters; i++)
            {
                HoldingRegisters.SetValue((UInt16)0, i);
            }
            try
            {
                if (FHandle != IntPtr.Zero)
                {
                    unsafe
                    {
                        bool* coilsParams = (bool*)(Marshal.AllocHGlobal(sizeof(bool) * nCoils)).ToPointer();
                        ushort* holdigRegistersParams = (ushort*)(Marshal.AllocHGlobal(sizeof(ushort) * nHoldingRegisters)).ToPointer();
                        bool* discreteParams = (bool*)(Marshal.AllocHGlobal(sizeof(bool) * nDiscreteInputs)).ToPointer();
                        for (i = 0; i < nDiscreteInputs; i++)
                            discreteParams[i] = (bool)DiscreteInputs.GetValue(i);
                        DllChangeData(FHandle, discreteParams, InputRegisters, &coilsParams, &holdigRegistersParams);
                        for (i = 0; i < nCoils; i++)
                            Coils.SetValue((Boolean)coilsParams[i], i);
                        for (i = 0; i < nHoldingRegisters; i++)
                            HoldingRegisters.SetValue((UInt16)holdigRegistersParams[i], i);
                        Marshal.FreeHGlobal((IntPtr)discreteParams);
                        Marshal.FreeHGlobal((IntPtr)coilsParams);
                        Marshal.FreeHGlobal((IntPtr)holdigRegistersParams);
                    }
                    errorCode = (UInt16)Modbus.LastServerError(FHandle);
                }
                else DoOnError(new ErrorEventArgs((UInt16)Modbus.Chyby.EL_INVALID_CREATION, "Server is not created properly."));
            }
            catch (Exception e)
            {
                DoOnError(new ErrorEventArgs(0, e.Message));
            }
            if (errorCode != (UInt16)Modbus.Chyby.NO_ERRORS)
            {
                DoOnError(new ErrorEventArgs(errorCode, Modbus.GetErrorMessage(errorCode)));
            }
        }
    }
}
