using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OnlineStore.DeviceLibrary
{
    public class OKLE
    {
        private byte[] buffer;
        private bool receive;
        private System.IO.Ports.SerialPort port;

        public OKLE()
        {
            port = new System.IO.Ports.SerialPort
            {
                BaudRate = 9600,  //设备支持4800,9600,19200,38400,57600,115200
                DataBits = 8,
                StopBits = System.IO.Ports.StopBits.One,
                Parity = System.IO.Ports.Parity.None
            };
            port.DataReceived += Port_DataReceived;

        }


        /// <summary>
        /// 打开或关闭状态
        /// </summary>
        public bool IsOpen { get { return port.IsOpen; } }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="comName">串口名</param>
        public void Open(string comName)
        {
            port.PortName = comName;
            try
            {
                port.Open();
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void Close()
        {
            port.Close();
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="addr">起始地址</param>
        /// <returns></returns>
        public int Read(short addr)
        {
            if (!IsOpen) return 0;

            byte[] cmd = ReadCmd(addr, 1);
            receive = false;
            port.Write(cmd, 0, cmd.Length);
            bool rtn = WaitReceive();
            if (rtn) rtn = SameReturn(cmd);
            
            if (rtn)
            {
                int num = buffer[3] * 256 + buffer[4];
                return num;
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="addr">起始地址</param>
        /// <param name="count">寄存器数量</param>
        /// <returns></returns>
        public int[] Read(short addr, short count)
        {
            if (!IsOpen) return null;

            byte[] cmd = ReadCmd(addr, count);
            receive = false;
            port.Write(cmd, 0, cmd.Length);
            bool rtn = WaitReceive();
            if (rtn) rtn = SameReturn(cmd);

            if (rtn)
            {
                int len = buffer[2];  //字节数
                int[] num = new int[len / 2];
                for (int i = 0; i < num.Length; i++)
                {
                    int idx = 3 + i * 2;
                    num[i] = buffer[idx] * 256 + buffer[idx + 1];
                }
                return num;
            }
            else
            {
                return null;
            }
        }







        /// <summary>
        /// 等待数据接收
        /// </summary>
        /// <returns></returns>
        private bool WaitReceive()
        {
            int sum = 0;
            while (true)
            {
                System.Threading.Thread.Sleep(50);
                sum += 50;

                if (receive) return true;
                if (!IsOpen) return false;
                if (sum >= 3000) return false;
            }
        }

        /// <summary>
        /// 串口数据接收
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            System.Threading.Thread.Sleep(50);
            byte[] buff = new byte[port.ReadBufferSize];
            int len = port.Read(buff, 0, buff.Length);
            Array.Copy(buff, buffer, len);
            receive = true;
        }

        /// <summary>
        /// CRC16冗余校验
        /// </summary>
        /// <param name="buff"></param>
        /// <param name="count"></param>
        /// <param name="high"></param>
        /// <param name="low"></param>
        private void CRC16(byte[] buff, int count, out byte high, out byte low)
        {
            int crc = 0xFFFF;
            int bit;

            for (int i = 0; i < count; i++)
            {
                crc ^= buff[i];
                for (int j = 0; j < 8; j++)
                {
                    bit = crc & 1;
                    crc >>= 1;
                    if (bit == 0)
                    {
                        //crc >>= 1;
                    }
                    else
                        crc ^= 0xA001;
                }
            }

            low = Convert.ToByte(crc & 255);
            high = Convert.ToByte((crc >> 8) & 255);
        }

        /// <summary>
        /// 读取命令,功能码03
        /// </summary>
        /// <param name="addr"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        private byte[] ReadCmd(short addr, short count)
        {
            byte[] addrArr = BitConverter.GetBytes(addr);
            byte[] countArr = BitConverter.GetBytes(count);

            byte[] cmd = new byte[8];
            cmd[0] = 0x01;         //模块地址
            cmd[1] = 0x03;         //功能码
            cmd[2] = addrArr[1];   //高8位
            cmd[3] = addrArr[0];   //低8位
            cmd[4] = countArr[1];  //高8位
            cmd[5] = countArr[0];  //低8位

            //CRC16校验
            CRC16(cmd, 6, out byte high, out byte low);
            cmd[6] = low;
            cmd[7] = high;

            return cmd;
        }

        /// <summary>
        /// 判断是同一条命令的返回值
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        private bool SameReturn(byte[] cmd)
        {
            //同一个功能码应答
            if (cmd[0] == buffer[0] && cmd[1] == buffer[1])
            {
                //CRC校验是否相同
                int len = cmd.Length - 2;
                CRC16(cmd, len, out byte high, out byte low);
                return cmd[len] == low && cmd[len + 1] == high;
            }
            else
            {
                return false;
            }
        }




    }
}