using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using Aitex.Triton160.RT;
using Aitex.Triton160.RT.PLC;

namespace Aitex.Core.RT.PLC
{
    public enum BlockType
    {
        CIO = 0x30,
        WR = 0x31,
        D = 0x82,
    }

    public class GroupFinsDataDevice : IDataDevice
    {
        private Dictionary<string, FinsDataDevice> _devices = new Dictionary<string, FinsDataDevice>()
        {
            {"local", new FinsDataDevice(RtInstance.LocalPlcIpAddress,
                RtInstance.LocalPlcIoBaseDi,  RtInstance.LocalPlcDiBlockLength,  RtInstance.LocalPlcDiType,
                RtInstance.LocalPlcIoBaseDo,  RtInstance.LocalPlcDoBlockLength,  RtInstance.LocalPlcDoType,
                RtInstance.LocalPlcIoBaseAi,  RtInstance.LocalPlcAiBlockLength,  RtInstance.LocalPlcAiType,
                RtInstance.LocalPlcIoBaseAo,  RtInstance.LocalPlcAoBlockLength,  RtInstance.LocalPlcAoType )},
        };

        public bool IsOpened
        {
            get
            {
                foreach (var device in _devices)
                {
                    if (!device.Value.IsOpened)
                        return false;
                }
                return true;
            }
        }

        public bool Open()
        {
            foreach (var device in _devices)
            {
                if (!device.Value.Open())
                    return false;
            }
            return true;
        }

        public void Close()
        {
            foreach (var device in _devices)
            {
                device.Value.Close();
            }
        }

        public object Read<T>(string type)
        {
            if (!_devices.ContainsKey(type))
            {
                LOG.Error("read undefined PLC type " + type);
                return null;
            }

            return _devices[type].Read<T>();
        }

        public bool Write<T>(string type, T buffer)
        {
            if (!_devices.ContainsKey(type))
            {
                LOG.Error("write undefined PLC type " + type);
                return false;
            }

            return _devices[type].Write<T>(buffer);
        }

        public List<string> GetTypes()
        {
            return _devices.Keys.ToList();
        }

        public bool IsOpen(string type)
        {
            return _devices[type].IsOpen(type);
        }

        public bool Open(string type)
        {
            return _devices[type].Open(type);
        }

        public void Close(string type)
        {
            _devices[type].Close(type);
        }
    }

    public class FinsDataDevice : IDataDevice
    {
        public bool IsOpened
        {
            get
            {
                return _isOpened;
            }
        }

        private TcpClient msender;
        private Socket msock;
        private byte[] Client, Server;

        private bool _isOpened = false;

        private string _ip = "192.168.10.10";
        private int port = 9600;

        private int _diStartPosition;
        private int _diLength;
        private BlockType _diBlockType;

        private int _doStartPosition;
        private int _doLength;
        private BlockType _doBlockType;

        private int _aiStartPosition;
        private int _aiLength;
        private BlockType _aiBlockType;

        private int _aoStartPosition;
        private int _aoLength;
        private BlockType _aoBlockType;

        private R_TRIG _failedTrigger = new R_TRIG();

        private DeviceTimer _timerWrite = new DeviceTimer();
        private DeviceTimer _timerRead = new DeviceTimer();

        public FinsDataDevice(string ip, int diStartPosition, int diLength, BlockType diBlockType,
                                         int doStartPosition, int doLength, BlockType doBlockType,
                                         int aiStartPosition, int aiLength, BlockType aiBlockType,
                                         int aoStartPosition, int aoLength, BlockType aoBlockType)
        {
            _ip = ip;
            _diStartPosition = diStartPosition;
            _diLength = diLength;
            _diBlockType = diBlockType;

            _doStartPosition = doStartPosition;
            _doLength = doLength;
            _doBlockType = doBlockType;

            _aiStartPosition = aiStartPosition;
            _aiLength = aiLength;
            _aiBlockType = aiBlockType;

            _aoStartPosition = aoStartPosition;
            _aoLength = aoLength;
            _aoBlockType = aoBlockType;
        }

        public bool Open()
        {
            if (string.IsNullOrEmpty(_ip))
                return true;

            try
            {
                Close();

                _isOpened = false;

                msender = new TcpClient(_ip, port);
                msock = msender.Client;
                msock.Send(HandShake());
                byte[] buffer = new byte[24];
                msock.Receive(buffer, SocketFlags.None);

                {
                    Client = new byte[4];
                    Client[0] = buffer[16];
                    Client[1] = buffer[17];
                    Client[2] = buffer[18];
                    Client[3] = buffer[19];
                    Server = new byte[4];
                    Server[0] = buffer[20];
                    Server[1] = buffer[21];
                    Server[2] = buffer[22];
                    Server[3] = buffer[23];
                }
                _isOpened = true;
            }
            catch (Exception ex)
            {
                _failedTrigger.CLK = true;

                if (_failedTrigger.Q)
                {
                    LOG.Write(ex, String.Format("Communication failed with PLC {0}:{1}", _ip, port));
                }

                return false;
            }

            _failedTrigger.RST = true;

            _isOpened = true;

            LOG.Write(String.Format("连接成功,PLC {0}:{1}", _ip, port));

            return true;
        }

        public void Close()
        {
            if (string.IsNullOrEmpty(_ip))
                return;
            try
            {
                if (_isOpened)
                {
                    LOG.Write(String.Format("关闭连接,PLC {0}:{1}", _ip, port));

                    _isOpened = false;
                    msender.Close();
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        public object Read<T>()
        {
            PLC_INPUT_DATA buf = new PLC_INPUT_DATA();
            buf.DI = new Byte[IOGroupManager.DioBlockLength];
            buf.AI = new float[IOGroupManager.AioBlockLength];

            if (string.IsNullOrEmpty(_ip))
                return buf;

            try
            {
                ReadBlock<byte>(_diBlockType, (ushort)_diStartPosition, (ushort)_diLength, ref buf.DI);
                ReadBlock_float(_aiBlockType, (ushort)_aiStartPosition, (ushort)_aiLength, ref buf.AI);

                return buf;
            }
            catch (Exception ex)
            {
                LOG.Error(String.Format("PLC ({0}) Read exception.", _ip), ex);

                Close();
            }
            return null;
        }

        public bool Write<T>(T buffer)
        {
            if (string.IsNullOrEmpty(_ip))
                return true;
            try
            {
                _timerWrite.Start(0);

                PLC_OUTPUT_DATA buf = (PLC_OUTPUT_DATA)(object)buffer;
                WriteBlock_hex(_aoBlockType, (ushort)_aoStartPosition, (ushort)_aoLength, buf.AO);
                WriteBlock_Byte(_doBlockType, (ushort)_doStartPosition, (ushort)_doLength, buf.DO);

                //WriteBlock_float(_aoBlockType, (ushort)_aoStartPosition, (ushort)_aoLength,  buf.AO);

                //double interval = _timerWrite.GetElapseTime();

                //if (interval > _averageWriteTime)
                //{
                //    LOG.Write(_ip + ":Max write PLC interval : " + interval);

                //    _averageWriteTime = interval;
                //}

                return true;
            }
            catch (Exception ex)
            {
                LOG.Error(String.Format("PLC ({0})Write exception.", _ip), ex);
                Close();
                return false;
            }
        }

        private void ReadBlock<T>(BlockType type, ushort addr, ushort len, ref T[] data)
        {
            byte[] FullCmd = new byte[34];
            //TCP FINS header
            FullCmd[0] = 0x46;//F
            FullCmd[1] = 0x49;//I
            FullCmd[2] = 0x4e;//N
            FullCmd[3] = 0x53;//S
            FullCmd[4] = 0;//cmd length
            FullCmd[5] = 0;
            FullCmd[6] = 0;
            FullCmd[7] = 0x1A;
            FullCmd[8] = 0;//frame command
            FullCmd[9] = 0;
            FullCmd[10] = 0;
            FullCmd[11] = 0x02;
            FullCmd[12] = 0;//err
            FullCmd[13] = 0;
            FullCmd[14] = 0;
            FullCmd[15] = 0;

            //command frame header
            FullCmd[16] = 0x80;//ICF
            FullCmd[17] = 0x00;//RSV
            FullCmd[18] = 0x02;//GCT, less than 8 network layers
            FullCmd[19] = 0x00;//DNA, local network
            FullCmd[20] = Server[3];//DA1
            FullCmd[21] = 0x00;//DA2, CPU unit
            FullCmd[22] = 0x00;//SNA, local network
            FullCmd[23] = Client[3];//SA1
            FullCmd[24] = 0x00;//SA2, CPU unit
            FullCmd[25] = Convert.ToByte(21);//SID

            FullCmd[26] = 0x01;   //FINS CMD Main Request Code    0101 Read IO Area
            FullCmd[27] = 0x01;   //Sub Request Code

            //Parameter
            FullCmd[28] = (byte)type;

            FullCmd[29] = (byte)((addr >> 8) & 0xFF);    //Block Add
            FullCmd[30] = (byte)(addr & 0xFF);
            FullCmd[31] = 0;    //Bit Add

            FullCmd[32] = (byte)((len >> 8) & 0xFF); ;    //Read Count
            FullCmd[33] = (byte)(len & 0xFF);

            msock.Send(FullCmd, SocketFlags.None);
            byte[] buffer = new byte[len + 30];
            msock.Receive(buffer);
            bool Succeed = true;
            if (buffer[11] == 3)
                Succeed = CheckHeadError(buffer[15]);
            if (Succeed)//no header error
            {
                T[] buf = new T[len];
                Array.Copy(buffer, 30, buf, 0, len);

                if (CheckEndCode(buffer[28], buffer[29]))
                {
                    for (int i = 0; i < len; i++)
                    {
                        data[i] = buf[i];
                    }
                }
            }
        }

        private void ReadBlock_float(BlockType type, ushort addr, ushort len, ref float[] data)
        {
            byte[] FullCmd = new byte[34];
            //TCP FINS header
            FullCmd[0] = 0x46;//F
            FullCmd[1] = 0x49;//I
            FullCmd[2] = 0x4e;//N
            FullCmd[3] = 0x53;//S
            FullCmd[4] = 0;//cmd length
            FullCmd[5] = 0;
            FullCmd[6] = 0;
            FullCmd[7] = 0x1A;
            FullCmd[8] = 0;//frame command
            FullCmd[9] = 0;
            FullCmd[10] = 0;
            FullCmd[11] = 0x02;
            FullCmd[12] = 0;//err
            FullCmd[13] = 0;
            FullCmd[14] = 0;
            FullCmd[15] = 0;

            //command frame header
            FullCmd[16] = 0x80;//ICF
            FullCmd[17] = 0x00;//RSV
            FullCmd[18] = 0x02;//GCT, less than 8 network layers
            FullCmd[19] = 0x00;//DNA, local network
            FullCmd[20] = Server[3];//DA1
            FullCmd[21] = 0x00;//DA2, CPU unit
            FullCmd[22] = 0x00;//SNA, local network
            FullCmd[23] = Client[3];//SA1
            FullCmd[24] = 0x00;//SA2, CPU unit
            FullCmd[25] = Convert.ToByte(21);//SID

            FullCmd[26] = 0x01;   //FINS CMD Main Request Code    0101 Read IO Area
            FullCmd[27] = 0x01;   //Sub Request Code

            //Parameter
            FullCmd[28] = (byte)type;

            FullCmd[29] = (byte)((addr >> 8) & 0xFF);    //Block Add
            FullCmd[30] = (byte)(addr & 0xFF);
            FullCmd[31] = 0;    //Bit Add

            int count = len * 2;

            FullCmd[32] = (byte)((count >> 8) & 0xFF); ;    //Read Count
            FullCmd[33] = (byte)(count & 0xFF);

            msock.Send(FullCmd, SocketFlags.None);
            byte[] buffer = new byte[31 + len * 4];
            msock.Receive(buffer);
            bool Succeed = true;
            if (buffer[11] == 3)
                Succeed = CheckHeadError(buffer[15]);
            if (Succeed)//no header error
            {
                if (CheckEndCode(buffer[28], buffer[29]))
                {
                    byte[] buf = new byte[4];

                    for (int i = 0; i < len; i++)
                    {
                        buf[0] = buffer[30 + i * 4 + 1];
                        buf[1] = buffer[30 + i * 4 + 0];
                        buf[2] = buffer[30 + i * 4 + 3];
                        buf[3] = buffer[30 + i * 4 + 2];
                        data[i] = BitConverter.ToSingle(buf, 0);
                    }
                }
            }
        }

        private void WriteBlock_Byte(BlockType type, ushort addr, ushort len, byte[] data)
        {
            byte[] FullCmd = new byte[34 + len];

            //TCP FINS header
            FullCmd[0] = 0x46;//F
            FullCmd[1] = 0x49;//I
            FullCmd[2] = 0x4e;//N
            FullCmd[3] = 0x53;//S

            int cmdLen = 26 + len;
            FullCmd[4] = 0;//cmd length
            FullCmd[5] = 0;
            FullCmd[6] = (byte)((cmdLen >> 8) & 0xFF);
            FullCmd[7] = (byte)(cmdLen & 0xFF);

            FullCmd[8] = 0;//frame command
            FullCmd[9] = 0;
            FullCmd[10] = 0;
            FullCmd[11] = 0x02;
            FullCmd[12] = 0;//err
            FullCmd[13] = 0;
            FullCmd[14] = 0;
            FullCmd[15] = 0;
            //command frame header
            FullCmd[16] = 0x80;//ICF
            FullCmd[17] = 0x00;//RSV
            FullCmd[18] = 0x02;//GCT, less than 8 network layers
            FullCmd[19] = 0x00;//DNA, local network
            FullCmd[20] = Server[3];//DA1
            FullCmd[21] = 0x00;//DA2, CPU unit
            FullCmd[22] = 0x00;//SNA, local network
            FullCmd[23] = Client[3];//SA1
            FullCmd[24] = 0x00;//SA2, CPU unit

            FullCmd[25] = Convert.ToByte(21);//SID

            FullCmd[26] = 0x01;   //FINS CMD Main Request Code    0101 Read IO Area
            FullCmd[27] = 0x02;   //Sub Request Code

            //Parameter
            FullCmd[28] = (byte)type;

            FullCmd[29] = (byte)((addr >> 8) & 0xFF);    //Block Add
            FullCmd[30] = (byte)(addr & 0xFF);
            FullCmd[31] = 0;    //Bit Add

            FullCmd[32] = (byte)((len >> 8) & 0xFF); ;    //Write Count
            FullCmd[33] = (byte)(len & 0xFF);

            for (int i = 0; i < len; i++)
            {
                FullCmd[34 + i] = data[i];
            }

            msock.Send(FullCmd, SocketFlags.None);
            byte[] buffer = new byte[len + 34];
            msock.Receive(buffer);
            bool Succeed = true;
            if (buffer[11] == 3)
                Succeed = CheckHeadError(buffer[15]);
            if (Succeed)//no header error
            {
                if (CheckEndCode(buffer[28], buffer[29]))
                {
                    //do nothing
                }
            }
        }

        private void WriteBlock_float(BlockType type, ushort addr, ushort len, float[] data)
        {
            byte[] FullCmd = new byte[34 + len * 4];

            //TCP FINS header
            FullCmd[0] = 0x46;//F
            FullCmd[1] = 0x49;//I
            FullCmd[2] = 0x4e;//N
            FullCmd[3] = 0x53;//S

            int cmdLen = 26 + len * 4;
            FullCmd[4] = 0;//cmd length
            FullCmd[5] = 0;
            FullCmd[6] = (byte)((cmdLen >> 8) & 0xFF);
            FullCmd[7] = (byte)(cmdLen & 0xFF);

            FullCmd[8] = 0;//frame command
            FullCmd[9] = 0;
            FullCmd[10] = 0;
            FullCmd[11] = 0x02;
            FullCmd[12] = 0;//err
            FullCmd[13] = 0;
            FullCmd[14] = 0;
            FullCmd[15] = 0;
            //command frame header
            FullCmd[16] = 0x80;//ICF
            FullCmd[17] = 0x00;//RSV
            FullCmd[18] = 0x02;//GCT, less than 8 network layers
            FullCmd[19] = 0x00;//DNA, local network
            FullCmd[20] = Server[3];//DA1
            FullCmd[21] = 0x00;//DA2, CPU unit
            FullCmd[22] = 0x00;//SNA, local network
            FullCmd[23] = Client[3];//SA1
            FullCmd[24] = 0x00;//SA2, CPU unit

            FullCmd[25] = Convert.ToByte(21);//SID

            FullCmd[26] = 0x01;   //FINS CMD Main Request Code    0101 Read IO Area
            FullCmd[27] = 0x02;   //Sub Request Code

            //Parameter
            FullCmd[28] = (byte)type;

            FullCmd[29] = (byte)((addr >> 8) & 0xFF);    //Block Add
            FullCmd[30] = (byte)(addr & 0xFF);
            FullCmd[31] = 0;    //Bit Add

            int count = len * 2;
            FullCmd[32] = (byte)((count >> 8) & 0xFF); ;    //Write Count
            FullCmd[33] = (byte)(count & 0xFF);

            try
            {
                for (int i = 0; i < len; i++)
                {
                    byte[] buf = BitConverter.GetBytes(data[i]);

                    FullCmd[34 + i * 4 + 0] = buf[1];
                    FullCmd[34 + i * 4 + 1] = buf[0];
                    FullCmd[34 + i * 4 + 2] = buf[3];
                    FullCmd[34 + i * 4 + 3] = buf[2];
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }

            msock.Send(FullCmd, SocketFlags.None);
            byte[] buffer = new byte[31 + len * 4];
            msock.Receive(buffer);
            bool Succeed = true;
            if (buffer[11] == 3)
                Succeed = CheckHeadError(buffer[15]);
            if (Succeed)//no header error
            {
                if (CheckEndCode(buffer[28], buffer[29]))
                {
                    //do nothing
                }
            }
        }

        private void WriteBlock_hex(BlockType type, ushort addr, ushort len, float[] data)
        {
            byte[] FullCmd = new byte[34 + len * 4];

            //TCP FINS header
            FullCmd[0] = 0x46;//F
            FullCmd[1] = 0x49;//I
            FullCmd[2] = 0x4e;//N
            FullCmd[3] = 0x53;//S

            int cmdLen = 26 + len * 4;
            FullCmd[4] = 0;//cmd length
            FullCmd[5] = 0;
            FullCmd[6] = (byte)((cmdLen >> 8) & 0xFF);
            FullCmd[7] = (byte)(cmdLen & 0xFF);

            FullCmd[8] = 0;//frame command
            FullCmd[9] = 0;
            FullCmd[10] = 0;
            FullCmd[11] = 0x02;
            FullCmd[12] = 0;//err
            FullCmd[13] = 0;
            FullCmd[14] = 0;
            FullCmd[15] = 0;
            //command frame header
            FullCmd[16] = 0x80;//ICF
            FullCmd[17] = 0x00;//RSV
            FullCmd[18] = 0x02;//GCT, less than 8 network layers
            FullCmd[19] = 0x00;//DNA, local network
            FullCmd[20] = Server[3];//DA1
            FullCmd[21] = 0x00;//DA2, CPU unit
            FullCmd[22] = 0x00;//SNA, local network
            FullCmd[23] = Client[3];//SA1
            FullCmd[24] = 0x00;//SA2, CPU unit

            FullCmd[25] = Convert.ToByte(21);//SID

            FullCmd[26] = 0x01;   //FINS CMD Main Request Code    0101 Read IO Area
            FullCmd[27] = 0x02;   //Sub Request Code

            //Parameter
            FullCmd[28] = (byte)type;

            FullCmd[29] = (byte)((addr >> 8) & 0xFF);    //Block Add
            FullCmd[30] = (byte)(addr & 0xFF);
            FullCmd[31] = 0;    //Bit Add

            int count = len * 2;
            FullCmd[32] = (byte)((count >> 8) & 0xFF); ;    //Write Count
            FullCmd[33] = (byte)(count & 0xFF);

            try
            {
                for (int i = 0; i < len; i++)
                {
                    byte[] buf = BitConverter.GetBytes(data[i]);

                    FullCmd[34 + i * 4 + 0] = buf[1];
                    FullCmd[34 + i * 4 + 1] = buf[0];
                    FullCmd[34 + i * 4 + 2] = buf[3];
                    FullCmd[34 + i * 4 + 3] = buf[2];
                }
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }

            msock.Send(FullCmd, SocketFlags.None);
            byte[] buffer = new byte[31 + len * 4];
            msock.Receive(buffer);
            bool Succeed = true;
            if (buffer[11] == 3)
                Succeed = CheckHeadError(buffer[15]);
            if (Succeed)//no header error
            {
                if (CheckEndCode(buffer[28], buffer[29]))
                {
                    //do nothing
                }
            }
        }

        private byte[] HandShake()
        {
            //handshake
            byte[] Handshake = new byte[20];
            Handshake[0] = 0x46;
            Handshake[1] = 0x49;
            Handshake[2] = 0x4e;
            Handshake[3] = 0x53;

            Handshake[4] = 0;
            Handshake[5] = 0;
            Handshake[6] = 0;
            Handshake[7] = 0x0c;

            Handshake[8] = 0;
            Handshake[9] = 0;
            Handshake[10] = 0;
            Handshake[11] = 0;

            Handshake[12] = 0;
            Handshake[13] = 0;
            Handshake[14] = 0;
            Handshake[15] = 0;

            Handshake[16] = 0;
            Handshake[17] = 0;
            Handshake[18] = 0;
            Handshake[19] = 0;//ask for client and server node number, the client node will allocated automatically

            return Handshake;
        }

        /// <summary>
        /// (若返回的头指令为3)检查命令头中的错误代码
        /// </summary>
        /// <param name="Code">错误代码</param>
        /// <returns>指示程序是否可以继续进行</returns>
        bool CheckHeadError(byte Code)
        {
            switch (Code)
            {
            case 0x00: return true;
            case 0x01: RaiseException("the head is not 'FINS'"); return false;
            case 0x02: RaiseException("the data length is too long"); return false;
            case 0x03: RaiseException("the command is not supported"); return false;
            }
            //no hit
            RaiseException("unknown exception"); return false;
        }

        private void RaiseException(string p)
        {
            _isOpened = false;
        }

        /// <summary>
        /// 检查命令帧中的EndCode
        /// </summary>
        /// <param name="Main">主码</param>
        /// <param name="Sub">副码</param>
        /// <returns>指示程序是否可以继续进行</returns>
        bool CheckEndCode(byte Main, byte Sub)
        {
            switch (Main)
            {
            case 0x00:
                switch (Sub)
                {
                case 0x00: return true;//the only situation of success
                case 0x01: RaiseException("service canceled"); return false;
                }
                break;
            case 0x01:
                switch (Sub)
                {
                case 0x01: RaiseException("local node not in network"); return false;
                case 0x02: RaiseException("token timeout"); return false;
                case 0x03: RaiseException("retries failed"); return false;
                case 0x04: RaiseException("too many send frames"); return false;
                case 0x05: RaiseException("node address range error"); return false;
                case 0x06: RaiseException("node address duplication"); return false;
                }
                break;
            case 0x02:
                switch (Sub)
                {
                case 0x01: RaiseException("destination node not in network"); return false;
                case 0x02: RaiseException("unit missing"); return false;
                case 0x03: RaiseException("third node missing"); return false;
                case 0x04: RaiseException("destination node busy"); return false;
                case 0x05: RaiseException("response timeout"); return false;
                }
                break;
            case 0x03:
                switch (Sub)
                {
                case 0x01: RaiseException("communications controller error"); return false;
                case 0x02: RaiseException("CPU unit error"); return false;
                case 0x03: RaiseException("controller error"); return false;
                case 0x04: RaiseException("unit number error"); return false;
                }
                break;
            case 0x04:
                switch (Sub)
                {
                case 0x01: RaiseException("undefined command"); return false;
                case 0x02: RaiseException("not supported by model/version"); return false;
                }
                break;
            case 0x05:
                switch (Sub)
                {
                case 0x01: RaiseException("destination address setting error"); return false;
                case 0x02: RaiseException("no routing tables"); return false;
                case 0x03: RaiseException("routing table error"); return false;
                case 0x04: RaiseException("too many relays"); return false;
                }
                break;
            case 0x10:
                switch (Sub)
                {
                case 0x01: RaiseException("command too long"); return false;
                case 0x02: RaiseException("command too short"); return false;
                case 0x03: RaiseException("elements/data don't match"); return false;
                case 0x04: RaiseException("command format error"); return false;
                case 0x05: RaiseException("header error"); return false;
                }
                break;
            case 0x11:
                switch (Sub)
                {
                case 0x01: RaiseException("area classification missing"); return false;
                case 0x02: RaiseException("access size error"); return false;
                case 0x03: RaiseException("address range error"); return false;
                case 0x04: RaiseException("address range exceeded"); return false;
                case 0x06: RaiseException("program missing"); return false;
                case 0x09: RaiseException("relational error"); return false;
                case 0x0a: RaiseException("duplicate data access"); return false;
                case 0x0b: RaiseException("response too long"); return false;
                case 0x0c: RaiseException("parameter error"); return false;
                }
                break;
            case 0x20:
                switch (Sub)
                {
                case 0x02: RaiseException("protected"); return false;
                case 0x03: RaiseException("table missing"); return false;
                case 0x04: RaiseException("data missing"); return false;
                case 0x05: RaiseException("program missing"); return false;
                case 0x06: RaiseException("file missing"); return false;
                case 0x07: RaiseException("data mismatch"); return false;
                }
                break;
            case 0x21:
                switch (Sub)
                {
                case 0x01: RaiseException("read-only"); return false;
                case 0x02: RaiseException("protected , cannot write data link table"); return false;
                case 0x03: RaiseException("cannot register"); return false;
                case 0x05: RaiseException("program missing"); return false;
                case 0x06: RaiseException("file missing"); return false;
                case 0x07: RaiseException("file name already exists"); return false;
                case 0x08: RaiseException("cannot change"); return false;
                }
                break;
            case 0x22:
                switch (Sub)
                {
                case 0x01: RaiseException("not possible during execution"); return false;
                case 0x02: RaiseException("not possible while running"); return false;
                case 0x03: RaiseException("wrong PLC mode"); return false;
                case 0x04: RaiseException("wrong PLC mode"); return false;
                case 0x05: RaiseException("wrong PLC mode"); return false;
                case 0x06: RaiseException("wrong PLC mode"); return false;
                case 0x07: RaiseException("specified node not polling node"); return false;
                case 0x08: RaiseException("step cannot be executed"); return false;
                }
                break;
            case 0x23:
                switch (Sub)
                {
                case 0x01: RaiseException("file device missing"); return false;
                case 0x02: RaiseException("memory missing"); return false;
                case 0x03: RaiseException("clock missing"); return false;
                }
                break;
            case 0x24:
                switch (Sub)
                { case 0x01: RaiseException("table missing"); return false; }
                break;
            case 0x25:
                switch (Sub)
                {
                case 0x02: RaiseException("memory error"); return false;
                case 0x03: RaiseException("I/O setting error"); return false;
                case 0x04: RaiseException("too many I/O points"); return false;
                case 0x05: RaiseException("CPU bus error"); return false;
                case 0x06: RaiseException("I/O duplication"); return false;
                case 0x07: RaiseException("CPU bus error"); return false;
                case 0x09: RaiseException("SYSMAC BUS/2 error"); return false;
                case 0x0a: RaiseException("CPU bus unit error"); return false;
                case 0x0d: RaiseException("SYSMAC BUS No. duplication"); return false;
                case 0x0f: RaiseException("memory error"); return false;
                case 0x10: RaiseException("SYSMAC BUS terminator missing"); return false;
                }
                break;
            case 0x26:
                switch (Sub)
                {
                case 0x01: RaiseException("no protection"); return false;
                case 0x02: RaiseException("incorrect password"); return false;
                case 0x04: RaiseException("protected"); return false;
                case 0x05: RaiseException("service already executing"); return false;
                case 0x06: RaiseException("service stopped"); return false;
                case 0x07: RaiseException("no execution right"); return false;
                case 0x08: RaiseException("settings required before execution"); return false;
                case 0x09: RaiseException("necessary items not set"); return false;
                case 0x0a: RaiseException("number already defined"); return false;
                case 0x0b: RaiseException("error will not clear"); return false;
                }
                break;
            case 0x30:
                switch (Sub)
                { case 0x01: RaiseException("no access right"); return false; }
                break;
            case 0x40:
                switch (Sub)
                { case 0x01: RaiseException("service aborted"); return false; }
                break;
            }
            //no hit
            RaiseException("unknown exception"); return false;
        }

        public object Read<T>(string type)
        {
            throw new NotImplementedException();
        }

        public bool Write<T>(string type, T buffer)
        {
            throw new NotImplementedException();
        }

        public List<string> GetTypes()
        {
            throw new NotImplementedException();
        }

        public bool IsOpen(string type)
        {
            if (string.IsNullOrEmpty(_ip))
                return true;
            return IsOpened;
        }

        public bool Open(string type)
        {
            if (string.IsNullOrEmpty(_ip))
                return true;
            return Open();
        }

        public void Close(string type)
        {
            if (string.IsNullOrEmpty(_ip))
                return;
            Close();
        }
    }

    public class Converter
    {
        public static byte[] StructToBytes(object obj)
        {
            int rawsize = Marshal.SizeOf(obj);
            IntPtr buffer = Marshal.AllocHGlobal(rawsize);
            Marshal.StructureToPtr(obj, buffer, false);
            byte[] rawdatas = new byte[rawsize];
            Marshal.Copy(buffer, rawdatas, 0, rawsize);
            Marshal.FreeHGlobal(buffer);
            return rawdatas;
        }

        public static object BytesToStruct(byte[] buf, int len, Type type)
        {
            object rtn;
            IntPtr buffer = Marshal.AllocHGlobal(len);
            Marshal.Copy(buf, 0, buffer, len);
            rtn = Marshal.PtrToStructure(buffer, type);
            Marshal.FreeHGlobal(buffer);
            return rtn;
        }

        public static void BytesToStruct(byte[] buf, int len, object rtn)
        {
            IntPtr buffer = Marshal.AllocHGlobal(len);
            Marshal.Copy(buf, 0, buffer, len);
            Marshal.PtrToStructure(buffer, rtn);
            Marshal.FreeHGlobal(buffer);
        }

        public static void BytesToStruct(byte[] buf, object rtn)
        {
            BytesToStruct(buf, buf.Length, rtn);
        }

        public static object BytesToStruct(byte[] buf, Type type)
        {
            return BytesToStruct(buf, buf.Length, type);
        }
    }
}