using Newtonsoft.Json.Linq;
using ProjectManagementSystem.Common.Config;
using ProjectManagementSystem.Common.Extenions;
using ProjectManagementSystem.Common.Logger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Common.Core
{
    public class PlcControl
    {
        private static PlcControl m_instance;

        private Dictionary<string, HslCommunication.Core.IReadWriteNet> m_plcDict = new Dictionary<string, HslCommunication.Core.IReadWriteNet>();
        public List<JObject> JDataList { get; set; } = new List<JObject>();

        private string[] columnNameArray = new string[]
        { 
            "PLC允许进入" ,
            "PLC放行" ,
            "PLC返修" ,
            "AGV请求进入",
            "AGV正在进入",
            "AGV到位",
            "AGV正在离开" 
        };

        private string[] signalMonitorColumnNameArray = new string[]
        {
            "PLC呼叫满盘",
            "PLC转运空盘"
        };

        public static PlcControl Instance
        {
            get
            {
                if (m_instance == null)
                {
                    m_instance = new PlcControl();
                    if (!m_instance.Initialize())
                    {
                        m_instance = null;
                    }
                }
                return m_instance;
            }
        }

        private PlcControl()
        {
        }

        private bool Initialize()
        {
            var dataList = ExcelConfig.Instance.RouteConfigList;

            for (int i = 0; i < dataList.Count; i++)
            {
                var data = dataList[i];
                string plcIpAddr = data.PlcIpAddr;

                if (!string.IsNullOrEmpty(plcIpAddr))
                {
                    bool selfPlcReadWrite = data.LocationCode.GetLocationMember("独立通讯(PLC)").ToValue<bool>();

                    if (selfPlcReadWrite)
                    {
                        //每个库位独立的PLC通讯对象
                        var plc = GetReadWriteNet(data);
                        data.PlcReadWrite = plc;
                    }
                    else
                    {
                        //库位相同IP共用PLC通讯对象
                        if (!m_plcDict.ContainsKey(plcIpAddr))
                        {
                            var plc = GetReadWriteNet(data);
                            m_plcDict.Add(plcIpAddr, plc);
                        }

                        if (m_plcDict.ContainsKey(plcIpAddr))
                        {
                            data.PlcReadWrite = m_plcDict[plcIpAddr];
                        }
                    }

                }
            }

            columnNameArray = Function.AppSetting.TryGetValue<string>("PlcSignalColumnName").ToValueArray<string>();
            var dataList2 = ExcelConfig.Instance.InfoPlcList;
            for (int i = 0; i < dataList2.Count; i++)
            {
                var data = dataList2[i];
                data.routeConfig = ExcelConfig.Instance.RouteConfigList.FirstOrDefault(d => d.LocationCode == data.库位号);
                data.JData = new JObject();

                string defaultAddr = data.routeConfig.PlcHeartbearAddr;

                data.PlcSignalAddrArray = new string[columnNameArray.Length];
                data.PlcSignalAddrLengthArray = new ushort[columnNameArray.Length];

                for (int infoIndex = 0; infoIndex < columnNameArray.Length; infoIndex++)
                {
                    string info = columnNameArray[infoIndex];
                    string plcRealAddr = data.routeConfig.LocationCode.GetLocationMember(info);

                    data.PlcSignalAddrArray[infoIndex] = string.IsNullOrEmpty(plcRealAddr) ? defaultAddr : plcRealAddr;
                    data.PlcSignalAddrLengthArray[infoIndex] = 1;
                }

                var jData = new JObject();
                jData["库位号"] = data.routeConfig.LocationCode;
                jData["IP地址"] = data.routeConfig.PlcIpAddr;

                JDataList.Add(jData);
            }

            Task.Factory.StartNew(UserThread);
            return true;
        }

        /// <summary>
        /// 获取创建的PLC通讯对象
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private HslCommunication.Core.IReadWriteNet GetReadWriteNet(RouteConfig data)
        {
            string plcIpAddr = data.PlcIpAddr;

            var plcProtocol = data.LocationCode.GetLocationMember("通讯协议(PLC)");
            switch (plcProtocol)
            {
                case "SiemensS7Net":
                    {
                        var plc = new HslCommunication.Profinet.Siemens.SiemensS7Net(HslCommunication.Profinet.Siemens.SiemensPLCS.S1500, plcIpAddr);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
                case "MelsecMcNet":
                    {
                        var plc = new HslCommunication.Profinet.Melsec.MelsecMcNet(plcIpAddr, 5000);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
                case "OmronFinsNet":
                    {
                        var plc = new HslCommunication.Profinet.Omron.OmronFinsNet(plcIpAddr, 5000);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
                case "AllenBradleyNet":
                    {
                        var plc = new HslCommunication.Profinet.AllenBradley.AllenBradleyNet(plcIpAddr);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
                case "ModbusRtuOverTcp":
                    {
                        var plc = new HslCommunication.ModBus.ModbusRtuOverTcp(plcIpAddr);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
                default:
                    {
                        var plc = new HslCommunication.Profinet.AllenBradley.AllenBradleyNet(plcIpAddr);
                        plc.ConnectTimeOut = 1000;
                        plc.ReceiveTimeOut = 1000;
                        plc.SetPersistentConnection();
                        return plc;
                    }
            }
        }

        private void Clear(RouteConfig routeConfig)
        {
            var plcBehaviorList = ExcelConfig.Instance.PlcBehaviorList
                .Where(d => !string.IsNullOrEmpty(d.Remark) && d.Remark.Contains("初始化"));

            if (plcBehaviorList == null
                || plcBehaviorList.Count() == 0)
            {
                //string strLog = $"{routeConfig.LocationCode} IP地址：{routeConfig.PlcIpAddr}，无需清零地址";
                //CLog.Instance.SystemLog.WriteInfo(strLog);
                return;
            }

            foreach (var item in plcBehaviorList)
            {
                foreach (string addrDesc in item.DbAddressWrite)
                {
                    string addr = routeConfig.LocationCode.GetPlcTaskAddr(addrDesc);
                    if (string.IsNullOrEmpty(addr)) continue;

                    int id = routeConfig.IOTransID;
                    if(id > 0)
                    {
                        var readResult2 = DeviceControl.Instance.GetOutputIO(id, addr);
                        if (readResult2.Item1
                            && readResult2.Item2)
                        {
                            DeviceControl.Instance.SetOutputIO(id, addr, false);
                            string strLog = $"{routeConfig.LocationCode}，IO对接盒ID{id}，清零：{addr} ({addrDesc})";
                            CLog.Instance.SystemLog.WriteInfo(strLog);
                            Thread.Sleep(50);
                        }
                    }

                    var plc = routeConfig.PlcReadWrite;
                    if (plc != null && routeConfig.PlcConnected)
                    {
                        var readResult = plc.ReadBool(addr);
                        if (!readResult.IsSuccess
                            || (readResult.IsSuccess && readResult.Content != false))
                        {
                            var result = plc.Write(addr, false);
                            string strLog = $"{routeConfig.LocationCode}，{routeConfig.PlcIpAddr}，清零：{addr} ({addrDesc})，写入{result.IsSuccess.ToChineseString()}";
                            CLog.Instance.SystemLog.WriteInfo(strLog);
                        }
                    }

                }
            }
        }

        private void PlcTaskProc()
        {
            var RouteConfigList = ExcelConfig.Instance.RouteConfigList;

            for (int i = 0; i < RouteConfigList.Count; i++)
            {
                var routeConfig = RouteConfigList[i];

                if (!string.IsNullOrEmpty(routeConfig.PlcHeartbearAddr)
                    && routeConfig.PlcReadWrite != null)
                {
                    //读取PLC心跳信号
                    var tempResult = routeConfig.PlcReadWrite.ReadBool(routeConfig.PlcHeartbearAddr);

                    if (routeConfig.PlcConnected != tempResult.IsSuccess)
                    {
                        CLog.Instance.SystemLog.WriteInfo($"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} PLC连接：{tempResult.IsSuccess.ToChineseString()}");
                    }
                    routeConfig.PlcConnected = tempResult.IsSuccess;

                    //回写PLC心跳信号
                    if (tempResult.IsSuccess
                        && !string.IsNullOrEmpty(routeConfig.PlcHeartbearAddr_AGV))
                    {
                        routeConfig.PlcReadWrite.Write(routeConfig.PlcHeartbearAddr_AGV, tempResult.Content);
                    }
                }

            }
        }

        private void PlcMonitorProc()
        {
            var RouteConfigList = ExcelConfig.Instance.RouteConfigList;

            for (int i = 0; i < RouteConfigList.Count; i++)
            {
                var routeConfig = RouteConfigList[i];
                if (routeConfig.PlcConnected)
                {
                    //监控PLC信号
                    for (int idx = 0; idx < signalMonitorColumnNameArray.Length; idx++)
                    {
                        string colName = signalMonitorColumnNameArray[idx];

                        var signalAddr = routeConfig.LocationCode.GetLocationMember(colName);
                        if (!string.IsNullOrEmpty(signalAddr))
                        {
                            var readResult = routeConfig.PlcReadWrite.ReadBool(signalAddr);
                            var signalValue = readResult.IsSuccess ? readResult.Content.ToString() : "读取失败";

                            if (signalValue != routeConfig.JData[colName]?.ToString())
                            {
                                CLog.Instance.SystemLog.WriteInfo($"{routeConfig.LocationCode} {routeConfig.PlcIpAddr} {colName}信号：{signalAddr}={signalValue}");
                            }
                            routeConfig.JData[colName] = signalValue;
                        }
                    }
                }
            }
        }

        private void PlcClearProc()
        {
            var RouteConfigList = ExcelConfig.Instance.RouteConfigList;
            for (int i = 0; i < RouteConfigList.Count; i++)
            {
                var routeConfig = RouteConfigList[i];
                if (routeConfig.PlcIsNeedClear)
                {
                    Clear(routeConfig);
                }
            }
        }

        public List<JObject> ReadPlcInfo()
        {
            //用于显示

            var infoPlcList = ExcelConfig.Instance.InfoPlcList;
            //var RouteConfigList = ExcelConfig.Instance.RouteConfigList;

            Parallel.ForEach(infoPlcList, delegate (InfoPlc info, ParallelLoopState loopState)
            {
                try
                {
                    if (info.routeConfig.PlcConnected)
                    {
                        DateTime start = DateTime.Now;

                        //单个读取信号
                        for (int i = 0; i < columnNameArray.Length; i++)
                        {
                            string colName = columnNameArray[i];
                            string addr = info.routeConfig.LocationCode.GetLocationMember(colName);

                            if (!string.IsNullOrEmpty(addr))
                            {
                                var result = info.routeConfig.PlcReadWrite.ReadBool(addr);
                                if (result.IsSuccess)
                                {
                                    info.JData[colName] = result.Content;
                                }
                                else
                                {
                                    info.JData[colName] = null;
                                }
                            }
                            else
                            {
                                info.JData[colName] = null;
                            }
                        }
                        info.LastUpdateTime = DateTime.Now;





                        //////离散地址批量读取，部分plc支持
                        ////var plc = info.routeConfig.PlcReadWrite as HslCommunication.Profinet.AllenBradley.AllenBradleyNet;
                        //var plc = info.routeConfig.PlcReadWrite as HslCommunication.Profinet.Siemens.SiemensS7Net;
                        //if (plc == null)
                        //{
                        //    CLog.Instance.SystemLog.WriteInfo("PlcReadWrite转换失败");
                        //    return;
                        //}

                        ////批量读取
                        //var result = plc.Read(info.PlcSignalAddrArray, info.PlcSignalAddrLengthArray);
                        //if (result.IsSuccess)
                        //{
                        //    for (int i = 0; i < columnNameArray.Length; i++)
                        //    {
                        //        string colName = columnNameArray[i];
                        //        string addr = info.routeConfig.LocationCode.GetLocationMember(colName);

                        //        if (string.IsNullOrEmpty(addr))
                        //        {
                        //            info.JData[colName] = null;
                        //        }
                        //        else
                        //        {
                        //            //info.JData[colName] = plc.ByteTransform.TransBool(result.Content, i);

                        //            int bitIndex = int.Parse(addr.Last().ToString());
                        //            info.JData[colName] = result.Content[i].GetBitVaule(bitIndex);
                        //        }
                        //    }

                        //    info.LastUpdateTime = DateTime.Now;
                        //}

                        info.JData["耗时"] = Math.Round((DateTime.Now - start).TotalMilliseconds);
                    }
                }
                catch
                {
                }
            });
            
            for (int index = 0; index < infoPlcList.Count; index++)
            {
                var info = infoPlcList[index];
                var jData = JDataList[index];

                for (int i = 0; i < columnNameArray.Length; i++)
                {
                    string colName = columnNameArray[i];
                    jData[colName] = info.JData[colName];
                }

                jData["耗时(ms)"] = info.JData["耗时"];
                jData["LastUpdateTime"] = info.LastUpdateTime;
                jData["有效"] = info.有效;
            }

            return JDataList.ToList();
        }



        private void UserThread()
        {
            CLog.Instance.SystemLog.WriteDebug($"PlcControl已启动");
            Thread.Sleep(2000);
            uint counter = 0;
            while (true)
            {
                try
                {
                    PlcTaskProc();
                    PlcMonitorProc();

                    if (counter % 10 == 0)
                    {
                        PlcClearProc();
                    }
                }
                catch (Exception ex)
                {
                    CLog.Instance.SystemLog.WriteException("PlcControl", ex);
                    Thread.Sleep(5000);
                }

                counter++;
                Thread.Sleep(1000);
            }
        }
    }
}
