using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static EasyModbus.TCPHandler;

namespace McProtocol.Mitsubishi
{
    public class McProtocolTcp
    {
        /// <summary>
        /// 使用的指令框架
        /// </summary>
        public McFrame CommandFrame { get; set; }
        private McCommand Command { get; set; }
        /// <summary>
        /// 主机名或者IP地址
        /// </summary>
        public string HostName { get; set; }
        /// <summary>
        /// 端口号
        /// </summary>
        public int PortNumber { get; set; }
        public int Device { private set; get; }

        private const int BlockSize = 0x0010;
        private TcpClient Client { get; set; }
        private NetworkStream Stream { get; set; }

        // 是否连接
        public bool Connected
        {
            get
            {
                return Client.Connected;
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public McProtocolTcp() : this("", 0, McFrame.MC3E)
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="iHostName"></param>
        /// <param name="iPortNumber"></param>
        /// <param name="frame"></param>
        public McProtocolTcp(string iHostName, int iPortNumber, McFrame frame)
        {
            CommandFrame = frame;
            Client = new TcpClient();

            //C70 = MC3E
            HostName = iHostName;
            PortNumber = iPortNumber;
        }

        /// <summary>
        /// 后处理
        /// </summary>
        public void Dispose()
        {
            TcpClient c = Client;
            if (c.Connected)
            {
                c.Close();
            }
        }

        public void Open()
        {
            if (!Client.Connected)
            {
                // 实现Keep Alive功能
                var ka = new List<byte>(sizeof(uint) * 3);
                ka.AddRange(BitConverter.GetBytes(1u));
                ka.AddRange(BitConverter.GetBytes(45000u));
                ka.AddRange(BitConverter.GetBytes(5000u));
                Client.Client.IOControl(IOControlCode.KeepAliveValues, ka.ToArray(), null);

                IAsyncResult asyncResult = Client.BeginConnect(HostName, PortNumber, null, null);
                if (!asyncResult.AsyncWaitHandle.WaitOne(1000))
                {
                    throw new Exception("连接超时");
                }
                Client.EndConnect(asyncResult);
                Stream = Client.GetStream();
            }
            Command = new McCommand(CommandFrame);
        }


        public int SetBitDevice(string iDeviceName, int iSize, int[] iData)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return SetBitDevice(type, addr, iSize, iData);
        }

        public int SetBitDevice(PlcDeviceType iType, int iAddress, int iSize, int[] iData)
        {
            var type = iType;
            var addr = iAddress;
            var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
            var d = (byte)iData[0];
            var i = 0;
            while (i < iData.Length)
            {
                if (i % 2 == 0)
                {
                    d = (byte)iData[i];
                    d <<= 4;
                }
                else
                {
                    d |= (byte)(iData[i] & 0x01);
                    data.Add(d);
                }
                ++i;
            }
            if (i % 2 != 0)
            {
                data.Add(d);
            }
            int length = (int)Command.FrameType;// == McFrame.MC3E) ? 11 : 15;
            byte[] sdCommand = Command.SetCommandMC3E(0x1401, 0x0001, data.ToArray());
            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            return rtCode;
        }

        public int GetBitDevice(string iDeviceName, int iSize, int[] oData)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return GetBitDevice(type, addr, iSize, oData);
        }

        public int GetBitDevice(PlcDeviceType iType, int iAddress, int iSize, int[] oData)
        {

            PlcDeviceType type = iType;
            int addr = iAddress;
            var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
            byte[] sdCommand = Command.SetCommandMC3E(0x0401, 0x0001, data.ToArray());
            int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            byte[] rtData = Command.Response;
            for (int i = 0; i < iSize; ++i)
            {
                if (i % 2 == 0)
                {
                    oData[i] = (rtCode == 0) ? ((rtData[i / 2] >> 4) & 0x01) : 0;
                }
                else
                {
                    oData[i] = (rtCode == 0) ? (rtData[i / 2] & 0x01) : 0;
                }
            }
            return rtCode;
        }

        public int WriteDeviceBlock(string iDeviceName, int iSize, ushort[] iData)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return WriteDeviceBlock(type, addr, iSize, iData);
        }

        public int WriteDeviceBlock(PlcDeviceType iType, int iAddress, int iSize, ushort[] iData)
        {
            PlcDeviceType type = iType;
            int addr = iAddress;
            List<byte> data;
            List<byte> DeviceData = new List<byte>();
            foreach (ushort t in iData)
            {
                byte[] value = BitConverter.GetBytes(t);
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(value);
                }
                DeviceData.AddRange(value);
            }
            byte[] sdCommand;
            int length;
            switch (CommandFrame)
            {
                case McFrame.MC3E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
                    data.AddRange(DeviceData.ToArray());
                    sdCommand = Command.SetCommandMC3E(0x1401, 0x0000, data.ToArray());
                    length = 11;
                    break;
                case McFrame.MC4E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
                    data.AddRange(DeviceData.ToArray());
                    sdCommand = Command.SetCommandMC4E(0x1401, 0x0000, data.ToArray());
                    length = 15;
                    break;
                case McFrame.MC1E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)(addr >> 24), 0x20, 0x44, (byte)iSize, 0x00 };
                    data.AddRange(DeviceData.ToArray());
                    //Add data
                    sdCommand = Command.SetCommandMC1E(0x03, data.ToArray());
                    length = 2;
                    break;
                default:
                    throw new Exception("Message frame not supported");
            }

            //TEST take care of the writing
            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            return rtCode;
        }

        public byte[] ReadDeviceBlock(string iDeviceName, int iSize, ushort[] oData)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return ReadDeviceBlock(type, addr, iSize, oData);
        }

        public byte[] ReadDeviceBlock(PlcDeviceType iType, int iAddress, int iSize, ushort[] oData)
        {

            PlcDeviceType type = iType;
            int addr = iAddress;
            List<byte> data;
            byte[] sdCommand;
            int length;

            switch (CommandFrame)
            {
                case McFrame.MC3E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
                    sdCommand = Command.SetCommandMC3E(0x0401, 0x0000, data.ToArray());
                    length = 11;
                    break;
                case McFrame.MC4E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, (byte)iSize, (byte)(iSize >> 8) };
                    sdCommand = Command.SetCommandMC4E(0x0401, 0x0000, data.ToArray());
                    length = 15;
                    break;
                case McFrame.MC1E:
                    data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)(addr >> 24), 0x20, 0x44, (byte)iSize, 0x00 };
                    sdCommand = Command.SetCommandMC1E(0x01, data.ToArray());
                    length = 2;
                    break;
                default:
                    throw new Exception("Message frame not supported");
            }

            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            byte[] rtData = Command.Response;
            for (int i = 0; i < iSize; ++i)
            {
                if (rtCode == 0)
                {
                    oData[i] = (ushort)(rtData[i * 2] << 8 | rtData[i * 2 + 1]);
                }
                //oData[i] = (rtCode == 0) ? BitConverter.ToUInt16(rtData, i * 2) : 0;
            }
            return rtData;
        }

        public int SetDevice(string iDeviceName, int iData)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return SetDevice(type, addr, iData);
        }

        public int SetDevice(PlcDeviceType iType, int iAddress, int iData)
        {
            PlcDeviceType type = iType;
            int addr = iAddress;
            var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, 0x01, 0x00, (byte)iData, (byte)(iData >> 8) };
            byte[] sdCommand = Command.SetCommandMC3E(0x1401, 0x0000, data.ToArray());
            int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            return rtCode;
        }

        public int GetDevice(string iDeviceName)
        {
            PlcDeviceType type;
            int addr;
            GetDeviceCode(iDeviceName, out type, out addr);
            return GetDevice(type, addr);
        }

        public int GetDevice(PlcDeviceType iType, int iAddress)
        {
            PlcDeviceType type = iType;
            int addr = iAddress;
            var data = new List<byte>(6) { (byte)addr, (byte)(addr >> 8), (byte)(addr >> 16), (byte)type, 0x01, 0x00 };
            byte[] sdCommand = Command.SetCommandMC3E(0x0401, 0x0000, data.ToArray());
            int length = (Command.FrameType == McFrame.MC3E) ? 11 : 15;
            byte[] rtResponse = TryExecution(sdCommand, length);
            int rtCode = Command.SetResponse(rtResponse);
            if (0 < rtCode)
            {
                this.Device = 0;
            }
            else
            {
                byte[] rtData = Command.Response;
                this.Device = BitConverter.ToInt16(rtData, 0);
            }
            return rtCode;
        }

        #region IPlcEx
        /// <summary>
        /// 得到字节数据
        /// </summary>
        /// <param name="iDeviceName">如：M1000</param>
        /// <param name="iSize">读取长度：>0</param>
        /// <returns>数据</returns>
        public int[] GetBitDevice(string iDeviceName, int iSize = 1)
        {
            int[] oData = new int[iSize];
            GetBitDevice(iDeviceName, iSize, oData);
            return oData;
        }
        /// <summary>
        /// 得到字节数据
        /// </summary>
        /// <param name="iType">设备类型，如：M</param>
        /// <param name="iAddress">如：M1000</param>
        /// <param name="iSize">读取长度：>0</param>
        /// <returns>数据</returns>
        public int[] GetBitDevice(PlcDeviceType iType, int iAddress, int iSize = 1)
        {
            int[] oData = new int[iSize];
            GetBitDevice(iType, iAddress, iSize, oData);
            return oData;
        }
        /// <summary>
        /// 设置字节数据
        /// </summary>
        /// <param name="iDeviceName">如：M1000</param>
        /// <param name="iData">设置的数据</param>
        public void SetBitDevice(string iDeviceName, params int[] iData)
        {
            SetBitDevice(iDeviceName, iData.Length, iData);
        }
        /// <summary>
        /// 设置字节数据
        /// </summary>
        /// <param name="iType">设备类型，如：M</param>
        /// <param name="iAddress">如：M1000</param>
        /// <param name="iData">设置的数据</param>
        public void SetBitDevice(PlcDeviceType iType, int iAddress, params int[] iData)
        {
            SetBitDevice(iType, iAddress, iData.Length, iData);
        }
        /// <summary>
        /// 得到数据块
        /// </summary>
        /// <param name="iDeviceName">如：D1000</param>
        /// <param name="iSize">读取长度：>0</param>
        /// <returns>数据</returns>
        public ushort[] ReadDeviceBlock(string iDeviceName, int iSize = 1)
        {
            ushort[] oData = new ushort[iSize];
            ReadDeviceBlock(iDeviceName, iSize, oData);
            return oData;
        }
        /// <summary>
        /// 得到数据块
        /// </summary>
        /// <param name="iType">设备类型，如：D</param>
        /// <param name="iAddress">如：D1000</param>
        /// <param name="iSize">读取长度：>0</param>
        /// <returns>数据</returns>
        public ushort[] ReadDeviceBlock(PlcDeviceType iType, int iAddress, int iSize = 1)
        {
            ushort[] oData = new ushort[iSize];
            ReadDeviceBlock(iType, iAddress, iSize, oData);
            return oData;
        }
        /// <summary>
        /// 写入数据块
        /// </summary>
        /// <param name="iDeviceName">如：D1000</param>
        /// <param name="iData">写入10进制数据</param>
        public void WriteDeviceBlock(string iDeviceName, params ushort[] iData)
        {
            WriteDeviceBlock(iDeviceName, iData.Length, iData);
        }
        /// <summary>
        /// 写入数据块
        /// </summary>
        /// <param name="iType">设备类型，如：D</param>
        /// <param name="iAddress">如：D1000</param>
        /// <param name="iData">写入10进制数据</param>
        public void WriteDeviceBlock(PlcDeviceType iType, int iAddress, params ushort[] iData)
        {
            WriteDeviceBlock(iType, iAddress, iData.Length, iData);
        }
        #endregion

        //public int GetCpuType(out string oCpuName, out int oCpuType)
        //{
        //    int rtCode = Command.Execute(0x0101, 0x0000, new byte[0]);
        //    oCpuName = "dummy";
        //    oCpuType = 0;
        //    return rtCode;
        //}

        public PlcDeviceType GetDeviceType(string s)
        {
            return (s == "M") ? PlcDeviceType.M :
                   (s == "SM") ? PlcDeviceType.SM :
                   (s == "L") ? PlcDeviceType.L :
                   (s == "F") ? PlcDeviceType.F :
                   (s == "V") ? PlcDeviceType.V :
                   (s == "S") ? PlcDeviceType.S :
                   (s == "X") ? PlcDeviceType.X :
                   (s == "Y") ? PlcDeviceType.Y :
                   (s == "B") ? PlcDeviceType.B :
                   (s == "SB") ? PlcDeviceType.SB :
                   (s == "DX") ? PlcDeviceType.DX :
                   (s == "DY") ? PlcDeviceType.DY :
                   (s == "D") ? PlcDeviceType.D :
                   (s == "SD") ? PlcDeviceType.SD :
                   (s == "R") ? PlcDeviceType.R :
                   (s == "ZR") ? PlcDeviceType.ZR :
                   (s == "W") ? PlcDeviceType.W :
                   (s == "SW") ? PlcDeviceType.SW :
                   (s == "TC") ? PlcDeviceType.TC :
                   (s == "TS") ? PlcDeviceType.TS :
                   (s == "TN") ? PlcDeviceType.TN :
                   (s == "CC") ? PlcDeviceType.CC :
                   (s == "CS") ? PlcDeviceType.CS :
                   (s == "CN") ? PlcDeviceType.CN :
                   (s == "SC") ? PlcDeviceType.SC :
                   (s == "SS") ? PlcDeviceType.SS :
                   (s == "SN") ? PlcDeviceType.SN :
                   (s == "Z") ? PlcDeviceType.Z :
                   (s == "TT") ? PlcDeviceType.TT :
                   (s == "TM") ? PlcDeviceType.TM :
                   (s == "CT") ? PlcDeviceType.CT :
                   (s == "CM") ? PlcDeviceType.CM :
                   (s == "A") ? PlcDeviceType.A :
                                 PlcDeviceType.Max;
        }

        public static bool IsBitDevice(PlcDeviceType type)
        {
            return !((type == PlcDeviceType.D) || (type == PlcDeviceType.SD) || (type == PlcDeviceType.Z) || (type == PlcDeviceType.ZR) || (type == PlcDeviceType.R) || (type == PlcDeviceType.W));
        }

        public bool IsHexDevice(PlcDeviceType type)
        {
            return (type == PlcDeviceType.X) || (type == PlcDeviceType.Y) || (type == PlcDeviceType.B) || (type == PlcDeviceType.W);
        }

        public void GetDeviceCode(string iDeviceName, out PlcDeviceType oType, out int oAddress)
        {
            string s = iDeviceName.ToUpper();
            string strAddress;

            // 1.取出一个字符
            string strType = s.Substring(0, 1);
            switch (strType)
            {
                case "A":
                case "B":
                case "D":
                case "F":
                case "L":
                case "M":
                case "R":
                case "V":
                case "W":
                case "X":
                case "Y":
                    // 2字母后面，它应该是一个数字，所以转换它
                    strAddress = s.Substring(1);
                    break;
                case "Z":
                    // 再拿出一个字符
                    strType = s.Substring(0, 2);
                    // 对于文件寄存器 : 2
                    // 对于索引寄存器 : 1
                    strAddress = s.Substring(strType.Equals("ZR") ? 2 : 1);
                    break;
                case "C":
                    // 再拿出一个字符
                    strType = s.Substring(0, 2);
                    switch (strType)
                    {
                        case "CC":
                        case "CM":
                        case "CN":
                        case "CS":
                        case "CT":
                            strAddress = s.Substring(2);
                            break;
                        default:
                            throw new Exception("Invalid format.");
                    }
                    break;
                case "S":
                    // 再拿出一个字符
                    strType = s.Substring(0, 2);
                    switch (strType)
                    {
                        case "SD":
                        case "SM":
                            strAddress = s.Substring(2);
                            break;
                        default:
                            throw new Exception("Invalid format.");
                    }
                    break;
                case "T":
                    // 再拿出一个字符
                    strType = s.Substring(0, 2);
                    switch (strType)
                    {
                        case "TC":
                        case "TM":
                        case "TN":
                        case "TS":
                        case "TT":
                            strAddress = s.Substring(2);
                            break;
                        default:
                            throw new Exception("Invalid format.");
                    }
                    break;
                default:
                    throw new Exception("Invalid format.");
            }
            oType = GetDeviceType(strType);
            oAddress = IsHexDevice(oType) ? Convert.ToInt32(strAddress, BlockSize) : Convert.ToInt32(strAddress);
        }


        private byte[] TryExecution(byte[] iCommand, int minlength)
        {
            byte[] rtResponse;
            int tCount = 10;
            do
            {
                rtResponse = Execute(iCommand);
                --tCount;
                if (tCount < 0)
                {
                    throw new Exception("无法从 PLC 获取正确的值.");
                }
            } while (Command.IsIncorrectResponse(rtResponse, minlength));
            return rtResponse;
        }

        // 表示用于通信的命令的内部类
        class McCommand
        {
            public McFrame FrameType { get; private set; }  // 框架类型
            private uint SerialNumber { get; set; }  // 序号
            private uint NetworkNumber { get; set; } // 网络号码
            private uint PcNumber { get; set; }      // 电脑号码
            private uint IoNumber { get; set; }      // 请求单元 I/O 编号/O番号
            private uint ChannelNumber { get; set; } // 请求单位站号
            private uint CpuTimer { get; set; }      // 中央处理器监控计时器
            private int ResultCode { get; set; }     // 退出代码
            public byte[] Response { get; private set; }    // 响应数据

            // 构造 函数
            public McCommand(McFrame iFrame)
            {
                FrameType = iFrame;
                SerialNumber = 0x0001u;
                NetworkNumber = 0x0000u;
                PcNumber = 0x00FFu;
                IoNumber = 0x03FFu;
                ChannelNumber = 0x0000u;
                CpuTimer = 0x0010u;
            }

            public byte[] SetCommandMC1E(byte Subheader, byte[] iData)
            {
                List<byte> ret = new List<byte>(iData.Length + 4);
                ret.Add(Subheader);
                ret.Add((byte)this.PcNumber);
                ret.Add((byte)CpuTimer);
                ret.Add((byte)(CpuTimer >> 8));
                ret.AddRange(iData);
                return ret.ToArray();
            }
            public byte[] SetCommandMC3E(uint iMainCommand, uint iSubCommand, byte[] iData)
            {
                var dataLength = (uint)(iData.Length + 6);
                List<byte> ret = new List<byte>(iData.Length + 20);
                uint frame = 0x0050u;
                ret.Add((byte)frame);
                ret.Add((byte)(frame >> 8));
                ret.Add((byte)NetworkNumber);
                ret.Add((byte)PcNumber);
                ret.Add((byte)IoNumber);
                ret.Add((byte)(IoNumber >> 8));
                ret.Add((byte)ChannelNumber);
                ret.Add((byte)dataLength);
                ret.Add((byte)(dataLength >> 8));
                ret.Add((byte)CpuTimer);
                ret.Add((byte)(CpuTimer >> 8));
                ret.Add((byte)iMainCommand);
                ret.Add((byte)(iMainCommand >> 8));
                ret.Add((byte)iSubCommand);
                ret.Add((byte)(iSubCommand >> 8));

                ret.AddRange(iData);
                return ret.ToArray();
            }
            public byte[] SetCommandMC4E(uint iMainCommand, uint iSubCommand, byte[] iData)
            {
                var dataLength = (uint)(iData.Length + 6);
                var ret = new List<byte>(iData.Length + 20);
                uint frame = 0x0054u;
                ret.Add((byte)frame);
                ret.Add((byte)(frame >> 8));
                ret.Add((byte)SerialNumber);
                ret.Add((byte)(SerialNumber >> 8));
                ret.Add(0x00);
                ret.Add(0x00);
                ret.Add((byte)NetworkNumber);
                ret.Add((byte)PcNumber);
                ret.Add((byte)IoNumber);
                ret.Add((byte)(IoNumber >> 8));
                ret.Add((byte)ChannelNumber);
                ret.Add((byte)dataLength);
                ret.Add((byte)(dataLength >> 8));
                ret.Add((byte)CpuTimer);
                ret.Add((byte)(CpuTimer >> 8));
                ret.Add((byte)iMainCommand);
                ret.Add((byte)(iMainCommand >> 8));
                ret.Add((byte)iSubCommand);
                ret.Add((byte)(iSubCommand >> 8));
                ret.AddRange(iData);
                return ret.ToArray();
            }
            public int SetResponse(byte[] iResponse)
            {
                int min;
                switch (FrameType)
                {
                    case McFrame.MC1E:
                        min = 2;
                        if (min <= iResponse.Length)
                        {
                            ResultCode = (int)iResponse[min - 2];
                            Response = new byte[iResponse.Length - 2];
                            Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
                        }
                        break;
                    case McFrame.MC3E:
                        min = 11;
                        if (min <= iResponse.Length)
                        {
                            var btCount = new[] { iResponse[min - 4], iResponse[min - 3] };
                            var btCode = new[] { iResponse[min - 2], iResponse[min - 1] };
                            int rsCount = BitConverter.ToUInt16(btCount, 0);
                            ResultCode = BitConverter.ToUInt16(btCode, 0);
                            Response = new byte[rsCount - 2];
                            Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
                        }
                        break;
                    case McFrame.MC4E:
                        min = 15;
                        if (min <= iResponse.Length)
                        {
                            var btCount = new[] { iResponse[min - 4], iResponse[min - 3] };
                            var btCode = new[] { iResponse[min - 2], iResponse[min - 1] };
                            int rsCount = BitConverter.ToUInt16(btCount, 0);
                            ResultCode = BitConverter.ToUInt16(btCode, 0);
                            Response = new byte[rsCount - 2];
                            Buffer.BlockCopy(iResponse, min, Response, 0, Response.Length);
                        }
                        break;
                    default:
                        throw new Exception("Frame type not supported.");

                }
                return ResultCode;
            }
            public bool IsIncorrectResponse(byte[] iResponse, int minLenght)
            {
                //TEST add 1E frame
                switch (this.FrameType)
                {
                    case McFrame.MC1E: return ((iResponse.Length < minLenght));

                    case McFrame.MC3E:
                    case McFrame.MC4E:
                        var btCount = new[] { iResponse[minLenght - 4], iResponse[minLenght - 3] };
                        var btCode = new[] { iResponse[minLenght - 2], iResponse[minLenght - 1] };
                        var rsCount = BitConverter.ToUInt16(btCount, 0) - 2;
                        var rsCode = BitConverter.ToUInt16(btCode, 0);
                        return (rsCode == 0 && rsCount != (iResponse.Length - minLenght));

                    default: throw new Exception("Type Not supported");

                }
            }
        }

        private readonly object balanceLock = new object();
        protected byte[] Execute(byte[] iCommand)
        {
            lock (balanceLock)
            {
                List<byte> list = new List<byte>();

                NetworkStream ns = Stream;
                ns.Write(iCommand, 0, iCommand.Length);
                ns.Flush();

                using (var ms = new MemoryStream())
                {
                    var buff = new byte[256];
                    do
                    {
                        int sz = ns.Read(buff, 0, buff.Length);
                        if (sz == 0)
                        {
                            throw new Exception("已断开连接");
                        }
                        ms.Write(buff, 0, sz);
                    }
                    while (ns.DataAvailable);

                    return ms.ToArray();
                }
            }
        }

        #region INT16
        public short ReadInt16(string address)
        {
            try
            {
                return ReadInt16(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public short[] ReadInt16(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count);//一个地址2个字节
                short[] values = new short[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToInt16(data, 0);
                }
                return values;

            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteInt16(string address, short value)
        {
            try
            {
                WriteInt16(address, new short[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteInt16(string address, short[] value)
        {
            try
            {
                ushort[] values = new ushort[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToUInt16(data, 0);
                }
                WriteDeviceBlock(address, values);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region UINT16
        public ushort ReadUInt16(string address)
        {
            try
            {
                return ReadUInt16(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public ushort[] ReadUInt16(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count);//一个地址2个字节
                ushort[] values = new ushort[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToUInt16(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteUInt16(string address, ushort value)
        {
            try
            {
                WriteUInt16(address, new ushort[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteUInt16(string address, ushort[] value)
        {
            try
            {
                ushort[] values = new ushort[value.Length];
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToUInt16(data, 0);
                }
                WriteDeviceBlock(address, values);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region INT32
        public int ReadInt32(string address)
        {
            try
            {
                return ReadInt32(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public int[] ReadInt32(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
                int[] values = new int[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
                    byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
                    List<byte> list = new List<byte>();
                    list.AddRange(data1);
                    list.AddRange(data2);
                    byte[] data = list.ToArray();
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToInt32(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteInt32(string address, int value)
        {
            try
            {
                WriteInt32(address, new int[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteInt32(string address, int[] value)
        {
            try
            {
                List<ushort> values = new List<ushort>();
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    ushort value1 = BitConverter.ToUInt16(data, 0);
                    ushort value2 = BitConverter.ToUInt16(data, 2);
                    values.Add(value1);
                    values.Add(value2);
                }
                WriteDeviceBlock(address, values.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region UINT32
        public uint ReadUInt32(string address)
        {
            try
            {
                return ReadUInt32(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public uint[] ReadUInt32(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
                uint[] values = new uint[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
                    byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
                    List<byte> list = new List<byte>();
                    list.AddRange(data1);
                    list.AddRange(data2);
                    byte[] data = list.ToArray();
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToUInt32(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteUInt32(string address, uint value)
        {
            try
            {
                WriteUInt32(address, new uint[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteUInt32(string address, uint[] value)
        {
            try
            {
                List<ushort> values = new List<ushort>();
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    ushort value1 = BitConverter.ToUInt16(data, 0);
                    ushort value2 = BitConverter.ToUInt16(data, 2);
                    values.Add(value1);
                    values.Add(value2);
                }
                WriteDeviceBlock(address, values.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region FLOAT
        public float ReadFloat(string address)
        {
            try
            {
                return ReadFloat(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public float[] ReadFloat(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count * 2);//一个地址2个字节
                float[] values = new float[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data1 = BitConverter.GetBytes(value[2 * i + 0]);
                    byte[] data2 = BitConverter.GetBytes(value[2 * i + 1]);
                    List<byte> list = new List<byte>();
                    list.AddRange(data1);
                    list.AddRange(data2);
                    byte[] data = list.ToArray();
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToSingle(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteFloat(string address, float value)
        {
            try
            {
                WriteFloat(address, new float[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteFloat(string address, float[] value)
        {
            try
            {
                List<ushort> values = new List<ushort>();
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    ushort value1 = BitConverter.ToUInt16(data, 0);
                    ushort value2 = BitConverter.ToUInt16(data, 2);
                    values.Add(value1);
                    values.Add(value2);
                }
                WriteDeviceBlock(address, values.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion


        #region Double
        public double ReadDouble(string address)
        {
            try
            {
                return ReadDouble(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public double[] ReadDouble(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count * 4);
                double[] values = new double[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data1 = BitConverter.GetBytes(value[4 * i + 0]);
                    byte[] data2 = BitConverter.GetBytes(value[4 * i + 1]);
                    byte[] data3 = BitConverter.GetBytes(value[4 * i + 2]);
                    byte[] data4 = BitConverter.GetBytes(value[4 * i + 3]);
                    List<byte> list = new List<byte>();
                    list.AddRange(data1);
                    list.AddRange(data2);
                    list.AddRange(data3);
                    list.AddRange(data4);
                    byte[] data = list.ToArray();
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToDouble(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteDouble(string address, double value)
        {
            try
            {
                WriteDouble(address, new double[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteDouble(string address, double[] value)
        {
            try
            {
                List<ushort> values = new List<ushort>();
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    ushort value1 = BitConverter.ToUInt16(data, 0);
                    ushort value2 = BitConverter.ToUInt16(data, 2);
                    ushort value3 = BitConverter.ToUInt16(data, 4);
                    ushort value4 = BitConverter.ToUInt16(data, 6);
                    values.Add(value1);
                    values.Add(value2);
                    values.Add(value3);
                    values.Add(value4);
                }
                WriteDeviceBlock(address, values.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

        #region Int64
        public long ReadInt64(string address)
        {
            try
            {
                return ReadInt64(address, 1)[0];
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public long[] ReadInt64(string address, int count = 1)
        {
            try
            {
                //地址解析 
                ushort[] value = ReadDeviceBlock(address, count * 4);
                long[] values = new long[count];
                for (int i = 0; i < count; i++)
                {
                    byte[] data1 = BitConverter.GetBytes(value[4 * i + 0]);
                    byte[] data2 = BitConverter.GetBytes(value[4 * i + 1]);
                    byte[] data3 = BitConverter.GetBytes(value[4 * i + 2]);
                    byte[] data4 = BitConverter.GetBytes(value[4 * i + 3]);
                    List<byte> list = new List<byte>();
                    list.AddRange(data1);
                    list.AddRange(data2);
                    list.AddRange(data3);
                    list.AddRange(data4);
                    byte[] data = list.ToArray();
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    values[i] = BitConverter.ToInt64(data, 0);
                }
                return values;
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        public void WriteInt64(string address, long value)
        {
            try
            {
                WriteInt64(address, new long[] { value });
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }


        public void WriteInt64(string address, long[] value)
        {
            try
            {
                List<ushort> values = new List<ushort>();
                for (int i = 0; i < value.Length; i++)
                {
                    byte[] data = BitConverter.GetBytes(value[i]);
                    if (BitConverter.IsLittleEndian)
                    {
                        Array.Reverse(data);
                    }
                    ushort value1 = BitConverter.ToUInt16(data, 0);
                    ushort value2 = BitConverter.ToUInt16(data, 2);
                    ushort value3 = BitConverter.ToUInt16(data, 4);
                    ushort value4 = BitConverter.ToUInt16(data, 6);
                    values.Add(value1);
                    values.Add(value2);
                    values.Add(value3);
                    values.Add(value4);
                }
                WriteDeviceBlock(address, values.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        #endregion

    }
    public enum McFrame
    {
        MC1E = 4,
        MC3E = 11,
        MC4E = 15
    }

    /// <summary>
    /// 定义 PLC 设备类型的枚举
    /// </summary>
    public enum PlcDeviceType
    {
        // PLC设备
        M = 0x90
      , SM = 0x91
      , L = 0x92
      , F = 0x93
      , V = 0x94
      , S = 0x98
      , X = 0x9C
      , Y = 0x9D
      , B = 0xA0
      , SB = 0xA1
      , DX = 0xA2
      , DY = 0xA3
      , D = 0xA8
      , SD = 0xA9
      , R = 0xAF
      , ZR = 0xB0
      , W = 0xB4
      , SW = 0xB5
      , TC = 0xC0
      , TS = 0xC1
      , TN = 0xC2
      , CC = 0xC3
      , CS = 0xC4
      , CN = 0xC5
      , SC = 0xC6
      , SS = 0xC7
      , SN = 0xC8
      , Z = 0xCC
      , TT
      , TM
      , CT
      , CM
      , A
      , Max
    }
}
