EquipmentController.cs 16.3 KB
using HHECS.BllModel;
using HHECS.DAQServer.Dto.Equipment;
using HHECS.DAQServer.Services;
using HHECS.DAQShared.Dto;
using HHECS.DAQShared.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using System.Text;
using System.Text.Json;

namespace HHECS.DAQServer.Controllers
{
    /// <summary>
    /// 设备数据
    /// </summary>
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class EquipmentController : ControllerBase
    {
        private readonly IFreeSql _freeSql;
        private readonly DataCacheService _dataCacheService;
        private readonly CommonService _commonService;
        private readonly IDistributedCache _cache;

        /// <summary>
        ///小于此时间,认为是无效数据,ORM框架有限制,应与实体类配置保持一致
        /// </summary>
        private readonly DateTime minStartTime;

        public EquipmentController(IFreeSql freeSql, DataCacheService dataCacheService, CommonService commonService, IDistributedCache cache)
        {
            _freeSql = freeSql;
            _dataCacheService = dataCacheService;
            _commonService = commonService;
            _cache = cache;
            minStartTime = DateTime.Parse("2024-6-11");
        }

        /// <summary>
        /// 推送设备实时数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<BllResult> SendEquipmentData(IEnumerable<EquipmentDataDto> data)
        {
            try
            {
                if (!data.Any())
                {
                    return BllResultFactory.Error($"数据不能为空!");
                }

                //缓存配置
                var options = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10));

                //缓存设备实时数据
                foreach (var record in data.GroupBy(x => x.EquipmentSN))
                {
                    //获取最新时间戳的数据
                    var lastItem = record.OrderByDescending(x => x.Timestamp).First();

                    //获取当前缓存数据
                    var cacheDataBytes = await _cache.GetAsync(record.Key);
                    if (cacheDataBytes != null)
                    {
                        //设备状态数据队列
                        _dataCacheService.EquipmentStatusDictionary.AddOrUpdate(record.Key, lastItem, (key, oldValue) =>
                        {
                            if (lastItem.Timestamp > oldValue.Timestamp)
                            {
                                return lastItem;
                            }
                            return oldValue;
                        });

                        //数据缓存
                        var cacheData = JsonSerializer.Deserialize<EquipmentDataDto>(Encoding.Default.GetString(cacheDataBytes));
                        if (cacheData.Timestamp >= lastItem.Timestamp)
                        {
                            continue;
                        }
                    }

                    var encodedCurrentData = JsonSerializer.SerializeToUtf8Bytes(lastItem);
                    await _cache.SetAsync(record.Key, encodedCurrentData, options);
                }
                var maxCacheCount = _commonService.GetMaxCacheCount();

                //缓存队列超过设定值,暂时不记录到队列,降低数据丢失的风险
                var currentQueueCount = _dataCacheService.EquipmentDataRecordQueue.Count;
                if (currentQueueCount >= maxCacheCount)
                {
                    return BllResultFactory.Error($"数据队列缓存超过设定值({maxCacheCount}),待当前队列数据({currentQueueCount})处理完成后才能继续");
                }

                var records = data.Where(x =>
                {
                    var time = _commonService.TimestampConvertToLocalDateTime(x.Timestamp);
                    if (time == default)
                    {
                        return false;
                    }

                    if (time < minStartTime)
                    {
                        return false;
                    }
                    return true;
                }).OrderBy(x => x.Timestamp).ToList();

                //加入缓存队列,存入数据库
                foreach (var item in records)
                {
                    _dataCacheService.EquipmentDataRecordQueue.Enqueue(item);
                }

                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error(ex.Message);
            }
        }

        /// <summary>
        /// 推送某段时间的设备数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<BllResult> SendEquipmentDataV2(IEnumerable<EquipmentDataV2Dto> data)
        {
            try
            {
                var maxCacheCount = _commonService.GetMaxCacheCount();
                var currentQueueCount = _dataCacheService.EquipmentDataRecordQueue.Count;
                if (currentQueueCount >= maxCacheCount)
                {
                    return BllResultFactory.Error($"数据队列缓存超过设定值[{maxCacheCount}],待当前队列数据[{currentQueueCount}]处理完成后才能继续");
                }

                foreach (var item in data.OrderBy(x => x.EquipmentSN).ThenBy(x => x.TimestampStart))
                {
                    var startTime = _commonService.TimestampConvertToLocalDateTime(item.TimestampStart);
                    var endTime = _commonService.TimestampConvertToLocalDateTime(item.TimestampEnd);
                    //无效时间
                    if (startTime <= minStartTime || startTime == default || endTime == default)
                    {
                        continue;
                    }

                    var nextStartTime = startTime;
                    //3秒间隔,分割数据
                    const int seccond = 3;
                    EquipmentDataDto lastRecord;
                    do
                    {
                        var currentTime = nextStartTime < endTime ? nextStartTime : endTime;
                        DateTimeOffset localTime = DateTime.SpecifyKind(currentTime, DateTimeKind.Local);
                        var timestamp = localTime.ToUnixTimeMilliseconds();
                        var record = new EquipmentDataDto
                        {
                            Plmeid = item.Plmeid,
                            EquipmentSN = item.EquipmentSN,
                            Reported = item.Reported,
                            Version = item.Version,
                            Timestamp = timestamp,
                        };
                        _dataCacheService.EquipmentDataRecordQueue.Enqueue(record);
                        nextStartTime = nextStartTime.AddSeconds(seccond);
                        lastRecord = new EquipmentDataDto
                        {
                            Plmeid = item.Plmeid,
                            EquipmentSN = item.EquipmentSN,
                            Reported = item.Reported,
                            Timestamp = timestamp,
                            Version = item.Version,
                        };
                    } while (nextStartTime <= endTime.AddSeconds(seccond));

                    if (lastRecord != null)
                    {
                        _dataCacheService.EquipmentStatusDictionary.AddOrUpdate(item.EquipmentSN, lastRecord, (key, oldValue) =>
                        {
                            if (lastRecord.Timestamp > oldValue.Timestamp)
                            {
                                return lastRecord;
                            }
                            return oldValue;
                        });
                    }
                }
                await Task.Delay(1);
                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error(ex.Message);
            }
        }

        /// <summary>
        /// 推送设备任务完成数
        /// </summary>
        /// <param name="taskInfos"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<BllResult> EquipmentTask(List<EquipmentTaskDto> taskInfos)
        {
            try
            {
                if (taskInfos.Count == 0)
                {
                    return BllResultFactory.Error($"数据不能为空!");
                }

                var equipmentCodes = taskInfos.Select(x => x.EquipmentCode).Distinct().ToList();
                var equipments = await _freeSql.Queryable<EquipmentExtend>().Where(x => equipmentCodes.Contains(x.Code)).ToListAsync(x => new EquipmentExtend
                {
                    Code = x.Code,
                    Name = x.Name,
                    EquipmentTypeId = x.EquipmentTypeId,
                    FactoryCode = x.FactoryCode,
                    ProjectCode = x.ProjectCode,
                });

                if (equipmentCodes.Count != equipments.Count)
                {
                    var temps = equipmentCodes.Except(equipments.Select(x => x.Code));
                    return BllResultFactory.Error($"设备编码[{string.Join(',', temps)}]不存在,请检查数据并重试!");
                }

                var equipmentTypeIds = equipments.Select(x => x.EquipmentTypeId).Distinct().ToList();
                var equipmentTypes = _freeSql.Queryable<EquipmentTypeExtend>().Where(x => equipmentTypeIds.Contains(x.Id)).ToList(x => new EquipmentTypeExtend
                {
                    Id = x.Id,
                    Code = x.Code,
                });

                using var equipmentTaskRepository = _freeSql.GetRepository<EquipmentTaskInfo>();
                var addTemps = new List<EquipmentTaskInfo>();
                var updateTemps = new List<EquipmentTaskInfo>();
                foreach (var item in taskInfos)
                {
                    var record = equipmentTaskRepository.Where(x => x.EquipmentCode == item.EquipmentCode && x.Created.Value.Date == item.DateTime.Date).First();
                    //更新
                    if (record != null)
                    {
                        equipmentTaskRepository.Attach(record);
                        record.TotalTask = item.TotalTask;
                        record.Updated = item.DateTime;
                        updateTemps.Add(record);
                        continue;
                    }
                    //新增
                    var currentEquipment = equipments.Where(x => x.Code == item.EquipmentCode).First();
                    var currentEquipmentTypeCode = equipmentTypes.Where(x => x.Id == currentEquipment.EquipmentTypeId).Select(x => x.Code).FirstOrDefault();
                    addTemps.Add(new EquipmentTaskInfo
                    {
                        EquipmentCode = currentEquipment.Code,
                        EquipmentName = currentEquipment.Name,
                        EquipmentTypeCode = currentEquipmentTypeCode,
                        ProjectCode = currentEquipment.ProjectCode,
                        FactoryCode = currentEquipment.FactoryCode,
                        TotalTask = item.TotalTask,
                        Created = item.DateTime,
                    });
                }

                var updateTotal = equipmentTaskRepository.Update(updateTemps);
                var addTotal = equipmentTaskRepository.Insert(addTemps);
                return BllResultFactory.Success($"操作成功,新增{addTotal.Count}条记录,更新{updateTotal}条记录");
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error($"程序异常:{ex.Message}");
            }
        }

        /// <summary>
        /// 更新客户端在线状态
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<BllResult> UpdateClientStatus(ClientStatusDto data)
        {
            try
            {
                var newValueToAdd = DateTime.Now;
                var clientStatusRepository = _freeSql.GetRepository<ClientStatus>();
                if (!await clientStatusRepository.Where(x => x.ClientKeys == data.ClientId).AnyAsync())
                {
                    return BllResultFactory.Error($"客户端标识:{data.ClientId}不存在,请查验后再试!");
                }
                //添加到队列,统一更新
                _dataCacheService.ClientStatusDictionary.AddOrUpdate(data.ClientId, newValueToAdd, (key, oldValue) => newValueToAdd);
                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error(ex.Message);
            }
        }

        /// <summary>
        /// 更新AGV点位信息
        /// </summary>
        /// <param name="agvPoints"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<BllResult> UpdateAGVPoint(List<AGVPointDto> agvPoints)
        {
            try
            {
                if (agvPoints.Count == 0)
                {
                    return BllResultFactory.Error($"数据不能为空!");
                }

                if (agvPoints.Where(x => string.IsNullOrWhiteSpace(x.EquipmentSN)).Any())
                {
                    return BllResultFactory.Error($"EquipmentSN数据不能为空");
                }

                var equipmentSNs = agvPoints.Select(x => x.EquipmentSN).ToList();
                var equipments = await _freeSql.Queryable<EquipmentExtend>().Where(x => equipmentSNs.Contains(x.Code)).ToListAsync(x => new EquipmentExtend
                {
                    Id = x.Id,
                    Code = x.Code,
                    ProjectCode = x.ProjectCode,
                    FactoryCode = x.FactoryCode,
                });

                var temp = equipmentSNs.Except(equipments.Select(x => x.Code)).Distinct();
                if (temp.Any())
                {
                    var str = string.Join(',', temp);
                    return BllResultFactory.Error($"设备SN[{str}]在系统中不存在,请检测数据是否正确,或联系管理员获取!");
                }

                var allPoints = _freeSql.Queryable<AGVPoint>().Where(x => equipmentSNs.Contains(x.EquipmentCode)).ToList();

                foreach (var item in agvPoints)
                {
                    //数据库现有数据
                    var points = allPoints.Where(x => x.EquipmentCode == item.EquipmentSN).ToList();
                    var oldBarCodes = points.Select(x => x.BarCode).ToList();
                    var newBarCodes = item.BarCodes;

                    var currentEquipment = equipments.Where(x => x.Code == item.EquipmentSN).FirstOrDefault();
                    if (currentEquipment == null)
                    {
                        return BllResultFactory.Error($"设备SN“{item.EquipmentSN}”不存在,请检查数据是否正确,或联系管理员处理!");
                    }

                    if (string.IsNullOrWhiteSpace(currentEquipment.ProjectCode))
                    {
                        return BllResultFactory.Error($"{nameof(item.EquipmentSN)}的“{nameof(currentEquipment.ProjectCode)}”字段数据未配置,请联系管理员处理!");
                    }

                    if (string.IsNullOrWhiteSpace(currentEquipment.FactoryCode))
                    {
                        return BllResultFactory.Error($"{nameof(item.EquipmentSN)}为的“{nameof(currentEquipment.FactoryCode)}”字段数据未配置,请联系管理员处理!");
                    }

                    var addTemps = newBarCodes.Except(oldBarCodes);
                    var removeTemps = oldBarCodes.Except(newBarCodes);

                    _freeSql.Delete<AGVPoint>().Where(x => x.EquipmentCode == item.EquipmentSN && removeTemps.Contains(x.BarCode)).ExecuteAffrows();
                    var temps = addTemps.Select(x => new AGVPoint
                    {
                        EquipmentCode = currentEquipment.Code,
                        BarCode = x,
                        ProjectCode = currentEquipment.ProjectCode,
                        FactoryCode = currentEquipment.FactoryCode,
                        Created = DateTime.Now
                    }).ToList();
                    _freeSql.Insert(temps).ExecuteAffrows();
                }

                return BllResultFactory.Success();
            }
            catch (Exception ex)
            {
                return BllResultFactory.Error(ex.Message);
            }
        }
    }
}