EquipmentExecutor.cs 11.1 KB
using HHECS.Application.Service;
using HHECS.Communication;
using HHECS.BllModel;
using HHECS.Infrastructure.Enums;
using HHECS.Infrastructure.Notice;
using HHECS.Model.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HHECS.Application.Error;
using HHECS.Application.Enums;
using HHECS.Executor.EquipmentHandler;
using HHECS.Model.Enums;
using NPOI.HSSF.Record;
using static FreeSql.Internal.GlobalFilter;

namespace HHECS.Executor
{
    /// <summary>
    /// 执行类,此类请使用单例模式并调用inti方法
    /// </summary>
    public class EquipmentExecutor
    {
        #region 属性

        /// <summary>
        /// 设备执行时,触发日志记录
        /// 交由前台程序自己实现,比如WPF客户端记录到日志控件
        /// </summary>
        public event Action<object, EquipmentExecuteLogEventArgs> EquipmentExecuteLog;

        /// <summary>
        /// 为true表示停止,为false表示执行
        /// </summary>
        private bool stop = true;

        /// <summary>
        /// 是否可执行  初始化成功
        /// </summary>
        public bool CanExcute { get; private set; } = false;


        /// <summary>
        /// 设备服务类
        /// </summary>
        public ExecuteService ExcuteService { get; set; } = new ExecuteService();

        /// <summary>
        /// PLC访问类
        /// </summary>
        public PLCCore PLC { get; set; }

        /// <summary>
        /// 所有设备
        /// </summary>
        public List<Equipment> Equipments { get; set; }

        /// <summary>
        /// 是否在执行中
        /// </summary>
        public bool IsExecuting
        {
            get { return CanExcute && !stop; }
        }

        /// <summary>
        /// 默认延迟100
        /// </summary>
        public int Delay { get; set; } = 100;

        /// <summary>
        /// 当前执行的用户
        /// 可传递一个WCS用户
        /// </summary>
        public User User { get; set; }

        public string DestinationArea { get; set; }

        #endregion



        /// <summary>
        /// 执行线程
        /// </summary>
        private void Excuting()
        {
            //todo:自定义设备分组,这里以IP分组为例
            var groups = Equipments.GroupBy(t => new { t.WarehouseCode, t.IP }).ToList();

            foreach (var item in groups)
            {
                Task.Run(async () =>
                    {
                        while (true)
                        {
                            await Task.Delay(Delay);
                            if (CanExcute)
                            {
                                if (!stop)
                                {
                                    var result = Excute(item.ToList(), item.Key.IP, item.Key.WarehouseCode);
                                    if (!result.Success)
                                    {
                                        NoticeBus.Notice($"{result.Msg}", Level.Error);
                                    }
                                }
                            }
                        }
                    });
            }

            //设备报警,按收集报警的时间点计算
            Task.Factory.StartNew(async () =>
            {
                //默认,只有在设备启动开始后,才会
                while (true)
                {
                    try
                    {
                        await Task.Delay(500);
                        if (!stop && CanExcute)
                        {
                            EquipmentCommon.EquipmentAlarmGenerate(Equipments);
                            EquipmentCommon.EquipmentAlarmAutoEndAsync(Equipments);

                            EquipmentCommon.EquipmentStatusRecordGenerate(Equipments);
                            EquipmentCommon.EquipmentStatusAutoEnd(Equipments);
                        }
                    }
                    catch (Exception ex)
                    {
                        NoticeBus.Notice($"更新状态和报警信息出现异常、{ex.Message}", Level.Exception);
                    }
                }
            });
        }

        /// <summary>
        /// todo:每个分组设备的具体执行,单次运行
        /// </summary>
        /// <param name="equipments"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private BllResult Excute(List<Equipment> equipments, string ip, string warehouseCode)
        {
            try
            {
                #region 检查是否连接正常

                //注意此处使用过滤后的tempEquipments
                List<Equipment> tempEquipments = equipments;
                if (!PLC.GetConnectStatus(ip).Success)
                {
                    PLC.DisConnect(ip);
                    var result2 = PLC.Connect(ip);
                    if (!result2.Success)
                    {
                        return BllResultFactory.Error($"重新连接打开IP为{ip}的PLC连接失败:{result2.Msg};请检查网络是否断开", ErrorCodeConst.PLC_E0001.ToString());
                    }
                }
                #endregion
                if (equipments.Count == 0)
                {
                    return BllResultFactory.Error($"未找到设备{ip}");
                }
                #region PLC心跳是否正常
                List<EquipmentProp> heartbeatProps = new List<EquipmentProp>();
                if (equipments.Any(t => t.EquipmentType.Code == EquipmentTypeConst.DoubleForkSRM.ToString() || t.EquipmentType.Code == EquipmentTypeConst.SingleForkSRM.ToString() || t.EquipmentType.Code == EquipmentTypeConst.SingleForkSSRM.ToString()))
                {
                    heartbeatProps = GetSRMHeartbeat(equipments.FirstOrDefault(t => t.IP == ip));
                }
                else
                {
                    heartbeatProps = GetStationHeartbeat(equipments.FirstOrDefault(t => t.IP == ip));
                }

                var readProp = heartbeatProps.Find(t => t.EquipmentTypePropTemplateCode == PLCHeartProps.PLCWrite.ToString());
                var readResult = PLC.Reads(readProp);
                if (readResult.Success && readProp.Value == "True")
                {
                    //回复一个
                    //var prop = heartbeatProps.Find(t => t.EquipmentTypePropTemplateCode == PLCHeartProps.WCSWrite.ToString());
                    //prop.Value = "True";
                    //PLC.Writes(prop);
                }
                else
                {
                    //表示心跳断开,排除其IP对应的设备
                    tempEquipments = tempEquipments.Where(t => t.IP != ip).ToList();
                }
                //无可用设备时,结束处理
                if (tempEquipments.Count == 0)
                {
                    return BllResultFactory.Error($"IP为{ip}的PLC心跳断开,请检查网络情况", ErrorCodeConst.PLC_E0001.ToString());
                }


                #endregion

                //读取所有地址值
                var result = PLC.Reads(tempEquipments.SelectMany(t => t.EquipmentProps).ToArray());
                if (!result.Success)
                {
                    return BllResultFactory.Error($"读取IP为{ip}的PLC地址错误:{result.Msg},如果此问题一直存在,请检查数据地址或网络配置", ErrorCodeConst.PLC_E0001.ToString());
                }

                //更新到数据库中(按需)
                Task.Run(() =>
                {
                    ExcuteService.EquipmentPropUpdate(tempEquipments.SelectMany(t => t.EquipmentProps).ToList());
                });

                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Create(BllResultCode.Exception, ex, $"程序处理出现异常:{ex.Message}", ErrorCodeConst.Exe_E0001.ToString());
            }
        }



        public List<EquipmentProp> GetSRMHeartbeat(Equipment equipment)
        {
            return new List<EquipmentProp>() {
                           new EquipmentProp(){
                               Equipment=equipment,
                           EquipmentTypePropTemplateCode=PLCHeartProps.PLCWrite.ToString(),
                           Address="DB100X0.0",
                           },
                            new EquipmentProp(){
                               Equipment=equipment,
                           EquipmentTypePropTemplateCode=PLCHeartProps.WCSWrite.ToString(),
                           Address="DB100X0.1",
                     }
                };
        }

        public List<EquipmentProp> GetStationHeartbeat(Equipment equipment)
        {
            return new List<EquipmentProp>() {
                           new EquipmentProp(){
                               Equipment=equipment,
                           EquipmentTypePropTemplateCode=PLCHeartProps.PLCWrite.ToString(),
                           Address="DB10000X0.0",
                           },
                            new EquipmentProp(){
                               Equipment=equipment,
                           EquipmentTypePropTemplateCode=PLCHeartProps.WCSWrite.ToString(),
                           Address="DB10000X0.1",
                     }
                };
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <returns></returns>
        public BllResult Init()
        {

            var result2 = ExcuteService.InitEquipment(DestinationArea);
            if (!result2.Success)
            {
                return BllResultFactory.Error($"初始化设备出错:{result2.Msg}");
            }
            Equipments = result2.Data;
            var ips = Equipments.Select(t => t.IP).ToList();
            var result = ExcuteService.InitPLC(ips);
            if (!result.Success)
            {
                return BllResultFactory.Error($"初始化PLC失败:{result.Msg}");
            }
            PLC = result.Data;

            CanExcute = true;
            Excuting();
            return BllResultFactory.Success();
        }


        /// <summary>
        /// 开启执行
        /// </summary>
        public BllResult StartExcute()
        {
            if (!CanExcute)
            {
                return BllResultFactory.Error("未初始化成功,启动失败");
            }
            stop = false;
            return BllResultFactory.Success();
        }

        /// <summary>
        /// 关闭执行
        /// </summary>
        public void StopExcute()
        {
            stop = true;
        }
        /// <summary>
        /// 记录设备执行,继续触发事件,交由具体实现类
        /// todo:考虑将此日志录入到数据库中方便查找
        /// </summary>
        /// <param name="arg1"></param>
        /// <param name="arg2"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Exc_EquipmentExecuteLog(object arg1, EquipmentExecuteLogEventArgs arg2)
        {
            EquipmentExecuteLog?.Invoke(arg1, arg2);
        }
    }
}