using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using QMAPP.FJC.Entity.Basic;
using QMAPP.ServicesAgent;
using System.Configuration;
using OPCPLC;
using QMAPP.FJC.Entity;
using OpcHost.Concrete;
using System.Threading;
using OpcHost.Common;
using OPCAutomation;

namespace OpcHost.Init
{
    public static class ParaInit
    {
        //用来存储工控机发起连接服务器建立双通道连接的设备信息
        public static List<string> connectMachineList;

        //用来存储生产设备信息
        public static List<MachineInfo> machineList;


        //用来存储OPC配置参数与服务端句柄的对应关系
        //key:OPC配置参数,样式为  设备号+"-"+模具号+"-"+数据库字段
        //value:服务端句柄
        public static Dictionary<string, int> parameterValueDict = new Dictionary<string, int>();


        /// <summary>
        /// 用来存储客户端句柄与设备参数对应关系
        /// key:客户端句柄值
        /// value:OPC配置参数,样式为  设备号+"-"+模具号+"-"+数据库字段
        /// </summary>
        public static Dictionary<int, string> clientHandleDict = new Dictionary<int, string>();

        //OPC配置参数列表静态变量
        public static List<ParameterConfig> paraConfigList;

        //设备模具参数信息静态变量
        public static List<ParameterConfig> moldList;

        //OPC连接字典静态变量
        public static Dictionary<string, OPCPLCAutomation> opcplcConnectionList = new Dictionary<string, OPCPLCAutomation>();

        public static int clientHandleMaxValue = 0;

        private static Dictionary<string, string> colunmConDict = new Dictionary<string, string>();

        #region 初始化

        /// <summary>
        /// 初始化相关信息
        /// </summary>
        public static void InitParameter()
        {
            moldList = new List<ParameterConfig>();

            machineList = new List<MachineInfo>();
            //获取数据库中所有的生产设备信息
            ServiceAgent wcfAgent = GetServiceAgent();

            //opcServer服务器IP
            string opcServerIP = ConfigurationManager.AppSettings.Get("OPCServerIP");

            //opcServer服务名称
            string opcServerName = ConfigurationManager.AppSettings.Get("OPCServerName");

            try
            {
                #region 获取数据库中所有的生产设备信息

                machineList = wcfAgent.InvokeServiceFunction<List<MachineInfo>>(QMAPP.ServicesAgent.B9BasicService.MachineInfoBLL_GetAllList.ToString(), new MachineInfo());

                #endregion

                #region 获取所有的配置信息

                paraConfigList = wcfAgent.InvokeServiceFunction<List<ParameterConfig>>(QMAPP.ServicesAgent.B9BasicService.ParameterConfigBLL_GetList.ToString(), new MachineInfo());

                #endregion

                #region 获取设备模块信息

                //对加工参数配置信息按照设备及模具信息进行分组
                var objlist = from pc in paraConfigList
                              group pc by new { pc.MACHINECODDE, pc.MOLDNUMBER, pc.CONNECTIONSTRING } into g
                              select new ParameterConfig { MACHINECODDE = g.Key.MACHINECODDE, MOLDNUMBER = g.Key.MOLDNUMBER, CONNECTIONSTRING = g.Key.CONNECTIONSTRING };

                //添加到静态变量中
                foreach (var ob in objlist)
                {
                    moldList.Add(new ParameterConfig() { MACHINECODDE = ob.MACHINECODDE, MOLDNUMBER = ob.MOLDNUMBER });
                }

                //按照设备进行排序
                moldList = moldList.OrderBy(o => o.MACHINECODDE).ToList<ParameterConfig>();

                #endregion

                #region 加载模块下的加工参数配置信息

                //变量所有的模具信息
                foreach (var m in moldList)
                {
                    MachineInfo machineEntity = machineList.First(o => o.MACHINECODDE == m.MACHINECODDE);

                    OPCPLCAutomation gtxConnection = new OPCPLC.OPCPLCAutomation();
                    gtxConnection.GetListOPCServers(opcServerIP);
                    gtxConnection.ConnectRemoteServer(opcServerName, opcServerIP);


                    //创建组信息
                    gtxConnection.CreateGroup(m.MACHINECODDE + m.MOLDNUMBER);

                    //获取该模具对应的OPC配置信息
                    List<ParameterConfig> machineParaConList = paraConfigList
                        .Where(o => o.MACHINECODDE == m.MACHINECODDE && o.MOLDNUMBER == m.MOLDNUMBER).ToList<ParameterConfig>();

                    //遍历该模具下的OPC配置信息
                    foreach (var pc in machineParaConList)
                    {
                        //初始化字典名称:  设备名称+模块编号+字段名称
                        string dicKeyStr = machineEntity.MACHINECODDE + "-" + m.MOLDNUMBER + "-" + pc.COLUMNCODE;

                        //初始化字段信息值为0
                        parameterValueDict.Add(dicKeyStr, 0);

                        //获取opc中tagName
                        string itemName = pc.CONNECTIONSTRING;

                        //将字典中的与PLC内存地址向绑定
                        parameterValueDict[dicKeyStr] = gtxConnection.AddKepItem(itemName);
                    }

                    //添加PLC连接信息
                    opcplcConnectionList.Add(m.MACHINECODDE + m.MOLDNUMBER, gtxConnection);

                }

                #endregion

                #region 开始对所有的生产设备进行轮询监控

                //获取所有的生产设备
                List<MachineInfo> produceMachineList = machineList
                    .Where(o => o.OPERATETYPE == EnumGeter.OPERATETYPE.PRODUCE.GetHashCode().ToString())
                    .ToList<MachineInfo>();


                //初始化工厂类
                OperateFactory factory = new OperateFactory();

                //按照机器来创建监控线程
                foreach (MachineInfo pm in produceMachineList)
                {

                    //通过设备的生产类型获取操作类别
                    EquipOperate opclass = factory.GetConcreteOperateClass(pm);

                    //添加监控线程
                    Thread t = new Thread(opclass.GetMachineData);
                    t.Start();

                }


                #endregion

            }
            catch (Exception)
            {

                throw;
            }
        }

        public static void InitParameterMonitor()
        {
            moldList = new List<ParameterConfig>();

            machineList = new List<MachineInfo>();
            //获取数据库中所有的生产设备信息
            ServiceAgent wcfAgent = GetServiceAgent();

            try
            {
                #region 获取数据库中所有的生产设备信息

                machineList = wcfAgent.InvokeServiceFunction<List<MachineInfo>>(QMAPP.ServicesAgent.B9BasicService.MachineInfoBLL_GetAllList.ToString(), new MachineInfo());

                #endregion

                #region 获取OPC配置信息

                paraConfigList = wcfAgent.InvokeServiceFunction<List<ParameterConfig>>(QMAPP.ServicesAgent.B9BasicService.ParameterConfigBLL_GetList.ToString(), new MachineInfo());

                #endregion

                #region 获取设备模块信息

                //对加工参数配置信息按照设备及模具信息进行分组
                var objlist = from pc in paraConfigList
                              group pc by new { pc.MACHINECODDE, pc.MOLDNUMBER } into g
                              select new ParameterConfig { MACHINECODDE = g.Key.MACHINECODDE, MOLDNUMBER = g.Key.MOLDNUMBER };

                //添加到静态变量中
                foreach (var ob in objlist)
                {
                    moldList.Add(new ParameterConfig() { MACHINECODDE = ob.MACHINECODDE, MOLDNUMBER = ob.MOLDNUMBER });
                }

                //按照设备进行排序
                moldList = moldList.OrderBy(o => o.MACHINECODDE).OrderBy(o => o.MOLDNUMBER).ToList<ParameterConfig>();

                #endregion


                //opcServer服务器IP
                string opcServerIP = ConfigurationManager.AppSettings.Get("OPCServerIP");

                //opcServer服务名称
                string opcServerName = ConfigurationManager.AppSettings.Get("OPCServerName");

                #region 加载每个模具下的OPC配置参数信息

                //遍历该模具下的OPC配置信息
                foreach (var m in moldList)
                {
                    //获取设备信息
                    MachineInfo machineEntity = machineList.First(o => o.MACHINECODDE == m.MACHINECODDE);

                    

                    #region 添加设备模具下加工参数

                    List<ParameterConfig> paraAndOperateList = paraConfigList.Where(o => (o.MACHINECODDE == m.MACHINECODDE && m.MOLDNUMBER == m.MOLDNUMBER)
                        && (m.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.PARAMETER.GetHashCode().ToString())
                        ).ToList<ParameterConfig>();

                    if (paraAndOperateList.Count > 0)
                    {
                        //初始化该模具下的OPC连接信息
                        OPCPLCAutomation paraAndOpeCon = new OPCPLC.OPCPLCAutomation();
                        paraAndOpeCon.GetListOPCServers(opcServerIP);

                        paraAndOpeCon.ConnectRemoteServer(opcServerName, opcServerIP);

                        paraAndOpeCon.CreateGroup(m.MACHINECODDE + "-" + m.MOLDNUMBER + "-GetData");

                        foreach (var pc in paraAndOperateList)
                        {
                            //初始化字典名称:  设备名称+模块编号+字段名称
                            string dicKeyStr = pc.MACHINECODDE + "-" + pc.MOLDNUMBER + "-" + pc.COLUMNCODE;

                            //初始化OPC配置参数与服务端句柄字典信息
                            parameterValueDict.Add(dicKeyStr, 0);

                            //获取OPC中的tagName
                            string itemName = pc.CONNECTIONSTRING;

                            //累计客户端句柄值
                            clientHandleMaxValue++;

                            //添加到客户端句柄与设备参数字典表中
                            clientHandleDict.Add(clientHandleMaxValue, dicKeyStr);

                            //通过OPC设备连接信息和客户端句柄获取服务端句柄
                            //并且将服务句柄与OPC配置信息进行绑定
                            parameterValueDict[dicKeyStr] = paraAndOpeCon.AddKepItem(itemName, clientHandleMaxValue);

                            colunmConDict.Add(dicKeyStr, m.MACHINECODDE + "-" + m.MOLDNUMBER + "-GetData");

                        }

                        //添加PLC连接信息
                        opcplcConnectionList.Add(m.MACHINECODDE + m.MOLDNUMBER + "-GetData", paraAndOpeCon);
                    }

                    #endregion

                    #region 添加设备模具下加工参数


                    List<ParameterConfig> writeList = paraConfigList
                        .Where(o => o.MACHINECODDE == m.MACHINECODDE && o.MOLDNUMBER == m.MOLDNUMBER && o.COLUMNTYPE == "2")
                        .ToList<ParameterConfig>();


                    if (writeList.Count > 0)
                    {
                        OPCPLCAutomation gtxConnection = new OPCPLC.OPCPLCAutomation();

                        gtxConnection.GetListOPCServers(opcServerIP);
                        //连接Opc服务
                        gtxConnection.ConnectRemoteServer(opcServerName, opcServerIP);

                        gtxConnection.CreateGroup(m.MACHINECODDE + "-" + m.MOLDNUMBER + "-WriteData");

                        //变该模块下的所有参数信息
                        foreach (var pc in writeList)
                        {
                            //初始化字典名称:  设备名称+模块编号+字段名称
                            string dicKeyStr = machineEntity.MACHINECODDE + "-" + m.MOLDNUMBER + "-" + pc.COLUMNCODE;

                            //初始化字段信息值为0
                            parameterValueDict.Add(dicKeyStr, 0);

                            //获取opc中tagName
                            string itemName = pc.CONNECTIONSTRING;

                            clientHandleMaxValue++;

                            clientHandleDict.Add(clientHandleMaxValue, dicKeyStr);

                            //将字典中的与PLC内存地址向绑定
                            parameterValueDict[dicKeyStr] = gtxConnection.AddKepItem(itemName, clientHandleMaxValue);

                            colunmConDict.Add(dicKeyStr, m.MACHINECODDE + "-" + m.MOLDNUMBER + "-WriteData");
                        }

                        //添加PLC连接信息
                        opcplcConnectionList.Add(m.MACHINECODDE + "-" + m.MOLDNUMBER + "-WriteData", gtxConnection);
                    }



                    #endregion

                    #region 添加模具下的加工完成标记和设备读取条码OPC配置信息

                    List<ParameterConfig> monitorList = paraConfigList.Where(o => (o.MACHINECODDE == m.MACHINECODDE && m.MOLDNUMBER == m.MOLDNUMBER)
                        && (m.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.COMPLETEFLAG.GetHashCode().ToString() || m.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.EQUIPSCANCODE.GetHashCode().ToString())
                        ).ToList<ParameterConfig>();

                    //存在加工完成标记和设备读取条码配置信息
                    if (monitorList.Count > 0)
                    {
                        //初始化该模具下的OPC连接信息
                        OPCPLCAutomation monitorCon = new OPCPLC.OPCPLCAutomation();
                        monitorCon.GetListOPCServers(opcServerIP);

                        monitorCon.ConnectRemoteServer(opcServerName, opcServerIP);

                        //创建监控组
                        monitorCon.CreateGroup(m.MACHINECODDE + "-" + m.MOLDNUMBER + "-Monitor");

                        monitorCon.KepGroups.DefaultGroupIsActive = true;//激活组。  
                        monitorCon.KepGroups.DefaultGroupDeadband = 0;// 死区值,设为0时,服务器端该组内任何数据变化都通知组。  
                        monitorCon.KepGroups.DefaultGroupUpdateRate = 200;//默认组群的刷新频率为200ms  
                        monitorCon.KepGroup.UpdateRate = 100;//刷新频率为1秒。  
                        monitorCon.KepGroup.IsSubscribed = true;//使用订阅功能,即可以异步,默认false 

                        monitorCon.KepGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
                        //gtxConnection.KepGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete);

                        foreach (var pc in monitorList)
                        {
                            //初始化字典名称:  设备名称+模块编号+字段名称
                            string dicKeyStr = pc.MACHINECODDE + "-" + pc.MOLDNUMBER + "-" + pc.COLUMNCODE;

                            //初始化OPC配置参数与服务端句柄字典信息
                            parameterValueDict.Add(dicKeyStr, 0);

                            //获取OPC中的tagName
                            string itemName = pc.CONNECTIONSTRING;

                            //累计客户端句柄值
                            clientHandleMaxValue++;

                            //添加到客户端句柄与设备参数字典表中
                            clientHandleDict.Add(clientHandleMaxValue, dicKeyStr);


                            //通过OPC设备连接信息和客户端句柄获取服务端句柄
                            //并且将服务句柄与OPC配置信息进行绑定
                            parameterValueDict[dicKeyStr] = monitorCon.AddKepItem(itemName, clientHandleMaxValue);


                            colunmConDict.Add(dicKeyStr, m.MACHINECODDE + "-" + m.MOLDNUMBER + "-Monitor");
                        }

                        //添加到OPC连接字典中
                        opcplcConnectionList.Add(m.MACHINECODDE + "-" + m.MOLDNUMBER + "-Monitor", monitorCon);
                    }

                    #endregion

                }

                #endregion

            }
            catch (Exception)
            {

                throw;
            }

        }

        #endregion

        /// <summary>
        /// 初始化代理信息
        /// </summary>
        /// <returns></returns>
        public static ServiceAgent GetServiceAgent()
        {
            try
            {
                //创建代理
                ServiceAgent agent = new ServiceAgent();

                //设置凭据
                //agent.ClientCredential.CredentialID = ConfigurationManager.AppSettings["ServiceCredentialID"];

                return agent;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private static void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            try
            {
                //延迟半秒
                Thread.Sleep(500);

                for (int i = 1; i <= NumItems; i++)
                {
                    //获取写入的值
                    var itemValue = ItemValues.GetValue(i);

                    //获取客户端句柄
                    int chvalue = Convert.ToInt32(ClientHandles.GetValue(i));

                    //获取服务端字典key值
                    string dicKeyStr = clientHandleDict[chvalue];

                    //获取设备信息
                    string machCode = dicKeyStr.Substring(0, dicKeyStr.IndexOf('-'));

                    string moldAndColumn = dicKeyStr.Substring(dicKeyStr.IndexOf('-') + 1);

                    //获取模块信息
                    string moldNumber = moldAndColumn.Substring(0, moldAndColumn.IndexOf('-'));

                    //获取字段名称
                    string columnCode = dicKeyStr.Substring(dicKeyStr.LastIndexOf('-') + 1);

                    //获取该标记信息
                    ParameterConfig currentPC = paraConfigList.First(o => o.MACHINECODDE == machCode && o.MOLDNUMBER == moldNumber && o.COLUMNCODE == columnCode);

                    #region 完成标记

                    if (currentPC.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.COMPLETEFLAG.GetHashCode().ToString())
                    {
                        //如果完成标记表示更新了加 工参数
                        //获取所有的加工参数
                        if ((Boolean)itemValue == true)
                        {

                            #region 获取该模块下的加工参数

                            List<ParameterConfig> paraList = paraConfigList
                             .Where(o => o.MACHINECODDE == machCode && o.MOLDNUMBER == moldNumber && o.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.PARAMETER.GetHashCode().ToString())
                             .ToList<ParameterConfig>();

                            //获取所有的加工参数
                            foreach (var parameter in paraList)
                            {

                                string item = parameter.MACHINECODDE + "-" + parameter.MOLDNUMBER + "-" + parameter.COLUMNCODE;

                                string conListKey = colunmConDict[item];

                                OPCPLCAutomation con = opcplcConnectionList[conListKey];

                                var result = con.ReadtagValue(parameterValueDict[item]);

                                if (result != null)
                                {
                                    parameter.PARAVALUE = result;
                                }
                            }

                            #endregion

                            #region 处理已经读取完加工参数标记

                            //获取该设备模具下对应的设备操作就绪配置信息
                            ParameterConfig writeConfig = paraConfigList.First(o => o.MACHINECODDE == currentPC.MACHINECODDE
                                && o.MOLDNUMBER == currentPC.MOLDNUMBER && o.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.PARAMETERREADED.GetHashCode().ToString());

                            //获取对应的OPC操作对象
                            //获取对应的服务端句柄
                            //向服务端句柄写入
                            opcplcConnectionList[writeConfig.MACHINECODDE + "-" + writeConfig.MOLDNUMBER + "-WriteData"].WritetagValue(parameterValueDict[writeConfig.MACHINECODDE + "-" + writeConfig.MOLDNUMBER + "-" + writeConfig.COLUMNCODE], true);

                            #endregion

                            //将参数信息传递到web服务中


                        }
                    }

                    #endregion

                    #region 设备扫描枪扫描条码

                    if (currentPC.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.EQUIPSCANCODE.GetHashCode().ToString())
                    {
                        //在双攻通信的条件下,将扫描的条码信息传送到工控机上
                        PLCService plcService = new PLCService();
                        plcService.ReturnProductCodeToMachine(itemValue.ToString(), machCode);
                    }


                    #endregion

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("系统出现异常:" + ex.Message);
            }

        }

        /// <summary>
        /// 向设备发送可以操作的指令信息
        /// </summary>
        /// <param name="config"></param>
        public static void SendOperateReadyOrder(ParameterConfig config)
        {
            try
            {
                //获取该设备模具下对应的设备操作就绪配置信息
                ParameterConfig writeConfig = paraConfigList.First(o => o.MACHINECODDE == config.MACHINECODDE
                    && o.MOLDNUMBER == config.MOLDNUMBER && o.COLUMNTYPE == OpcHostEnumGeter.COLUMNTYPE.OPERATEFLAG.GetHashCode().ToString());

                //获取对应的OPC操作对象
                //获取对应的服务端句柄
                //向服务端句柄写入
                opcplcConnectionList[writeConfig.MACHINECODDE + "-" + writeConfig.MOLDNUMBER + "-WriteData"].WritetagValue(parameterValueDict[writeConfig.MACHINECODDE + "-" + writeConfig.MOLDNUMBER + "-" + writeConfig.COLUMNCODE], true);

            }
            catch (Exception)
            {
                
                throw;
            }
            
        }
    }
}