using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using System.Threading;
using System.IO.Ports;
using Modbus.Device;
using Common;

namespace MoonControl
{
    public delegate void CommandExcuteHandler(string cmd, StringBuilder req, StringBuilder res);
    public class PLCControlSerial
    {
        private static readonly int TRYCOUNT = 5;               //重测次数
        private static readonly int WAITTIME = 500;             //单位毫秒
        /// <summary>
        /// 命令执行结束事件
        /// </summary>
        public event CommandExcuteHandler CommandFinished;

        private const ushort RD4000 = 0x1FA0;                   //设备状态检测1	设备空闲可以测试 2	光栅遮挡 3	气缸不在原点位 4	伺服报错
        private const ushort RD4002 = 0x1FA2;                   //1	请求测试darkroom
        private const ushort RD4003 = 0x1FA3;                   //1	请求测试SFR, 3	请求测试IR
        private const ushort RD4004 = 0x1FA4;                   //1	请求测试Blemish
        private const ushort RD4005 = 0x1FA5;                   //1	转盘转盘完成
        private const ushort RD4006 = 0x1FA6;                   //1 start 按键按下

        private const ushort WD4100 = 0x9004;                   //1	测试软件打开, 2	测试软件关闭
        private const ushort WD3100 = 0x1C1C;                   //home 位置扫描到sn(1=有/0=无)
        private const ushort WD3022 = 0x1BCE;                   //darkroom 测试, 1=OK/2=NG
        private const ushort WD3404 = 0x1D4C;                   //SFR测试, 1=OK/2=NG
        private const ushort WD3406 = 0x1D4E;                   //IR测试, 1=OK/2=NG
        private const ushort WD3308 = 0x1CEC;                   //Blemish 测试,  1=OK/2=NG

        private SerialPort m_serialPort = null;
        private IModbusSerialMaster master = null;


        public string Port
        {
            get;
            set;
        }

        public int BaudRate
        {
            get;
            set;
        }

        public Parity Parity
        {
            get;
            set;
        }

        public int DataBits
        {
            get;
            set;
        }

        public StopBits StopBits
        {
            get;
            set;
        }


        /// <summary>
        /// 从站地址
        /// </summary>
        public byte Slave_addr
        { get; set; }


        public PLCControlSerial()
        {
            BaudRate = 9600;
            Parity = System.IO.Ports.Parity.Even;
            DataBits = 8;
            StopBits = System.IO.Ports.StopBits.One;
            Slave_addr = 1;
        }

        protected void OnCommandFinished(string cmd, StringBuilder req, StringBuilder res)
        {
            if (CommandFinished != null)
            {
                Task.Factory.StartNew(() =>
                {
                    CommandFinished(cmd, req, res);
                });
            }
        }

        # region 串口模式

        protected Tuple<bool, string> OpenSerial(string com, int baudRate = 9600, Parity parity = Parity.Even, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            Port = com;
            BaudRate = baudRate;
            Parity = parity;
            DataBits = dataBits;
            StopBits = stopBits;

            try
            {
                if (m_serialPort == null)
                {
                    m_serialPort = new SerialPort(Port, BaudRate, this.Parity, this.DataBits, this.StopBits);
                }
                if (m_serialPort.IsOpen == false)
                    m_serialPort.Open();
                if (master == null)
                    master = ModbusSerialMaster.CreateRtu(m_serialPort);
                return new Tuple<bool, string>(true, "port opne ok");
            }
            catch (Exception ex)
            {
                return new Tuple<bool, string>(false, ex.Message);
            }
        }

        private void CloseSerial()
        {
            if (m_serialPort != null)
                m_serialPort.Close();
        }

        #endregion

        #region 软件连接

        /// <summary>
        /// 通知plc软件已打开, 
        /// </summary>
        /// <param name="com">端口</param>
        /// <param name="baudrate">波特率</param>
        /// <param name="product"></param>
        /// <returns></returns>
        public Tuple<bool, string> TestSoftwaetOpen(string com, int baudrate=9600)
        {
            try
            {
                var rtn = this.OpenSerial(com, baudrate);
                if (rtn.Item1)
                {
                    this.master.WriteSingleRegister(this.Slave_addr, WD4100, 1);
                    return new Tuple<bool, string>(true, "OK");
                }
                else
                {
                    return rtn;
                }
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("TestSoftwaetOpen  Fail：" + ex.Message);
                return new Tuple<bool, string>(false, ex.Message);
            }
        }

        /// <summary>
        /// 通知plc测试软件已关闭, 并关闭plc连接
        /// </summary>
        /// <param name="product"></param>
        /// <returns></returns>
        public Tuple<bool,string> TestSoftwaetClose(string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD4100, 2);
                this.CloseSerial();
                return new Tuple<bool, string>(true, "OK");
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("TestSoftwaetClose  Fail：" + ex.Message);
                return new Tuple<bool, string>(false, ex.Message);
            }
        }

        #endregion

        #region 测试步骤


        /// <summary>
        /// 设备状态检测
        /// </summary>
        /// <param name="product"></param>
        /// <returns></returns>
        public Tuple<bool, string> DeviceStatus()
        {
            try
            {
                ushort[] daas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4000, 1);
                if (daas != null && daas.Length >= 1)
                {
                    if (daas[0] == 1)
                    {
                        return new Tuple<bool, string>(true, "设备空闲可以测试");
                    }
                    else if (daas[0] == 2)
                    {
                        FileWriter.WriteLog("DeviceStatus ：" + "光栅遮挡");
                        return new Tuple<bool, string>(false, "光栅遮挡");
                    }
                    else if (daas[0] == 3)
                    {
                        FileWriter.WriteLog("DeviceStatus ：" + "气缸不在原点位");
                        return new Tuple<bool, string>(false, "气缸不在原点位");
                    }
                    else if (daas[0] == 4)
                    {
                        FileWriter.WriteLog("DeviceStatus ：" + "伺服报错");
                        return new Tuple<bool, string>(false, "伺服报错");
                    }
                    else
                    {
                        FileWriter.WriteLog("DeviceStatus ：未知错误, data=" + daas[0]);
                        return new Tuple<bool, string>(false, "未知错误:" + daas[0]);
                    }
                }
                else
                {
                    FileWriter.WriteLog("DeviceStatus fail：读PLC D4000 fail");
                    return new Tuple<bool, string>(false, "读PLC D4000 fail");
                }
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("DeviceStatus exception：" + ex.Message);
                return new Tuple<bool, string>(false, "DeviceStatus exception:" + ex.Message);
            }
        }

        /// <summary>
        /// 开始测试(检测start按键是否按下?),返回true可以测开始试
        /// </summary>
        /// <param name="product"></param>
        /// <returns>start按键按下返回true,否则返回失败</returns>
        public bool StartTest()
        {
            ushort[] datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4006, 1);
            if (datas != null && datas.Length > 0)
            {
                return datas[0] == 1;
            }
            else
            {
                FileWriter.WriteLog("StartTest 未读到数据");
                return false;
            }
        }

        /// <summary>
        /// 转盘旋转状态检测
        /// </summary>
        /// <param name="timeout">超时时间，单位:秒</param>
        /// <param name="product"></param>
        /// <returns>true 转盘旋转到位，否则未到位</returns>
        public bool RotationStatus(int timeout = 30, string product = "AS")
        {
            ushort[] datas = null;
            DateTime endtime = DateTime.Now.AddSeconds(timeout);
            while (endtime >= DateTime.Now)
            {
                datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4005, 1);
                if (datas != null && datas.Length > 0 && datas[0] == 1)
                {
                    return true;
                }
                Thread.Sleep(WAITTIME);
            }
            if (datas != null)
                FileWriter.WriteLog("RotationStatus fail :" + string.Format("value={0}", datas[0]));
            else
                FileWriter.WriteLog("RotationStatus fail: 未响应");
            return false;
        }

        /// <summary>
        /// 暗室测试
        /// </summary>
        /// <param name="timeout">超时时间，单位:秒</param>
        /// <param name="product"></param>
        /// <returns>返回true 暗室测试准备好，可以测试</returns>
        public bool DarkroomTest(int timeout=30, string product = "AS")
        {
            ushort[] datas = null;
            DateTime endtime = DateTime.Now.AddSeconds(timeout);
            while (endtime >= DateTime.Now)
            {
                datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4002, 1);
                if (datas != null && datas.Length > 0 && datas[0] == 1)
                {
                    return true;
                }
                Thread.Sleep(WAITTIME);
            }
            if (datas != null)
                FileWriter.WriteLog("DarkroomTest fail :" + string.Format("value={0}", datas[0]));
            else
                FileWriter.WriteLog("DarkroomTest fail: 未响应");
            return false;
        }

        /// <summary>
        /// SFR测试(SFR和IR在一个位置)
        /// </summary>
        /// <param name="timeout">超时时间，单位:秒</param>
        /// <param name="product"></param>
        /// <returns>返回true, SFR准备好，可以测试</returns>
        public bool SFRTest(int timeout = 30, string product = "AS")
        {
            ushort[] datas = null;
            DateTime endtime = DateTime.Now.AddSeconds(timeout);
            while (endtime >= DateTime.Now)
            {
                datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4003, 1);
                if (datas != null && datas.Length > 0 && datas[0] == 1)
                {
                    return true;
                }
                Thread.Sleep(WAITTIME);
            }
            if (datas != null)
                FileWriter.WriteLog("SFRTest fail :" + string.Format("value={0}", datas[0]));
            else
                FileWriter.WriteLog("SFRTest fail: 未响应");
            return false;
        }

        /// <summary>
        /// IR测试(SFR和IR在一个位置)
        /// </summary>
        /// <param name="timeout">超时时间，单位:秒</param>
        /// <param name="product"></param>
        /// <returns>返回true， IR准备好，可以测试</returns>
        public bool IRTest(int timeout = 30, string product = "AS")
        {
            ushort[] datas = null;
            DateTime endtime = DateTime.Now.AddSeconds(timeout);
            while (endtime >= DateTime.Now)
            {
                datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4003, 1);
                if (datas != null && datas.Length > 0 && datas[0] == 3)
                {
                    return true;
                }
                Thread.Sleep(WAITTIME);
            }
            if (datas != null)
                FileWriter.WriteLog("IRTest fail :" + string.Format("value={0}", datas[0]));
            else
                FileWriter.WriteLog("IRTest fail: 未响应");
            return false;
        }

        /// <summary>
        /// Blemish测试
        /// </summary>
        /// <param name="timeout">超时时间，单位:秒</param>
        /// <param name="product"></param>
        /// <returns>返回true， Blemish准备好，可以测试</returns>
        public bool BlemishTest(int timeout = 30, string product = "AS")
        {
            ushort[] datas = null;
            DateTime endtime = DateTime.Now.AddSeconds(timeout);
            while (endtime >= DateTime.Now)
            {
                datas = this.master.ReadHoldingRegisters(this.Slave_addr, RD4004, 1);
                if (datas != null && datas.Length > 0 && datas[0] == 1)
                {
                    return true;
                }
                Thread.Sleep(WAITTIME);
            }
            if (datas != null)
                FileWriter.WriteLog("BlemishTest fail :" + string.Format("value={0}", datas[0]));
            else
                FileWriter.WriteLog("BlemishTest fail: 未响应");
            return false;
        }

        /// <summary>
        /// 扫描到sn，写入寄存器
        /// </summary>
        /// <param name="status"></param>
        /// <param name="product"></param>
        /// <returns></returns>
        public bool ScannerStatus(bool status, string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD3100, (ushort)(status ? 1 : 2));
                return true;
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("ScannerStatus exception :" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 设置darkroom的测试状态
        /// </summary>
        /// <param name="status">成功:true, false:失败</param>
        /// <param name="product"></param>
        /// <returns>设置成功返回true,否则返回false</returns>
        public bool SetDarkroomTestStatus(bool status, string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD3022, (ushort)(status ? 1 : 2));
                return true;
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("SetDarkroomTestStatus exception :" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 设置SFR的测试状态
        /// </summary>
        /// <param name="status">成功:true, false:失败</param>
        /// <param name="product"></param>
        /// <returns>设置成功返回true,否则返回false</returns>
        public bool SetSFRTestStatus(bool status, string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD3404, (ushort)(status ? 1 : 2));
                return true;
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("SetSFRTestStatus exception :" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 设置IR的测试状态
        /// </summary>
        /// <param name="status">成功:true, false:失败</param>
        /// <param name="product"></param>
        /// <returns>设置成功返回true,否则返回false</returns>
        public bool SetIRTestStatus(bool status, string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD3406, (ushort)(status ? 1 : 2));
                return true;
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("SetIRTestStatus exception :" + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 设置blemish测试状态
        /// </summary>
        /// <param name="status">成功:true, false:失败</param>
        /// <param name="product"></param>
        /// <returns>设置成功返回true,否则返回false</returns>
        public bool SetBlemishTestStatus(bool status, string product = "AS")
        {
            try
            {
                this.master.WriteSingleRegister(this.Slave_addr, WD3308, (ushort)(status ? 1 : 2));
                return true;
            }
            catch (Exception ex)
            {
                FileWriter.WriteLog("SetBlemishTestStatus exception :" + ex.Message);
                return false;
            }
        }
       

        #endregion

    }
}
