using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HslCommunication.ModBus;
using HslCommunication;
using HslCommunication.BasicFramework;
using System.Net;
using System.Threading;
using System.Collections;
using System.Timers;
using HslCommunication.Controls;
using parkMonitor.entity;
using parkMonitor.tools;
using parkMonitor.model;
using System.Diagnostics;
using System.Configuration;
using parkMonitor.server.uiLogServer;
using parkMonitor.LOG;
using System.Net.Sockets;

namespace parkMonitor.server
{
    /// <summary>
    /// PLC通信类
    /// </summary>
    public class PLCLinker : IEquipments
    {
        /// <summary>
        /// PLC 连接状态flag
        /// </summary>
        private Boolean isConnection = false;
        /// <summary>
        /// 激光数据记录状态flag
        /// </summary>
        private bool isRecorded = false;
        /// <summary>
        /// 读写异常状态flag
        /// </summary>
        private bool exceptionBreak = false;
        /// <summary>
        /// 系统关闭flag
        /// </summary>
        private bool isClosing = false;
        /// <summary>
        /// 从PLC读到的字符串
        /// </summary>
        private static string json = "";
        /// <summary>
        /// 将写入PLC的字符串
        /// </summary>
        private string json2 = "";
        /// <summary>
        /// 从plc获得结构化数据
        /// </summary>
        private PLCMessage plcMsg;
        /// <summary>
        /// 定时器
        /// </summary>
        System.Timers.Timer timer;
        /// <summary>
        /// 读PLC初始地址
        /// </summary>
        private int startAddr = 0;
        /// <summary>
        /// 读PLC地址长度
        /// </summary>
        private int addrLength = 110;
        /// <summary> 
        /// plc重连标记位
        /// </summary>
        private int linkCount = 0;
        /// <summary>
        /// 无法获取配置文件时使用的PLC刷新频率
        /// </summary>
        private const short PLC_TIME_SCALE = 200;
        /// <summary>
        /// PLC的IP地址
        /// </summary>
        private string ipString;
        /// <summary>
        /// PLC的端口号
        /// </summary>
        private int port;
        /// <summary>
        /// 工作站号
        /// </summary>
        private byte station;
        /// <summary>
        /// 取车启动机械手PLC地址
        /// </summary>
        private int fetching_startRobot_address;
        /// <summary>
        /// 停车启动机械手PLC地址
        /// </summary>
        private int parking_startRobot_address;
        /// <summary>
        /// 停车位ID对应PLC地址
        /// </summary>
        private int parkingSpaceID_address;
        /// <summary>
        /// 停车激光数据入口
        /// </summary>
        private int parkingLaserCenterX_address;
        /// <summary>
        /// 启动激光地址
        /// </summary>
        private int laser_start_address;
        /// <summary>
        /// 取车完成地址
        /// </summary>
        private int fetch_completed_address;
        /// <summary>
        /// 取车完成已获得标志地址
        /// </summary>
        private int fetch_completed_acknowledge_address;
        /// <summary>
        /// 取至缓冲区地址
        /// </summary>
        private int fetch_to_address;
        /// <summary>
        /// 前轮距转存地址
        /// </summary>
        private int frontWheelbase_address;
        /// <summary>
        /// 后轮距转存地址
        /// </summary>
        private int rearWheelbase_address;
        /// <summary>
        /// 停车完成地址
        /// </summary>
        private int park_completed_address;
        /// <summary>
        /// 停车完成已获得地址
        /// </summary>
        private int park_completed_acknowledge_address;
        /// <summary>
        /// 激光重测总数
        /// </summary>
        private int LASER_RESCAN_COUNT;
        /// <summary>
        /// 激光心跳检测窗口阈值
        /// </summary>
        private int LASER_HEARTBEAT_PERIOD;
        /// <summary>
        /// PLC刷新频率，来自于配置文件
        /// </summary>
        private int PLC_refresh_interval;
        /// <summary>
        /// 激光管理list
        /// </summary>
        private List<LaserProcessUnit> laserMgmtList = new List<LaserProcessUnit>();
        /// <summary>
        /// 机械手处理单元实例
        /// </summary>
        private RobotProcessUnit rpu1;
        /// <summary>
        /// PLC列表中被压缩的地址块
        /// </summary>
        private List<string> decompressIndex;

        /// <summary>
        /// plc构造函数
        /// </summary>
        public PLCLinker()
        {
            try
            {
                LASER_RESCAN_COUNT = Int32.Parse(ConfigurationManager.AppSettings.Get("laser_rescan_count"));
                LASER_HEARTBEAT_PERIOD = Int32.Parse(ConfigurationManager.AppSettings.Get("laser_countdown"));
                PLC_refresh_interval = Int32.Parse(ConfigurationManager.AppSettings.Get("PLC_refresh_interval"));

                parking_startRobot_address = Int32.Parse(ConfigurationManager.AppSettings.Get("parking_startRobot_address"));
                parkingLaserCenterX_address = Int32.Parse(ConfigurationManager.AppSettings.Get("parkingLaserCenterX_address"));
                parkingSpaceID_address = Int32.Parse(ConfigurationManager.AppSettings.Get("parkingSpaceID_address"));
                park_completed_address = Int32.Parse(ConfigurationManager.AppSettings.Get("park_completed_address"));
                park_completed_acknowledge_address = Int32.Parse(ConfigurationManager.AppSettings.Get("park_completed_acknowledge_address"));

                fetching_startRobot_address = Int32.Parse(ConfigurationManager.AppSettings.Get("fetching_startRobot_address"));
                fetch_to_address = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_to_address"));
                fetch_completed_address = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_completed_address"));
                fetch_completed_acknowledge_address = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_completed_acknowledge_address"));
                frontWheelbase_address = Int32.Parse(ConfigurationManager.AppSettings.Get("frontWheelbase_address"));
                rearWheelbase_address = Int32.Parse(ConfigurationManager.AppSettings.Get("rearWheelbase_address"));

                laser_start_address = Int32.Parse(ConfigurationManager.AppSettings.Get("laser_start_address"));

                plcMsg = new PLCMessage();
                rpu1 = new RobotProcessUnit(1, parking_startRobot_address, parkingLaserCenterX_address, parkingSpaceID_address, park_completed_address, park_completed_acknowledge_address, fetching_startRobot_address, fetch_to_address, fetch_completed_address, fetch_completed_acknowledge_address, frontWheelbase_address, rearWheelbase_address);

                //激光管理
                for (int i = 1; i < 20; i++)
                {
                    try
                    {
                        if (ConfigurationManager.AppSettings.AllKeys.Contains("laser" + i + "_status_address"))
                        {
                            string laser = ConfigurationManager.AppSettings.Get("laser" + i + "_status_address");
                            int laser_status_address = Int32.Parse(laser);
                            LaserProcessUnit lpu = new LaserProcessUnit(i, laser_start_address, laser_status_address, LASER_RESCAN_COUNT, LASER_HEARTBEAT_PERIOD);
                            laserMgmtList.Add(lpu);
                            plcMsg.laserMsgList.Add(lpu.laserMsg);
                        }
                        else { break; }
                    }
                    catch (Exception) { }
                }

                //定义plc被压缩的地址段
                decompressIndex = new List<string>();
                //25-27
                for (int i = 0; i < 3; i++)
                {
                    decompressIndex.Add((25 + i).ToString());
                }
                //48-53
                for (int i = 0; i < 6; i++)
                {
                    decompressIndex.Add((48 + i).ToString());
                }
                //70-82
                for (int i = 0; i < 13; i++)
                {
                    decompressIndex.Add((70 + i).ToString());
                }
            }
            catch (Exception)
            {
                Console.WriteLine("PLC相关配置文件异常");
            }
        }

        //**************************************** PLC读写相关方法 **************************************
        /// <summary>
        /// PLC连接对象
        /// </summary>
        private ModBusTcpClient busTcpClient
        {
            get;
            set;
        }
        /// <summary>
        /// 写入字符串到json2
        /// </summary>
        /// <param name="message">将写入的字符串</param>
        private void WriteMessage(string message)
        {
            isRecorded = true;
            //json2 = "[{\"Adress\":\"1\",\"Value\":\"0043\"},{\"Adress\":\"2\",\"Value\":\"0043\"}]";
            json2 = message;
        }

        /// <summary>
        /// 采集数据显示转json
        /// </summary>
        /// <param name="str">将被转为json字符串的字符串</param>
        private static void DisplayString_ToJson(string str)
        {
            List<PLCNode> return_Value = new List<PLCNode>();
            if (!string.IsNullOrEmpty(str))
            {
                ArrayList array = GetSeparateSubString(str, 4);
                int Read_Address = 0;//0地址
                return_Value.Clear();
                foreach (string arr in array)
                {
                    PLCNode rv = new PLCNode();
                    //Console.WriteLine("地址:" + Read_Address + ": " + arr);
                    rv.Address = Read_Address.ToString();
                    rv.Value = Int32.Parse(arr, System.Globalization.NumberStyles.HexNumber).ToString();//plc数据16进制？
                    return_Value.Add(rv);
                    Read_Address++;
                }
            }
            json = JsonHelper.SerializeObject(return_Value);//序列化     
            //List<PLCValue> prv = JsonHelper.DeserializeJsonToList<PLCValue>(json);

            //Console.WriteLine(json);
        }

        /// <summary>
        /// 截取字符串到字符串数组
        /// </summary>
        /// <param name="txtString">输入字符串</param>
        /// <param name="charNumber">最大字符个数</param>
        /// <returns></returns>
        private static ArrayList GetSeparateSubString(string txtString, int charNumber)
        {
            ArrayList arrList = new ArrayList();
            string tempStr = txtString;
            for (int i = 0; i < tempStr.Length; i += charNumber)
            {
                if ((tempStr.Length - i) > charNumber)
                {
                    arrList.Add(tempStr.Substring(i, charNumber));
                }
                else
                {
                    arrList.Add(tempStr.Substring(i));
                }
            }
            return arrList;
        }

        /// <summary>
        /// 初始化定时器
        /// </summary>
        private void InitTimer()
        {
            try
            {
                timer = new System.Timers.Timer(PLC_refresh_interval);
            }
            catch (Exception)
            {
                timer = new System.Timers.Timer(PLC_TIME_SCALE);
            }

            timer.AutoReset = true;
            timer.Enabled = true;
            timer.Elapsed += new ElapsedEventHandler(TimerUp);
        }

        /// <summary>
        /// 实时采集
        /// </summary>
        /// <param name="sender">发送者对象，系统提供</param>
        /// <param name="e">定时数据</param>
        private void TimerUp(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                if (isConnection)
                {
                    //从哪个地址开始,读取长度
                    PLC_Read((ushort)startAddr, (ushort)addrLength);
                    //LaserMonitor();
                    plcMsg.originalPlcList = JsonHelper.DeserializeJsonToList<PLCNode>(json);
                }
                else
                {
                    json = "";
                    plcMsg.originalPlcList = new List<PLCNode>();
                    if (EntityForCore.ins.globalStatus)
                    {
                        Reconnect();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private void Reconnect()
        {
            //plc掉线则将系统暂停
            EntityForCore.ins.globalStatus = false;
            //if (linkCount-- >= 0)
            //{
            linkCount = 10;
            Task.Factory.StartNew(() =>
            {
                MyTimer mt = new MyTimer();
                mt.StartTiming();
                while (linkCount-- > 0)
                {
                    mt.EndTiming();
                    int count;
                    if (mt.IsLonger(1, 1, true, out count))
                    {
                        //断线后只显示一次
                        UILogServer.ins.warn("暂停系统，尝试重连PLC");
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "PLC重连中，请稍候。。。。。。");
                    }
                    LinkStart();
                    if (isConnection)
                    {
                        EntityForCore.ins.globalStatus = true;
                        linkCount = 10;
                        exceptionBreak = false;
                        break;
                    }
                    Thread.Sleep(6000);
                }
                if (linkCount <= 0)
                {
                    UILogServer.ins.warn("重连PLC超过尝试次数，请检查硬件，之后手动连接PLC");
                }
            });
            //linkCount = -1;
            //}
        }

        /// <summary>
        /// 从PLC读取数据
        /// </summary>
        /// <param name="address">地址</param>
        /// <param name="length">值</param>
        private void PLC_Read(ushort address, ushort length)
        {
            try
            {
                OperateResult<byte[]> read = busTcpClient.ReadRegister(address, length);
                if (read.IsSuccess)
                {
                    string returned_value = SoftBasic.ByteToHexString(read.Content);
                    DisplayString_ToJson(returned_value);
                }
                else
                {
                    if (!exceptionBreak)
                    {
                        AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                        isConnection = false;
                        exceptionBreak = true;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 写入Json字符串到PLC
        /// </summary>
        /// <param name="json">json格式字符串</param>
        private void WriteJson(string json)
        {
            try
            {
                if (!string.IsNullOrEmpty(json))
                {
                    List<PLCNode> PLC_Value = JsonHelper.DeserializeJsonToList<PLCNode>(json);
                    foreach (var item in PLC_Value)
                    {
                        //WriteResultRender(busTcpClient.WriteRegister(ushort.Parse(item.Address), ushort.Parse(item.Value)), item.Address);
                        busTcpClient.WriteRegister(ushort.Parse(item.Address), ushort.Parse(item.Value));
                        Thread.Sleep(20);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 判断写入是否成功
        /// </summary>
        /// <param name="result">判断结果</param>
        private static void WriteResultRender(OperateResult result)
        {
            if (result.IsSuccess)
            {
                Console.WriteLine("写入成功");
            }
            else
            {
                Console.WriteLine("写入失败");
            }
        }

        /// <summary>
        /// 地址-值写入plc
        /// </summary>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        private void SendtoPLC(string addr, string value)
        {
            PLCNode wrv = new PLCNode();
            wrv.Address = addr;
            wrv.Value = value;
            json2 = "[" + JsonHelper.SerializeObject(wrv) + "]";
            WriteMessage(json2);
            if (isConnection && isRecorded)
            {
                WriteJson(json2);
                isRecorded = false;
            }
        }

        /// <summary>启动与plc的连接</summary>
        private void LinkStart()
        {
            if (busTcpClient != null)
            {
                busTcpClient.ConnectClose();
            }
            busTcpClient = new ModBusTcpClient(ipString, port, station);
            try
            {
                if (busTcpClient != null)
                {
                    OperateResult connect = busTcpClient.ConnectServer();
                    if (connect.IsSuccess)
                    {
                        AsyncCmdServer.ins.send(AsyncCmdType.PLCOnline);
                        UILogServer.ins.info("PLC连接成功");
                        isConnection = true;
                        //linkCount = 0;
                    }
                    else
                    {
                        AsyncCmdServer.ins.send(AsyncCmdType.PLCOffline);
                        //UILogServer.ins.error("PLC连接失败，重试");
                        isConnection = false;
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            InitTimer();//启动计时器
        }

        /// <summary>
        /// 将plc地址块完全解压
        /// </summary>
        /// <param name="plcList">原始PLC数据列表</param>
        /// <param name="decompressIndex">压缩数据段</param>
        /// <returns>解压后的数据列表</returns>
        private List<PLCNode> ListDecompression(List<PLCNode> plcList, List<string> decompressIndex)
        {
            List<string> temp = new List<string>();
            List<PLCNode> decomPlcList = new List<PLCNode>();
            if (plcList != null && plcList.Count != 0 && addrLength > 100)
            {
                try
                {
                    for (int i = 0; i < plcList.Count; i++)
                    {
                        if (!decompressIndex.Contains(plcList[i].Address))
                        {
                            temp.Add(plcList[i].Value);
                        }
                        //定位三个被压缩区域
                        else
                        {
                            //默认LSB优先
                            int value = Int32.Parse(plcList[i].Value);
                            int count = 16;
                            while (count-- > 0)
                            {
                                temp.Add((value & 0x01).ToString());
                                value = value >> 1;
                            }
                        }
                    }
                }
                catch (Exception) { }
            }
            for (int i = 0; i < temp.Count; i++)
            {
                decomPlcList.Add(new PLCNode(i.ToString(), temp[i]));
            }
            return decomPlcList;
        }
        //************************************ 雷达相关方法 **************************************
        /// <summary>
        /// 根据PLC处获得的关于激光的数据，做出相应的操作
        /// </summary>
        private void LaserMonitor()
        {
            try
            {
                if (plcMsg.originalPlcList != null && plcMsg.originalPlcList.Count != 0)
                {
                    foreach (LaserProcessUnit lds in laserMgmtList)
                    {
                        foreach (PLCNode p in plcMsg.originalPlcList)
                        {
                            int addr = Int32.Parse(p.Address);
                            int value = Int32.Parse(p.Value);
                            lds.LaserStatusChecking(addr, value);
                        }
                        lds.LaserRecord(plcMsg.originalPlcList);
                    }
                }
                else
                {
                    AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                    return;
                }
                Thread.Sleep(200);
            }
            catch (Exception)
            {
                AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                return;
            }
        }
        /// <summary>
        /// 实时更新激光状态值
        /// </summary>
        private void UpdateLaserStatus()
        {
            try
            {
                lock (plcMsg)
                {
                    if (plcMsg.originalPlcList != null && plcMsg.originalPlcList.Count != 0)
                    {
                        foreach (LaserProcessUnit lds in laserMgmtList)
                        {
                            foreach (PLCNode p in plcMsg.originalPlcList)
                            {
                                int addr = Int32.Parse(p.Address);
                                int value = Int32.Parse(p.Value);
                                lds.UpdateLaserStatus(addr, value);
                            }
                        }
                    }
                    else
                    {
                        AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                        return;
                    }
                }
            }
            catch (Exception)
            {
                AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                return;
            }
        }
        /// <summary>
        /// 等待激光空闲状态/心跳状态
        /// </summary>
        /// <param name="lpuTemp">激光处理单元</param>
        private void WaitForLaserReady(LaserProcessUnit lpuTemp)
        {
            bool jumpOut = false;
            MyTimer mt = new MyTimer();
            mt.StartTiming();
            lock (lpuTemp)
            {
                while (!jumpOut)
                {
                    if (plcMsg != null && plcMsg.originalPlcList.Count > lpuTemp.laser_status_address)
                    {
                        foreach (PLCNode node in plcMsg.originalPlcList)
                        {
                            //判断激光空闲状态
                            lock (lpuTemp.laserMsg)
                            {
                                if (node.Address.Equals(lpuTemp.laser_status_address.ToString()))
                                {
                                    Console.WriteLine(node.Value+", occupied:"+ lpuTemp.laserMsg.occupied+",id:"+lpuTemp.id);
                                    if ((node.Value.Equals("254") || node.Value.Equals("255")) && !lpuTemp.laserMsg.occupied)
                                    {
                                        lpuTemp.laserMsg.occupied = true;
                                        jumpOut = true;
                                        //UILogServer.ins.log("获取摆扫激光空闲状态，准备启动激光");
                                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "获取摆扫激光空闲状态，准备启动激光");
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    Thread.Sleep(1000);
                    mt.EndTiming();
                    int activationCount = 0;
                    if (mt.IsLonger(15, 1, false, out activationCount))
                    {
                        if (activationCount == 1)
                        {
                            UILogServer.ins.info("启动前暂未获取摆扫激光空闲状态，继续等待");
                        }
                        if (MyTimer.restart && !mt.rolledBack)
                        {
                            mt.rolledBack = true;
                            UILogServer.ins.error("启动前超时未能获取摆扫空闲状态，请检查设备");
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "启动前超时未能获取摆扫空闲状态，请检查设备");
                        }
                    }
                }
            }
        }

        //***************公有方法****************
        /// <summary>
        /// 获取plc数据与激光数据
        /// </summary>
        /// <returns>返回plc消息实例</returns>
        public AbstractMessage GetMessage()
        {
            try
            {
                //if (json != "")
                //{
                //    plcMsg.originalPlcList = JsonHelper.DeserializeJsonToList<PLCNode>(json);
                //}
                //else
                //{
                //    plcMsg.originalPlcList = new List<PLCNode>();
                //}

                if (decompressIndex != null && decompressIndex.Count != 0)
                {
                    plcMsg.extendedPlcList = ListDecompression(plcMsg.originalPlcList, decompressIndex);
                }

                return (PLCMessage)plcMsg.Clone();
            }
            catch (Exception)
            {
                AsyncCmdServer.ins.send(AsyncCmdType.PLCReadException);
                return new PLCMessage();
            }
        }

        /// <summary>
        /// 传入信息写入plc
        /// </summary>
        /// <param name="message">plc消息则传入plc，控制消息则根据指令编号分别处理</param>
        public void SetMessage(AbstractMessage message)
        {
            //if (!isConnection) { UILogServer.ins.error("PLC未连接"); return; }
            if (message.GetType().Equals(typeof(PLCMessage)))
            {
                PLCMessage ps = (PLCMessage)message;
                for (int i = 0; i < ps.originalPlcList.Count; i++)
                {
                    SendtoPLC(ps.originalPlcList[i].Address, ps.originalPlcList[i].Value);
                }
            }
            else if (message.GetType().Equals(typeof(PLCNode)))
            {
                PLCNode pv = (PLCNode)message;
                SendtoPLC(pv.Address, pv.Value);
            }
            else if (message.GetType().Equals(typeof(ControlMessage)))
            {
                ControlMessage cm = (ControlMessage)message;
                if (cm != null && cm.status != 0)
                {
                    switch (cm.status)
                    {
                        //停车startLaser--park_command_address
                        case 1:
                            //start laser scanning for parking
                            LaserProcessUnit lpuTemp = null;
                            Task.Factory.StartNew(() =>
                            {
                                string laser_start_addr = "";
                                foreach (LaserProcessUnit lpu in laserMgmtList)
                                {
                                    if (cm.posID / 6 + 1 == lpu.id)
                                    {
                                        lpuTemp = lpu;
                                        laser_start_addr = lpu.laser_start_address.ToString();
                                        break;
                                    }
                                }
                                if (laser_start_addr == "")
                                    return;
                                lock (lpuTemp)
                                {
                                    WaitForLaserReady(lpuTemp);
                                    SendtoPLC(laser_start_addr, "0");
                                    lpuTemp.laserMsg.licenseNum = cm.LicenseNum;
                                    Thread.Sleep(300);
                                    SendtoPLC(laser_start_addr, cm.posID.ToString());
                                    Thread.Sleep(100);
                                    //SendtoPLC("1", "1");//停车指令置1
                                    Log.WriteLog(LogType.NOT_DATABASE, "已发送激光" + lpuTemp.id + "启动指令");
                                    //UILogServer.ins.info("已发送激光" + lpuTemp.id + "启动指令");
                                }
                            });
                            //Console.WriteLine("扫摆激光启动");
                            break;
                        //停车激光的6个数据,停车机械手,车位信息4个
                        case 2:
                            if (cm.RobotID == 1)
                            {
                                //停车指令置1
                                SendtoPLC("1", "1");
                                //停车启动机械手
                                SendtoPLC(rpu1.parking_startRobot_address.ToString(), "1");
                                //自动或手动输入激光雷达数据
                                SendtoPLC(rpu1.parking_laserCenterX_address.ToString(), cm.centerX);
                                SendtoPLC((rpu1.parking_laserCenterX_address + 1).ToString(), cm.centerY);
                                SendtoPLC((rpu1.parking_laserCenterX_address + 2).ToString(), cm.angleA);
                                //车位信息
                                SendtoPLC(rpu1.parkingSpaceID_address.ToString(), cm.parkingSpaceID);
                                SendtoPLC((rpu1.parkingSpaceID_address + 1).ToString(), cm.parkingSpaceX);
                                SendtoPLC((rpu1.parkingSpaceID_address + 2).ToString(), cm.parkingSpaceY);
                                SendtoPLC((rpu1.parkingSpaceID_address + 3).ToString(), cm.parkingSpaceZ);
                            }
                            break;
                        //停车完成归零--park_completed_address
                        case 3:
                            if (cm.RobotID == 1)
                            {
                                //SendtoPLC(rpu1.park_completed_address.ToString(), "0");//停车完成复位
                                SendtoPLC(rpu1.park_completed_acknowledge_address.ToString(), "0");
                                SendtoPLC("1", "0");//停车指令置0
                                //Console.WriteLine("停车完成");
                            }
                            break;
                        //取车机械手,车位信息4个
                        case 4:
                            if (cm.RobotID == 1 && cm.fetchPosition != 0)
                            {
                                //前后轮轮距
                                SendtoPLC(rpu1.frontWheelbase.ToString(), cm.frontWheelbase.ToString());
                                SendtoPLC(rpu1.rearWheelbase.ToString(), cm.rearWheelbase.ToString());
                                //取至区域
                                //SendtoPLC(rpu1.fetch_to_address.ToString(), pm.fetchPosition.ToString());
                                SendtoPLC(rpu1.fetch_to_address.ToString(), cm.parkingSpaceX);
                                //车位信息
                                SendtoPLC(rpu1.parkingSpaceID_address.ToString(), cm.parkingSpaceID);
                                SendtoPLC((rpu1.parkingSpaceID_address + 1).ToString(), cm.parkingSpaceX);
                                SendtoPLC((rpu1.parkingSpaceID_address + 2).ToString(), cm.parkingSpaceY);
                                SendtoPLC((rpu1.parkingSpaceID_address + 3).ToString(), cm.parkingSpaceZ);
                                SendtoPLC(rpu1.fetching_startRobot_address.ToString(), "1");
                            }
                            break;
                        case 5:
                            if (cm.RobotID == 1)
                            {
                                //取车完成复位
                                SendtoPLC(rpu1.fetch_completed_acknowledge_address.ToString(), "0");
                                SendtoPLC(rpu1.fetch_to_address.ToString(), "0");
                            }
                            break;
                        case 6:
                            if (cm.RobotID == 1)
                            {
                                //停车回滚
                                SendtoPLC(rpu1.parking_startRobot_address.ToString(), "0");
                                SendtoPLC("1", "0");//停车指令置0
                            }
                            break;
                        case 7:
                            if (cm.RobotID == 1)
                            {
                                //取车回滚
                                SendtoPLC(rpu1.fetching_startRobot_address.ToString(), "0");
                                SendtoPLC(rpu1.fetch_to_address.ToString(), "0");
                            }
                            break;
                        case 10:
                            if (!EntityForCore.ins.globalStatus && linkCount <= 0)
                            {
                                Reconnect();
                            }
                            else
                            {
                                UILogServer.ins.warn("无需手动重连PLC");
                            }
                            break;
                        default: Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "输入PLC数据异常"); break;
                    }
                }
            }
            else
            {
                AsyncCmdServer.ins.send(AsyncCmdType.PLCWriteException);
            }
        }

        /// <summary>
        /// 开启激光线程与模拟plc自动重置子线程
        /// </summary>
        public void Start()
        {
            exceptionBreak = false; linkCount = 0;
            try
            {
                ipString = ConfigurationManager.AppSettings.Get("PLC_ip_address");
                port = Int32.Parse(ConfigurationManager.AppSettings.Get("PLC_port"));
                station = (byte)Int32.Parse(ConfigurationManager.AppSettings.Get("PLC_station"));
                startAddr = Int32.Parse(ConfigurationManager.AppSettings.Get("PLC_start_address"));
                addrLength = Int32.Parse(ConfigurationManager.AppSettings.Get("PLC_address_length"));
            }
            catch (Exception) { UILogServer.ins.error("PLC配置文件异常"); Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "PLC配置文件异常"); }
            //for (int i = startAddr; i < startAddr + addrLength; i++)
            //{
            //    plcMsg.originalPlcList.Add(new PLCNode(i.ToString(), "0"));
            //}
            plcMsg.originalPlcList = new List<PLCNode>();
            LinkStart();

            Task.Factory.StartNew(() =>
            {
                while (!isClosing)
                {
                    LaserMonitor();
                }
            });
            Task.Factory.StartNew(() =>
            {
                while (!isClosing)
                {
                    UpdateLaserStatus();
                }
            });

            ////模拟plc与激光自动操作
            //Task.Factory.StartNew(() =>
            //{
            //    laserAnim();
            //});
            //Task.Factory.StartNew(() =>
            //{
            //    wheelbaseAnim();
            //});
            //Task.Factory.StartNew(() =>
            //{
            //    autoCycling();
            //});
        }

        /// <summary>
        /// 系统停止
        /// </summary>
        public void Stop()
        {
            exceptionBreak = false; isClosing = true;
            busTcpClient.ConnectClose();//stop释放资源
            //throw new NotImplementedException();
        }

        /// <summary>
        /// 模拟摆扫激光
        /// </summary>
        private void laserAnim()
        {
            bool laserWorking = false;
            while (!isClosing)
            {
                if (plcMsg.originalPlcList != null)
                {
                    //模拟plc与激光操作
                    foreach (PLCNode p in plcMsg.originalPlcList)
                    {
                        int addr = Int32.Parse(p.Address);
                        int value = Int32.Parse(p.Value);
                        //模拟摆扫激光
                        if (laserMgmtList.Count != 0)
                        {
                            if (addr == laser_start_address)
                            {
                                //心跳
                                if (value == 0)
                                {
                                    laserWorking = false;
                                    SendtoPLC(laserMgmtList[0].laser_status_address.ToString(), "254");
                                    Thread.Sleep(500);
                                    SendtoPLC(laserMgmtList[0].laser_status_address.ToString(), "255");
                                    Thread.Sleep(500);
                                }
                                //模拟测量
                                else
                                {
                                    if (!laserWorking)
                                    {
                                        SendtoPLC(laserMgmtList[0].laser_status_address.ToString(), "1");
                                        Thread.Sleep(300);
                                        SendtoPLC(laserMgmtList[0].laser_status_address.ToString(), "2");
                                        Thread.Sleep(500);
                                        SendtoPLC((laserMgmtList[0].laser_status_address + 1).ToString(), (new Random(DateTime.Now.Millisecond).Next(4000, 7000)).ToString());
                                        SendtoPLC((laserMgmtList[0].laser_status_address + 2).ToString(), (new Random(DateTime.Now.Millisecond).Next(4000, 7000)).ToString());
                                        SendtoPLC((laserMgmtList[0].laser_status_address + 3).ToString(), (new Random(DateTime.Now.Millisecond).Next(90, 150)).ToString());
                                        SendtoPLC(laserMgmtList[0].laser_status_address.ToString(), "3");
                                        Thread.Sleep(500);
                                    }
                                    laserWorking = true;
                                }
                            }
                        }
                    }
                }
                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// 模拟轮距雷达
        /// </summary>
        private void wheelbaseAnim()
        {
            bool working = false;
            while (!isClosing)
            {
                if (plcMsg.originalPlcList != null)
                {
                    //模拟plc与激光操作
                    foreach (PLCNode p in plcMsg.originalPlcList)
                    {
                        int addr = Int32.Parse(p.Address);
                        int value = Int32.Parse(p.Value);
                        //模拟轮距激光
                        if (addr == parking_startRobot_address)
                        {
                            if (value == 1)
                            {
                                if (!working)
                                {
                                    working = true;
                                    Thread.Sleep(500);
                                    SendtoPLC(frontWheelbase_address.ToString(), (new Random(DateTime.Now.Millisecond).Next(100, 300)).ToString());
                                    Thread.Sleep(1);
                                    SendtoPLC(rearWheelbase_address.ToString(), (new Random(DateTime.Now.Millisecond).Next(100, 300)).ToString());
                                    SendtoPLC("87", "3");
                                    Thread.Sleep(500);
                                }
                            }
                            else
                            {
                                working = false;
                                SendtoPLC("87", "254");
                                Thread.Sleep(500);
                                SendtoPLC("87", "255");
                                Thread.Sleep(500);
                            }
                        }
                    }
                }
                Thread.Sleep(200);
            }
        }

        /// <summary>
        /// 模拟PLC操作
        /// </summary>
        private void autoCycling()
        {
            while (!isClosing)
            {
                if (plcMsg.originalPlcList != null)
                {
                    //模拟plc与激光操作
                    foreach (PLCNode p in plcMsg.originalPlcList)
                    {
                        int addr = Int32.Parse(p.Address);
                        int value = Int32.Parse(p.Value);
                        //停取车复位
                        if (addr == park_completed_acknowledge_address && value == 0)
                        {
                            Thread.Sleep(1000);
                            SendtoPLC(park_completed_address.ToString(), "0");
                            SendtoPLC(park_completed_acknowledge_address.ToString(), "1");
                        }
                        if (addr == fetch_completed_acknowledge_address && value == 0)
                        {
                            Thread.Sleep(1000);
                            SendtoPLC(fetch_completed_address.ToString(), "0");
                            SendtoPLC(fetch_completed_acknowledge_address.ToString(), "1");
                        }
                        //停车完成
                        if (addr == parking_startRobot_address && value == 1)
                        {
                            Thread.Sleep(2000);
                            SendtoPLC(park_completed_address.ToString(), "1");
                            SendtoPLC(addr.ToString(), "0");
                        }
                        //取车完成
                        if (addr == fetching_startRobot_address && value == 1)
                        {
                            Thread.Sleep(2000);
                            SendtoPLC(fetch_completed_address.ToString(), "1");
                            SendtoPLC(addr.ToString(), "0");
                        }
                    }
                }
                Thread.Sleep(200);
            }
        }
    }

    /// <summary>
    /// 激光数据记录与处理类
    /// </summary>
    class LaserProcessUnit
    {
        /// <summary>
        /// 激光启动对应PLC地址
        /// </summary>
        public int laser_start_address { get; set; }
        /// <summary>
        /// 激光状态对应PLC地址
        /// </summary>
        public int laser_status_address { get; set; }
        /// <summary>
        /// 激光id
        /// </summary>
        public int id { get; set; }
        private int LASER_RESCAN_COUNT, LASER_HEARTBEAT_PERIOD, laser_rescan_countdown, laser_heartbeat_countdown;
        private bool laser_record, laser_heartbeat_test, enable_status_check = true, disordered = false;
        private HashSet<int> laser_heartbeat = new HashSet<int>();
        /// <summary>
        /// plc类句柄
        /// </summary>
        private IEquipments plc = null;
        /// <summary>
        /// 激光消息，用于保存激光数据、状态等信息
        /// </summary>
        public LaserMessage laserMsg = new LaserMessage();

        /// <summary>
        /// 激光处理单元构造函数，初始化各属性值
        /// </summary>
        /// <param name="id">编号</param>
        /// <param name="park_command_address">停车指令地址</param>
        /// <param name="laser_status_address">激光状态地址</param>
        /// <param name="laser_rescan_count">激光重测次数</param>
        /// <param name="laser_heartbeat_period">激光心跳时间窗口</param>
        public LaserProcessUnit(int id, int park_command_address, int laser_status_address, int laser_rescan_count, int laser_heartbeat_period)
        {
            try
            {
                laserMsg.id = id;
                this.id = id;
                laserMsg.status = 6;
                this.laser_start_address = park_command_address;
                this.laser_status_address = laser_status_address;
                LASER_RESCAN_COUNT = laser_rescan_count;
                LASER_HEARTBEAT_PERIOD = laser_heartbeat_period;
                laser_rescan_countdown = LASER_RESCAN_COUNT;
                laser_heartbeat_countdown = LASER_HEARTBEAT_PERIOD;
                plc = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.PLC);
            }
            catch (Exception)
            {
                UILogServer.ins.error("激光设备配置文件错误");
                Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "激光设备配置文件错误");
            }
        }

        /// <summary>
        /// 更新激光状态
        /// </summary>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        public void UpdateLaserStatus(int addr, int value)
        {
            int status_addr = laser_status_address;
            if (addr == status_addr)
            {
                laserMsg.status = value;
                //UILogServer.ins.log(value.ToString());
                //系统异常状态复位
                if (value != 5 && disordered)
                {
                    disordered = false;
                }
            }
        }

        /// <summary>
        /// 激光状态监测
        /// </summary>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        public void LaserStatusChecking(int addr, int value)
        {
            if (enable_status_check)
            {
                int status_addr = laser_status_address;
                if (addr == status_addr)
                {
                    //after status 0, start to check laser heartbeat
                    if (value == 0)
                    {
                        laserMsg.abort_rescan = false;//就绪状态设置允许重测
                        laser_heartbeat_test = true;
                    }
                    //after status 3 or 4, start to check laser heartbeat
                    if (value == 3 && !laserMsg.recorded && laserMsg.licenseNum != "")
                    {
                        if (plc == null)
                        {
                            plc = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.PLC);
                        }
                        if (plc != null)
                        {
                            PLCNode pn = new PLCNode(laser_start_address.ToString(), "0");
                            plc.SetMessage(pn);
                            //停车指令置0
                            MyTimer mt = new MyTimer();
                            mt.StartTiming();
                            while (laserMsg.status != 254 && laserMsg.status != 255)
                            {
                                Thread.Sleep(1000);
                                mt.EndTiming();
                                int activationCount = 0;
                                if (mt.IsLonger(15, 1, false, out activationCount))
                                {
                                    if (activationCount == 1)
                                    {
                                        UILogServer.ins.info("记录数据前未获得心跳，继续等待");
                                    }
                                    if (MyTimer.restart && !mt.rolledBack)
                                    {
                                        mt.rolledBack = true;
                                        UILogServer.ins.error("记录数据前超时未获得心跳，请检查设备");
                                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "记录数据前超时未获得心跳");
                                        return;
                                    }
                                }
                            }
                            laser_record = true;
                            laser_rescan_countdown = LASER_RESCAN_COUNT;
                            laser_heartbeat_test = true;
                        }
                    }

                    else if (value == 4)
                    {
                        laser_record = false;
                        //启动重测指令
                        if (laser_rescan_countdown > 0)
                        {
                            enable_status_check = false;
                            Task t = Task.Factory.StartNew(() =>
                            {
                                Thread.Sleep(500);
                                if (plc == null)
                                {
                                    plc = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.PLC);
                                }
                                if (plc != null)
                                {
                                    laser_rescan_countdown--;
                                    //停车指令置0
                                    PLCNode pn = new PLCNode(laser_start_address.ToString(), "0");
                                    plc.SetMessage(pn);
                                    //未终止重测，车未开走，停车指令归零后置1
                                    if (!laserMsg.abort_rescan)
                                    {
                                        UILogServer.ins.error("激光" + laserMsg.id + "计算异常，重新测量");
                                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "激光" + laserMsg.id + "计算异常，重新测量");
                                        //重测检测心跳
                                        Task rescan_wait_heartbeat = Task.Factory.StartNew(() =>
                                        {
                                            MyTimer mt = new MyTimer();
                                            mt.StartTiming();
                                            while (laserMsg.status != 254 && laserMsg.status != 255)
                                            {
                                                Thread.Sleep(1000);
                                                mt.EndTiming();
                                                int activationCount = 0;
                                                if (mt.IsLonger(15, 1, false, out activationCount))
                                                {
                                                    if (activationCount == 1)
                                                    {
                                                        UILogServer.ins.info("重测前未获得心跳，继续等待");
                                                    }
                                                    if (MyTimer.restart && !mt.rolledBack)
                                                    {
                                                        mt.rolledBack = true;
                                                        UILogServer.ins.error("发起重测前超时未能获取摆扫激光心跳，请检查设备");
                                                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "发起重测前超时未能获取摆扫激光心跳");
                                                        return;
                                                    }
                                                }
                                            }
                                        });
                                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "获得心跳，准备发起重测");
                                        rescan_wait_heartbeat.Wait();
                                        pn = new PLCNode(laser_start_address.ToString(), "1");
                                        plc.SetMessage(pn);
                                    }
                                }
                                Thread.Sleep(500);
                                enable_status_check = true;
                            });
                        }
                        else if (laser_rescan_countdown == 0)
                        {
                            //激光1异常
                            laser_rescan_countdown = -1;
                            laser_heartbeat_test = false;
                            UILogServer.ins.error("激光" + laserMsg.id + "计算异常超过重测次数，请检查");
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "激光" + laserMsg.id + "计算异常超过重测次数");
                        }
                    }

                    //status 5, system error
                    if (value == 5)
                    {
                        lock (laserMsg)
                        {
                            laser_heartbeat_test = false;
                            if (!disordered)
                            {
                                UILogServer.ins.error("激光" + laserMsg.id + "系统异常,请检查");
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "激光" + laserMsg.id + "系统异常");
                            }
                            disordered = true;
                        }
                    }

                    //find the heartbeat of laser
                    if (laser_heartbeat_test && laser_heartbeat_countdown-- > 0 && value != 3 && value != 4)
                    {
                        if (value == 254 || value == 255)
                            laser_heartbeat.Add(value);
                        if (laser_heartbeat.Contains(254) && laser_heartbeat.Contains(255))
                        {
                            lock (laserMsg)
                            {
                                laserMsg.disconnected = false;
                            }
                            laser_heartbeat_countdown = LASER_HEARTBEAT_PERIOD;
                            laser_heartbeat_test = false;
                            laser_heartbeat.Clear();
                        }
                        //fail to check laser heartbeat
                        if (laser_heartbeat_countdown <= 0)
                        {
                            laser_heartbeat_countdown = LASER_HEARTBEAT_PERIOD;
                            lock (laserMsg)
                            {
                                if (laserMsg.status >= 0 && !laserMsg.disconnected)
                                {
                                    UILogServer.ins.error("激光" + laserMsg.id + "心跳检测失败");
                                    Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "激光" + laserMsg.id + "心跳检测失败");
                                }
                                laserMsg.disconnected = true;
                            }
                        }
                    }
                }
            }
            else { return; }
        }

        /// <summary>
        /// 激光数据记录
        /// </summary>
        /// <param name="plist">plc数据列表</param>
        public void LaserRecord(List<PLCNode> plist)
        {
            lock (laserMsg)
            {
                //激光未掉线，记录数据写入plc
                if (laserMsg.status != -1)
                {
                    if (laser_record)
                    {
                        laser_record = false;
                        foreach (PLCNode p in plist)
                        {
                            int addr = Int32.Parse(p.Address);
                            int value = Int32.Parse(p.Value);
                            if (addr == laser_status_address + 1)
                                laserMsg.data.centerX = value;
                            else if (addr == laser_status_address + 2)
                                laserMsg.data.centerY = value;
                            else if (addr == laser_status_address + 3)
                                laserMsg.data.angleA = value;
                            else if (addr == laser_status_address + 4)
                                laserMsg.data.length = value;
                            else if (addr == laser_status_address + 5)
                                laserMsg.data.width = value;
                            else if (addr == laser_status_address + 6)
                                laserMsg.data.height = value;
                        }
                        laserMsg.recorded = true;
                        UILogServer.ins.info("摆扫激光测量数据已记录");
                    }
                }
            }
        }
    }

    /// <summary>
    /// 机械手逻辑处理类
    /// </summary>
    class RobotProcessUnit
    {
        /// <summary>
        /// 机械手id
        /// </summary>
        public int id { get; set; }
        /// <summary>
        /// 停车启动机械手对应PLC地址
        /// </summary>
        public int parking_startRobot_address { get; set; }
        /// <summary>
        /// 停车激光数据写入入口
        /// </summary>
        public int parking_laserCenterX_address { get; set; }
        /// <summary>
        /// 停车位ID对应PLC地址
        /// </summary>
        public int parkingSpaceID_address { get; set; }
        /// <summary>
        /// 停车完成对应PLC地址
        /// </summary>
        public int park_completed_address { get; set; }
        /// <summary>
        /// 用于停车完成复位PLC地址
        /// </summary>
        public int park_completed_acknowledge_address { get; set; }
        /// <summary>
        /// 取车启动机械手PLC地址
        /// </summary>
        public int fetching_startRobot_address { get; set; }
        /// <summary>
        /// 取车完成PLC地址
        /// </summary>
        public int fetch_completed_address { get; set; }
        /// <summary>
        /// 用于取车完成复位PLC地址
        /// </summary>
        public int fetch_completed_acknowledge_address { get; set; }
        /// <summary>
        /// 取车放置位置PLC地址
        /// </summary>
        public int fetch_to_address { get; set; }
        /// <summary>
        /// 前轮轮距记录
        /// </summary>
        public int frontWheelbase { get; set; }
        /// <summary>
        /// 后轮轮距记录
        /// </summary>
        public int rearWheelbase { get; set; }
        /// <summary>
        /// 构造函数，初始化各属性值
        /// </summary>
        /// <param name="id">编号</param>
        /// <param name="parking_startRobot_address">停车启动机械手地址</param>
        /// <param name="parking_laserCenterX_address">停车激光中心X坐标</param>
        /// <param name="parkingSpaceID_address">车位ID地址</param>
        /// <param name="park_completed_address">停车完成地址</param>
        /// <param name="park_completed_acknowledge_address">停车完成获取标志地址</param>
        /// <param name="fetching_startRobot_address">取车启动机械手地址</param>
        /// <param name="fetch_to_address">取至缓冲区地址</param>
        /// <param name="fetch_completed_address">取车完成地址</param>
        /// <param name="fetch_completed_acknowledge_address">取车完成获得标志地址</param>
        /// <param name="frontWheelbase">前轮距转存</param>
        /// <param name="rearWheelbase">后轮距转存</param>
        public RobotProcessUnit(int id, int parking_startRobot_address, int parking_laserCenterX_address, int parkingSpaceID_address, int park_completed_address, int park_completed_acknowledge_address, int fetching_startRobot_address, int fetch_to_address, int fetch_completed_address, int fetch_completed_acknowledge_address, int frontWheelbase, int rearWheelbase)
        {
            this.id = id;
            this.parking_startRobot_address = parking_startRobot_address;
            this.parking_laserCenterX_address = parking_laserCenterX_address;
            this.parkingSpaceID_address = parkingSpaceID_address;
            this.park_completed_address = park_completed_address;
            this.park_completed_acknowledge_address = park_completed_acknowledge_address;
            this.fetching_startRobot_address = fetching_startRobot_address;
            this.fetch_completed_address = fetch_completed_address;
            this.fetch_completed_acknowledge_address = fetch_completed_acknowledge_address;
            this.fetch_to_address = fetch_to_address;
            this.frontWheelbase = frontWheelbase;
            this.rearWheelbase = rearWheelbase;
        }
    }
}
