PLCSystemFaultAnalysis.cs 19.6 KB
using HHECS.BllModel;
using HHECS.DAQHandle.Dto;
using HHECS.DAQShared.Common.Enums;
using HHECS.DAQShared.Models;

namespace HHECS.DAQHandle.EquipmentHandle
{
    /// <summary>
    /// PLC系统报警
    /// </summary>
    /// <remarks>只有报警</remarks>
    internal class PLCSystemFaultAnalysis : AnalysisBase
    {
        public PLCSystemFaultAnalysis(AnalysisParameter parameter) : base(parameter) { }

        /// <summary>
        /// 无需更新状态
        /// </summary>
        /// <param name="equipment"></param>
        /// <param name="records"></param>
        /// <returns></returns>
        protected override BllResult UpdateEquipmentStatus(Equipment equipment, IEnumerable<EquipmentDataRecord> records)
        {
            return BllResultFactory.Success();
        }

        /// <summary>
        /// 无需更新手动状态
        /// </summary>
        /// <param name="equipment"></param>
        /// <param name="records"></param>
        /// <returns></returns>
        protected override BllResult UpdateEquipmentManualRecord(Equipment equipment, IEnumerable<EquipmentDataRecord> records)
        {
            return BllResultFactory.Success();
        }

        protected override BllResult UpdateEquipmentAlarm(Equipment equipment, IEnumerable<EquipmentDataRecord> records)
        {
            try
            {
                using var equipmentAlarmRecordRepository = freeSql.GetRepository<EquipmentAlarmRecord>();
                using var equipmentAlarmRecordDetailRepository = freeSql.GetRepository<EquipmentAlarmRecordDetail>();
                var alarmRecordCacheTempsResult = AssemblyAlarmData(equipment, records);
                if (!alarmRecordCacheTempsResult.Success)
                {
                    return BllResultFactory.Error(alarmRecordCacheTempsResult.Msg);
                }

                //无报警数据
                if (alarmRecordCacheTempsResult.Data.Count == 0)
                {
                    //结束当前设备的超时主报警
                    var needEndAlaremRecords = equipmentAlarmRecordRepository.Where(x => x.EquipmentCode == equipment.Code && !x.IsEnd).OrderBy(x => x.CreateTime).Limit(100)
                        .ToList(x => new EquipmentAlarmRecord
                        {
                            Id = x.Id,
                        });
                    foreach (var item in needEndAlaremRecords)
                    {
                        equipmentAlarmRecordRepository.Attach(item);
                        item.IsEnd = true;
                    }
                    equipmentAlarmRecordRepository.Update(needEndAlaremRecords);

                    //结束当前设备的超时报警明细
                    var mainAlarmIds = needEndAlaremRecords.Select(x => x.Id).ToList();
                    var needEndAlaremDetailRecords = equipmentAlarmRecordDetailRepository.Where(x => mainAlarmIds.Contains(x.EquipmentAlarmRecordId) && x.EquipmentCode == equipment.Code && !x.IsEnd).OrderBy(x => x.CreateTime).ToList(x => new EquipmentAlarmRecordDetail
                    {
                        Id = x.Id,
                    });
                    foreach (var item in needEndAlaremDetailRecords)
                    {
                        equipmentAlarmRecordDetailRepository.Attach(item);
                        item.IsEnd = true;
                    }
                    equipmentAlarmRecordDetailRepository.Update(needEndAlaremDetailRecords);

                    //无报警则跳过
                    return BllResultFactory.Success();
                }

                var alarmRecordCacheTemps = alarmRecordCacheTempsResult.Data;

                var oldAlarmRecord = equipmentAlarmRecordRepository.Where(x => x.EquipmentCode == equipment.Code && x.EquipmentPropCode == EquipmentTotalAlarmCode && !x.IsEnd).OrderByDescending(x => x.CreateTime).First();

                var lastCacheTemp = alarmRecordCacheTemps.OrderByDescending(x => x.CreateTime).First();
                var addTemps = new List<EquipmentAlarmRecord>();
                //数据库无报警记录
                if (oldAlarmRecord == null)
                {
                    foreach (var item in alarmRecordCacheTemps)
                    {
                        //不是最后一条记录的数据需要结束
                        if (!item.Equals(lastCacheTemp))
                        {
                            item.IsEnd = true;
                        }
                        addTemps.Add(item);
                    }
                    equipmentAlarmRecordRepository.Insert(addTemps);
                    var allAlarmDetail = addTemps.SelectMany(x => x.EquipmentAlarmRecordDetails).ToList();
                    equipmentAlarmRecordDetailRepository.Insert(allAlarmDetail);
                    return BllResultFactory.Success();
                }

                //数据库有报警记录,则需要校验
                foreach (var item in alarmRecordCacheTemps)
                {
                    //对于之前的数据,这里暂时不做处理
                    if (oldAlarmRecord.UpdateTime >= item.UpdateTime)
                    {
                        continue;
                    }

                    //日期为同一天,在设定时间以内,则更新
                    if (oldAlarmRecord.UpdateTime.Date == item.CreateTime.Date && (item.CreateTime - oldAlarmRecord.UpdateTime).TotalSeconds <= AppConfig.DataTimeOut)
                    {
                        equipmentAlarmRecordRepository.Attach(oldAlarmRecord);
                        //不是最后一条记录,说明已经成为历史报警,需要结束标记
                        if (!item.Equals(lastCacheTemp))
                        {
                            oldAlarmRecord.IsEnd = true;
                        }

                        //无具体报警信息,则更新报警文本,这里的文本需要与数据组装方法“AssemblyAlarmData”里面的文本内容一致
                        if (oldAlarmRecord.AlarmMessage.Contains("无具体报警信息") && oldAlarmRecord.AlarmMessage != item.AlarmMessage)
                        {
                            oldAlarmRecord.AlarmMessage = item.AlarmMessage;
                        }

                        oldAlarmRecord.ErrorDuration = GetErrorDuration(oldAlarmRecord.CreateTime, item.UpdateTime);
                        oldAlarmRecord.UpdateTime = item.UpdateTime;
                        oldAlarmRecord.HandleTime = DateTime.Now;
                        equipmentAlarmRecordRepository.Update(oldAlarmRecord);

                        //更新报警明细
                        var allAlarmDetail = equipmentAlarmRecordDetailRepository.Where(x => !x.IsEnd &&
                        x.EquipmentAlarmRecordId == oldAlarmRecord.Id).ToList();

                        var addAlarmDetailTemps = new List<EquipmentAlarmRecordDetail>();

                        //报警明细
                        var equipmentAlarmRecordDetails = item.EquipmentAlarmRecordDetails.Select(x => new EquipmentAlarmRecordDetail
                        {
                            EquipmentAlarmRecordId = oldAlarmRecord.Id,
                            EquipmentCode = x.EquipmentCode,
                            EquipmentName = x.EquipmentName,
                            EquipmentPropCode = x.EquipmentPropCode,
                            EquipmentPropName = x.EquipmentPropName,
                            EquipmentTypeCode = x.EquipmentTypeCode,
                            ErrorDuration = x.ErrorDuration,
                            AlarmMessage = x.AlarmMessage,
                            ProjectCode = x.ProjectCode,
                            FactoryCode = x.FactoryCode,
                            IsEnd = oldAlarmRecord.IsEnd,
                            CreateTime = x.CreateTime,
                            UpdateTime = x.UpdateTime,
                        }).ToList();

                        foreach (var alarmDetailGroup in equipmentAlarmRecordDetails.GroupBy(x => x.EquipmentPropCode))
                        {
                            //缓存里面的第一条记录
                            var firstCacheTemp = alarmDetailGroup.OrderBy(x => x.CreateTime).First();
                            //数据库里面的最后一条记录
                            var lastDBTemp = allAlarmDetail.Where(x => x.EquipmentPropCode == alarmDetailGroup.Key).OrderByDescending(x => x.CreateTime).FirstOrDefault();
                            //存在记录,且在设定时间之内,则需要更新
                            if (lastDBTemp != null && (firstCacheTemp.CreateTime - lastDBTemp.UpdateTime).TotalSeconds <= AppConfig.DataTimeOut)
                            {
                                equipmentAlarmRecordDetailRepository.Attach(lastDBTemp);
                                lastDBTemp.ErrorDuration = GetErrorDuration(lastDBTemp.CreateTime, firstCacheTemp.UpdateTime);
                                lastDBTemp.UpdateTime = firstCacheTemp.UpdateTime;
                                //状态有编号,则更新
                                if (lastDBTemp.IsEnd != oldAlarmRecord.IsEnd)
                                {
                                    lastDBTemp.IsEnd = oldAlarmRecord.IsEnd;
                                }
                                equipmentAlarmRecordDetailRepository.Update(lastDBTemp);
                                addAlarmDetailTemps.AddRange(alarmDetailGroup.Where(x => !x.Equals(firstCacheTemp)).ToList());
                                continue;
                            }
                            addAlarmDetailTemps.AddRange(alarmDetailGroup.ToList());
                        }

                        equipmentAlarmRecordDetailRepository.Insert(addAlarmDetailTemps);
                        continue;
                    }

                    //其他情况需要新增记录
                    //不是最后一条记录
                    if (!item.Equals(lastCacheTemp))
                    {
                        item.IsEnd = true;
                    }
                    addTemps.Add(item);
                }
                equipmentAlarmRecordRepository.Insert(addTemps);
                equipmentAlarmRecordDetailRepository.Insert(addTemps.SelectMany(x => x.EquipmentAlarmRecordDetails).ToList());
                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error(ex.Message);
            }
        }

        protected override BllResult<EquipmentStatus> GetEquipmentStatus(Equipment equipment, EquipmentDataRecord record)
        {
            try
            {
                var tagResult = JsonConvertToTagList(record.Tags);
                if (!tagResult.Success)
                {
                    //数据格式不正确,忽略报警
                    return BllResultFactory.Success(EquipmentStatus.Free);
                }

                var tags = tagResult.Data;
                var equipmentProps = equipment.EquipmentProps.Where(x => x.PropType == EquipmentPropType.PLCMonitorAddress && !string.IsNullOrWhiteSpace(x.MonitorCompareValue)).ToList();
                foreach (var item in equipmentProps)
                {
                    var tag = tags.Where(x => x.Tag == item.Code).FirstOrDefault();
                    if (tag == null)
                    {
                        continue;
                    }
                    if (item.MonitorCompareValue == tag.Value)
                    {
                        //故障
                        return BllResultFactory.Success(EquipmentStatus.Failure);
                    }
                }
                return BllResultFactory.Success(EquipmentStatus.Free);
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error<EquipmentStatus>(ex.Message);
            }
        }

        protected override BllResult<List<EquipmentAlarmRecord>> AssemblyAlarmData(Equipment equipment, IEnumerable<EquipmentDataRecord> records)
        {
            try
            {
                //设备总报警编号
                var totalAlarmProp = new EquipmentProp
                {
                    Code = "PLCSystemFault",
                    Name = "PLC系统报警",
                };

                //当前设备监控地址属性模板
                var monitorPropTemps = equipment.EquipmentProps.Where(x => x.PropType == EquipmentPropType.PLCMonitorAddress
                && !string.IsNullOrEmpty(x.MonitorCompareValue))
                    .DistinctBy(x => x.Code)
                    .OrderBy(x => x.Id).ToList();

                //数据组装
                var temps = new List<EquipmentAlarmRecord>();
                foreach (var item in records.OrderBy(x => x.Timestamp))
                {
                    var currentRecordStatusResult = GetEquipmentStatus(equipment, item);
                    if (!currentRecordStatusResult.Success)
                    {
                        continue;
                    }

                    if (currentRecordStatusResult.Data != EquipmentStatus.Failure)
                    {
                        //状态有变化,则结束最后一条报警记录
                        var lastTemp = temps.Where(x => !x.IsEnd).OrderByDescending(x => x.CreateTime).FirstOrDefault();
                        if (lastTemp != null)
                        {
                            lastTemp.IsEnd = true;
                            lastTemp.EquipmentAlarmRecordDetails.ForEach(x => x.IsEnd = true);
                        }
                        continue;
                    }

                    var time = TimestampConvertToLocalDateTime(item.Timestamp);
                    var tagResult = JsonConvertToTagList(item.Tags);
                    //数据异常,则直接弃用这条记录
                    if (!tagResult.Success)
                    {
                        continue;
                    }
                    var tagItems = tagResult.Data;

                    var (mainAlarm, detailAlarms) = GenerateAlarmMessage(equipment, totalAlarmProp, monitorPropTemps, tagItems);

                    var cacheTemp = temps.Where(x => x.EquipmentPropCode == totalAlarmProp.Code && !x.IsEnd)
                        .OrderByDescending(x => x.CreateTime).FirstOrDefault();

                    //间隔在设定时间内,认为是同一个主报警
                    if (cacheTemp != null && cacheTemp.UpdateTime.Date == time.Date &&
                       (time - cacheTemp.UpdateTime).TotalSeconds <= AppConfig.DataTimeOut)
                    {
                        cacheTemp.UpdateTime = time;
                        cacheTemp.ErrorDuration = GetErrorDuration(cacheTemp.CreateTime, time);
                        //结束其他报警明细
                        var alarmCodes = detailAlarms.Select(x => x.Key).ToList();
                        foreach (var oldAlarm in cacheTemp.EquipmentAlarmRecordDetails)
                        {
                            if (oldAlarm.IsEnd)
                            {
                                continue;
                            }

                            if (!alarmCodes.Contains(oldAlarm.EquipmentPropCode))
                            {
                                //本次无报警的记录,需要结束
                                oldAlarm.IsEnd = true;
                            }
                        }

                        //更新报警明细
                        foreach (var newAlarm in detailAlarms)
                        {
                            var lastItem = cacheTemp.EquipmentAlarmRecordDetails.Where(x => x.EquipmentPropCode == newAlarm.Key && !x.IsEnd).FirstOrDefault();
                            if (lastItem == null)
                            {
                                var newItem = new EquipmentAlarmRecordDetail
                                {
                                    EquipmentAlarmRecordId = cacheTemp.Id,
                                    EquipmentCode = equipment.Code,
                                    EquipmentName = equipment.Name,
                                    EquipmentPropCode = newAlarm.Key,
                                    EquipmentPropName = monitorPropTemps.Where(p => p.Code == newAlarm.Key).Select(p => p.Remark).FirstOrDefault(),
                                    EquipmentTypeCode = CurrentEquipmentType.Code,
                                    ProjectCode = equipment.ProjectCode,
                                    FactoryCode = equipment.FactoryCode,
                                    AlarmMessage = newAlarm.Value,
                                    IsEnd = false,
                                    ErrorDuration = 1,
                                    CreateTime = time,
                                    UpdateTime = time,
                                };
                                cacheTemp.EquipmentAlarmRecordDetails.Add(newItem);
                                continue;
                            }

                            //记录不为空,更新时间
                            lastItem.UpdateTime = time;
                            lastItem.ErrorDuration = GetErrorDuration(lastItem.CreateTime, time);
                        }
                        continue;
                    }

                    //获取设备故障坐标
                    var alarmMessage = string.Join(";", detailAlarms.Select(x => x.Value));
                    if (string.IsNullOrWhiteSpace(alarmMessage))
                    {
                        alarmMessage = $"{mainAlarm},无具体报警信息";
                    }
                    var mainAlarmRecord = new EquipmentAlarmRecord
                    {
                        Id = Guid.NewGuid(),
                        EquipmentCode = equipment.Code,
                        EquipmentName = equipment.Name,
                        EquipmentPropCode = totalAlarmProp.Code,
                        EquipmentPropName = totalAlarmProp.Name,
                        EquipmentTypeCode = CurrentEquipmentType.Code,
                        ErrorDuration = 1,
                        IsEnd = false,
                        AlarmMessage = alarmMessage,
                        FactoryCode = equipment.FactoryCode,
                        ProjectCode = equipment.ProjectCode,
                        HandleTime = DateTime.Now,
                        CreateTime = time,
                        UpdateTime = time,
                    };

                    mainAlarmRecord.EquipmentAlarmRecordDetails = detailAlarms.Select(x => new EquipmentAlarmRecordDetail
                    {
                        EquipmentAlarmRecordId = mainAlarmRecord.Id,
                        EquipmentCode = equipment.Code,
                        EquipmentName = equipment.Name,
                        EquipmentPropCode = x.Key,
                        EquipmentPropName = monitorPropTemps.Where(p => p.Code == x.Key).Select(p => p.Remark).FirstOrDefault(),
                        EquipmentTypeCode = CurrentEquipmentType.Code,
                        ProjectCode = equipment.ProjectCode,
                        FactoryCode = equipment.FactoryCode,
                        AlarmMessage = x.Value,
                        IsEnd = false,
                        ErrorDuration = 1,
                        CreateTime = time,
                        UpdateTime = time,
                    }).ToList();

                    //其他情况,新增
                    temps.Add(mainAlarmRecord);
                }
                return BllResultFactory.Success(temps);
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error<List<EquipmentAlarmRecord>>(ex.Message);
            }
        }
    }
}