From 8f17b0435e6b4111327a1b437516a90d38627a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E6=AD=A3=E6=98=93?= Date: Fri, 28 Mar 2025 09:33:30 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PBL/Logging/AlarmLogController.cs | 102 +++++++++ DOAN.Model/PBL/AlarmLog.cs | 60 +++++ DOAN.Model/PBL/Dto/AlarmLogDto.cs | 48 ++++ DOAN.Model/PBL/PlcButton.cs | 48 ++++ DOAN.Service/PBL/AlarmLogService.cs | 87 ++++++++ DOAN.Service/PBL/BigScreenService.cs | 42 +++- DOAN.Service/PBL/IService/IAlarmLogService.cs | 21 ++ DOAN.Service/PBL/MESInteractionServcie.cs | 209 +++++++++++++----- DOAN.ServiceCore/DoanBackgroundService.cs | 171 ++++++++++++-- 9 files changed, 709 insertions(+), 79 deletions(-) create mode 100644 DOAN.Admin.WebApi/Controllers/PBL/Logging/AlarmLogController.cs create mode 100644 DOAN.Model/PBL/AlarmLog.cs create mode 100644 DOAN.Model/PBL/Dto/AlarmLogDto.cs create mode 100644 DOAN.Model/PBL/PlcButton.cs create mode 100644 DOAN.Service/PBL/AlarmLogService.cs create mode 100644 DOAN.Service/PBL/IService/IAlarmLogService.cs diff --git a/DOAN.Admin.WebApi/Controllers/PBL/Logging/AlarmLogController.cs b/DOAN.Admin.WebApi/Controllers/PBL/Logging/AlarmLogController.cs new file mode 100644 index 0000000..011ba73 --- /dev/null +++ b/DOAN.Admin.WebApi/Controllers/PBL/Logging/AlarmLogController.cs @@ -0,0 +1,102 @@ +using Microsoft.AspNetCore.Mvc; +using DOAN.Model.PBL.Dto; +using DOAN.Model.PBL; +using DOAN.Service.PBL.IPBLService; +using DOAN.Admin.WebApi.Filters; + +//创建时间:2025-02-22 +namespace DOAN.Admin.WebApi.Controllers.PBL +{ + /// + /// 库存报警日志 + /// + [Verify] + [Route("PBL/AlarmLog")] + public class AlarmLogController : BaseController + { + /// + /// 库存报警日志接口 + /// + private readonly IAlarmLogService _AlarmLogService; + + public AlarmLogController(IAlarmLogService AlarmLogService) + { + _AlarmLogService = AlarmLogService; + } + + /// + /// 查询库存报警日志列表 + /// + /// + /// + [HttpGet("list")] + [ActionPermissionFilter(Permission = "alarmlog:list")] + public IActionResult QueryAlarmLog([FromQuery] AlarmLogQueryDto parm) + { + var response = _AlarmLogService.GetList(parm); + return SUCCESS(response); + } + + + /// + /// 查询库存报警日志详情 + /// + /// + /// + [HttpGet("{Id}")] + [ActionPermissionFilter(Permission = "alarmlog:query")] + public IActionResult GetAlarmLog(string Id) + { + var response = _AlarmLogService.GetInfo(Id); + + var info = response.Adapt(); + return SUCCESS(info); + } + + /// + /// 添加库存报警日志 + /// + /// + [HttpPost] + [ActionPermissionFilter(Permission = "alarmlog:add")] + [Log(Title = "库存报警日志", BusinessType = BusinessType.INSERT)] + public IActionResult AddAlarmLog([FromBody] AlarmLogDto parm) + { + var modal = parm.Adapt().ToCreate(HttpContext); + + var response = _AlarmLogService.AddAlarmLog(modal); + + return SUCCESS(response); + } + + /// + /// 更新库存报警日志 + /// + /// + [HttpPut] + [ActionPermissionFilter(Permission = "alarmlog:edit")] + [Log(Title = "库存报警日志", BusinessType = BusinessType.UPDATE)] + public IActionResult UpdateAlarmLog([FromBody] AlarmLogDto parm) + { + var modal = parm.Adapt().ToUpdate(HttpContext); + var response = _AlarmLogService.UpdateAlarmLog(modal); + + return ToResponse(response); + } + + /// + /// 删除库存报警日志 + /// + /// + [HttpPost("delete/{ids}")] + [ActionPermissionFilter(Permission = "alarmlog:delete")] + [Log(Title = "库存报警日志", BusinessType = BusinessType.DELETE)] + public IActionResult DeleteAlarmLog([FromRoute]string ids) + { + var idArr = Tools.SplitAndConvert(ids); + + return ToResponse(_AlarmLogService.Delete(idArr)); + } + + } +} \ No newline at end of file diff --git a/DOAN.Model/PBL/AlarmLog.cs b/DOAN.Model/PBL/AlarmLog.cs new file mode 100644 index 0000000..bc73a0e --- /dev/null +++ b/DOAN.Model/PBL/AlarmLog.cs @@ -0,0 +1,60 @@ + +namespace DOAN.Model.PBL +{ + /// + /// 库存报警日志 + /// + [SugarTable("alarm_log")] + public class AlarmLog + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false)] + public string Id { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 编码 + /// + public string Code { get; set; } + + /// + /// 料架id + /// + [SugarColumn(ColumnName = "storagelocation_id")] + public int? StoragelocationId { get; set; } + + /// + /// 系统类别 + /// + public int? Type { get; set; } + + /// + /// 系统状态 + /// + public int? Status { get; set; } + + /// + /// 系统备注 + /// + public string Remark { get; set; } + + /// + /// 发生时间 + /// + [SugarColumn(ColumnName = "action_time")] + public DateTime? ActionTime { get; set; } + + /// + /// 完成报警时间 + /// + [SugarColumn(ColumnName = "end_time")] + public DateTime? EndTime { get; set; } + + } +} \ No newline at end of file diff --git a/DOAN.Model/PBL/Dto/AlarmLogDto.cs b/DOAN.Model/PBL/Dto/AlarmLogDto.cs new file mode 100644 index 0000000..0863d94 --- /dev/null +++ b/DOAN.Model/PBL/Dto/AlarmLogDto.cs @@ -0,0 +1,48 @@ + +namespace DOAN.Model.PBL.Dto +{ + /// + /// 库存报警日志查询对象 + /// + public class AlarmLogQueryDto : PagerInfo + { + public string Name { get; set; } + public string Code { get; set; } + public int? StoragelocationId { get; set; } + public int? Type { get; set; } + public int? Status { get; set; } + + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + } + + /// + /// 库存报警日志输入输出对象 + /// + public class AlarmLogDto + { + [Required(ErrorMessage = "主键不能为空")] + public string Id { get; set; } + + public string Name { get; set; } + + public string Code { get; set; } + + public int? StoragelocationId { get; set; } + + public int? Type { get; set; } + + public int? Status { get; set; } + + public string Remark { get; set; } + + public DateTime? ActionTime { get; set; } + + public DateTime? EndTime { get; set; } + + + + [ExcelColumn(Name = "系统类别")] + public string TypeLabel { get; set; } + } +} \ No newline at end of file diff --git a/DOAN.Model/PBL/PlcButton.cs b/DOAN.Model/PBL/PlcButton.cs new file mode 100644 index 0000000..bdc605b --- /dev/null +++ b/DOAN.Model/PBL/PlcButton.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DOAN.Model.PBL +{ + /// + /// PLC地址 + /// + [SugarTable("plc_button_table")] + public class PlcButton + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true, IsIdentity = false)] + public int Id { get; set; } + + /// + /// 按钮名称 + /// + public string Name { get; set; } + + /// + /// 按钮型号 + /// + public string Code { get; set; } + + /// + /// 料架id + /// + [SugarColumn(ColumnName = "storagelocation_id")] + public int StoragelocationId { get; set; } + + /// + /// PLC地址 + /// + public int Address { get; set; } + + /// + /// PLC地址下标 + /// + public int Index { get; set; } + + } +} diff --git a/DOAN.Service/PBL/AlarmLogService.cs b/DOAN.Service/PBL/AlarmLogService.cs new file mode 100644 index 0000000..2b06b78 --- /dev/null +++ b/DOAN.Service/PBL/AlarmLogService.cs @@ -0,0 +1,87 @@ +using Infrastructure.Attribute; +using Infrastructure.Extensions; +using DOAN.Model.PBL.Dto; +using DOAN.Model.PBL; +using DOAN.Repository; +using DOAN.Service.PBL.IPBLService; + +namespace DOAN.Service.PBL +{ + /// + /// 库存报警日志Service业务层处理 + /// + [AppService(ServiceType = typeof(IAlarmLogService), ServiceLifetime = LifeTime.Transient)] + public class AlarmLogService : BaseService, IAlarmLogService + { + /// + /// 查询库存报警日志列表 + /// + /// + /// + public PagedInfo GetList(AlarmLogQueryDto parm) + { + var predicate = QueryExp(parm); + + var response = Queryable() + .Where(predicate.ToExpression()) + .ToPage(parm); + + return response; + } + + + /// + /// 获取详情 + /// + /// + /// + public AlarmLog GetInfo(string Id) + { + var response = Queryable() + .Where(x => x.Id == Id) + .First(); + + return response; + } + + /// + /// 添加库存报警日志 + /// + /// + /// + public AlarmLog AddAlarmLog(AlarmLog model) + { + return Insertable(model).ExecuteReturnEntity(); + } + + /// + /// 修改库存报警日志 + /// + /// + /// + public int UpdateAlarmLog(AlarmLog model) + { + return Update(model, true); + } + + /// + /// 查询导出表达式 + /// + /// + /// + private static Expressionable QueryExp(AlarmLogQueryDto parm) + { + var predicate = Expressionable.Create() + .AndIF(!string.IsNullOrEmpty(parm.Name),it =>it.Name.Contains(parm.Name)) + .AndIF(!string.IsNullOrEmpty(parm.Code), it => it.Code.Contains(parm.Code)) + .AndIF(parm.Type > -1, it => it.Type == parm.Type) + .AndIF(parm.Status > -1, it => it.Status == parm.Status) + .AndIF(parm.StoragelocationId > -1, it => it.StoragelocationId == parm.StoragelocationId) + .AndIF(parm.StartTime.HasValue, it => it.ActionTime >= parm.StartTime) + .AndIF(parm.EndTime.HasValue, it => it.ActionTime <= parm.EndTime) + ; + + return predicate; + } + } +} \ No newline at end of file diff --git a/DOAN.Service/PBL/BigScreenService.cs b/DOAN.Service/PBL/BigScreenService.cs index adbb806..2d44410 100644 --- a/DOAN.Service/PBL/BigScreenService.cs +++ b/DOAN.Service/PBL/BigScreenService.cs @@ -142,7 +142,8 @@ public class BigScreenService : BaseService, IBigScreenService AlarmNum = layer.AlarmNum ?? 2, // 确认默认值 PackageNum = layer.PackageNum ?? 0, IsLight = layer.IsLight == 1, - IsFeedingMaterial = (layer.PackageNum ?? 0) <= (layer.AlarmNum ?? 2) && layer.IsLackAlarm == 1 + // IsFeedingMaterial = (layer.PackageNum ?? 0) < (layer.AlarmNum ?? 2) && layer.IsLackAlarm == 1 + IsFeedingMaterial = CalculateIsOneLayerNumFeedingMaterial(layer) }).ToArray(); var rackDto = new BigScreenDto { @@ -150,7 +151,8 @@ public class BigScreenService : BaseService, IBigScreenService RackCode = group.Key, IsLight = layers.Any(l => l.IsLight == 1), IsInUse = layers.Any(l => l.IsLackAlarm == 1), - IsFeedingMaterial = layers.Any(l => (l.PackageNum ?? 0) <= (l.AlarmNum ?? 2) && l.IsLackAlarm == 1), + // IsFeedingMaterial = layers.Any(l => (l.PackageNum ?? 0) < (l.AlarmNum ?? 2) && l.IsLackAlarm == 1), + IsFeedingMaterial = CalculateIsAllFeedingMaterial(layers), LayerObjectArray = layerObjects }; result.Add(rackDto); @@ -158,4 +160,40 @@ public class BigScreenService : BaseService, IBigScreenService return result; } + // 将复杂逻辑封装到方法中 + private bool CalculateIsAllFeedingMaterial(List layers) + { + int OneTotalPackageNum = layers.Where(it => it.LayerNum == 1).Sum(it => it.PackageNum ?? 0); + int SecondTotalPackageNum = layers.Where(it => it.LayerNum == 2).Sum(it => it.PackageNum ?? 0); + int alarmNum = 2; + if (layers.Any(ls => ls.Remark == "合并料架")) + { + alarmNum = 3; + } + else + { + alarmNum = layers[0].AlarmNum ?? 0; + } + return OneTotalPackageNum < alarmNum || SecondTotalPackageNum < alarmNum; + + } + + private bool CalculateIsOneLayerNumFeedingMaterial(Storagelocation layer) + { + if (layer.Remark == "合并料架") + { + int totalPackageNum = Context.Queryable() + .Where(it => it.LayerNum == layer.LayerNum) + .Where(it => it.RackCode == layer.RackCode) + .Sum(it => it.PackageNum ?? 0); + return totalPackageNum < 3; + } + else + { + return (layer.PackageNum ?? 0) < (layer.AlarmNum ?? 2) && layer.IsLackAlarm == 1; + } + + + } + } diff --git a/DOAN.Service/PBL/IService/IAlarmLogService.cs b/DOAN.Service/PBL/IService/IAlarmLogService.cs new file mode 100644 index 0000000..3fc032b --- /dev/null +++ b/DOAN.Service/PBL/IService/IAlarmLogService.cs @@ -0,0 +1,21 @@ +using DOAN.Model.PBL.Dto; +using DOAN.Model.PBL; + +namespace DOAN.Service.PBL.IPBLService +{ + /// + /// 库存报警日志service接口 + /// + public interface IAlarmLogService : IBaseService + { + PagedInfo GetList(AlarmLogQueryDto parm); + + AlarmLog GetInfo(string Id); + + + AlarmLog AddAlarmLog(AlarmLog parm); + int UpdateAlarmLog(AlarmLog parm); + + + } +} diff --git a/DOAN.Service/PBL/MESInteractionServcie.cs b/DOAN.Service/PBL/MESInteractionServcie.cs index fc96806..68d4495 100644 --- a/DOAN.Service/PBL/MESInteractionServcie.cs +++ b/DOAN.Service/PBL/MESInteractionServcie.cs @@ -8,7 +8,9 @@ using Mapster; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.SignalR; using Newtonsoft.Json.Linq; +using System; using System.Security.Cryptography.X509Certificates; +using static System.Formats.Asn1.AsnWriter; namespace DOAN.Service.PBL { @@ -39,77 +41,154 @@ namespace DOAN.Service.PBL /// public bool MESLightUp(LightUpDto light, PLCTool pLCTool) { - int result = 0; - // 1.记录MES交互记录 - MES_Interation_Log item = light.Adapt(); - item.Id = XUEHUA; - item.CreatedTime = DateTime.Now; - Context.Insertable(item).ExecuteCommand(); - // 2.根据总成零件号 ,版本 查询对应零件号,使得对应料架亮灯 - List MirrorshellShelfList = Context - .Queryable() - .Where(it => - it.Partnumber - == SqlFunc - .Subqueryable() - .Where(It => It.Productcode == light.AssemblyPartNumber) - .Select(it => it.MirrorshellCode) - ) - .ToList(); - if (MirrorshellShelfList != null && MirrorshellShelfList.Count() > 0) + try { - Storagelocation storagelocation = new(); - // 是否合并料架 - bool isMergeRack = MirrorshellShelfList.Count > 1; - if (isMergeRack) + Context.Ado.BeginTran(); + int result = 0; + // 1.记录MES交互记录 + MES_Interation_Log item = light.Adapt(); + item.Id = XUEHUA; + item.CreatedTime = DateTime.Now; + Context.Insertable(item).ExecuteCommand(); + // 2.根据总成零件号 ,版本 查询对应零件号,使得对应料架亮灯 + List MirrorshellShelfList = Context + .Queryable() + .Where(it => + it.Partnumber + == SqlFunc + .Subqueryable() + .Where(It => It.Productcode == light.AssemblyPartNumber) + .Select(it => it.MirrorshellCode) + ) + .ToList(); + if (MirrorshellShelfList != null && MirrorshellShelfList.Count() > 0) { - // 合并料架 判断先进先出 - foreach(var shelf in MirrorshellShelfList) + Storagelocation storagelocation = new(); + // 是否合并料架 + bool isMergeRack = MirrorshellShelfList.Count > 1; + if (isMergeRack) { - // 第一个有箱子的 - if(shelf.PackageNum > 0) + Storagelocation leftShelf = MirrorshellShelfList[0]; + Storagelocation rightShelf = MirrorshellShelfList[1]; + // 料架今天是否有补料信息 (补料完成信号判定) + var today = DateTime.Today; + bool hasAlarm = Context.Queryable() + .Where(it => it.ActionTime != null && + it.ActionTime >= today && + it.ActionTime < today.AddDays(1)) + .Where(it => it.StoragelocationId == leftShelf.Id || it.StoragelocationId == rightShelf.Id) + .OrderByDescending(it => it.ActionTime) + .Any(); + int packageTotal = leftShelf.PackageNum.Value + rightShelf.PackageNum.Value; + //1. 在今日有补料信号的前提下 料架>6箱(已补料,未消耗最新箱,先消耗旧箱,哪边存在报警补哪边) + if (packageTotal == 8 && hasAlarm) { - storagelocation = shelf; - continue; + if (leftShelf.IsLackAlarm == 1) + { + storagelocation = leftShelf; + } + else if (rightShelf.IsLackAlarm == 1) + { + storagelocation = rightShelf; + } + else + { + storagelocation = rightShelf; + } + } + else if (packageTotal == 7 && hasAlarm) + { + if (leftShelf.IsLackAlarm == 1) + { + storagelocation = leftShelf; + } + else if (rightShelf.IsLackAlarm == 1) + { + storagelocation = rightShelf; + } + else + { + storagelocation = rightShelf; + } + // 切换报警 + DoChangeAlarmStatus(leftShelf, rightShelf); + } + else if (packageTotal <= 6 && packageTotal > 4 && hasAlarm) + { + if (leftShelf.IsLackAlarm == 1) + { + storagelocation = rightShelf; + } + else if (rightShelf.IsLackAlarm == 1) + { + storagelocation = leftShelf; + } + else + { + storagelocation = rightShelf; + } + } + else + { + // 默认情况 + // 合并料架 判断 20250224 左先出原则 + foreach (var shelf in MirrorshellShelfList) + { + // 第一个有箱子的料架 + if (shelf.PackageNum > 0) + { + storagelocation = shelf; + break; + } + } + if (storagelocation.RackCode == null) + { + storagelocation = MirrorshellShelfList[^1]; + } } } - if (storagelocation.RackCode == null) + else { - storagelocation = MirrorshellShelfList[^1]; + // 单独料架 + storagelocation = MirrorshellShelfList[0]; } + // 3.对应料架亮灯 + bool isSucesss = pLCTool.WriteBit(storagelocation.PlcAddress, true); + if (isSucesss) + { + storagelocation.IsLight = 1; + result += Context.Updateable(storagelocation).ExecuteCommand(); + } + //亮灯日志 + Light_Log light_Log = new Light_Log(); + light_Log.Id = XUEHUA; + light_Log.LightOperation = 1; + light_Log.Operationer = "PBL"; + light_Log.CreatedTime = DateTime.Now; + light_Log.ShelfCode = storagelocation.RackCode; + light_Log.LayerNum = storagelocation.LayerNum; + light_Log.IsSuccess = isSucesss; + result += Context.Insertable(light_Log).ExecuteCommand(); } else { - // 单独料架 - storagelocation = MirrorshellShelfList[0]; + // 发送socket 通知 + string message = + $"MES产品编号{light.AssemblyPartNumber}或者版本{light.Version},在PBL中找不到。请维护PBL料架信息--{DateTime.Now}"; + notificationHubContext.Clients.All.SendAsync("PBL_bom_except", message); + Context.Ado.RollbackTran(); + return false; } - // 3.对应料架亮灯 - bool isSucesss = pLCTool.WriteBit(storagelocation.PlcAddress, true); - if (isSucesss) { - storagelocation.IsLight = 1; - result += Context.Updateable(storagelocation).ExecuteCommand(); - } - //亮灯日志 - Light_Log light_Log = new Light_Log(); - light_Log.Id = XUEHUA; - light_Log.LightOperation = 1; - light_Log.Operationer = "PBL"; - light_Log.CreatedTime = DateTime.Now; - light_Log.ShelfCode = storagelocation.RackCode; - light_Log.LayerNum = storagelocation.LayerNum; - light_Log.IsSuccess = isSucesss; - result += Context.Insertable(light_Log).ExecuteCommand(); + notificationHubContext.Clients.All.SendAsync("PBL_storagelocation_change"); + Context.Ado.CommitTran(); + return result > 0; } - else + catch (Exception) { - // 发送socket 通知 - string message = - $"MES产品编号{light.AssemblyPartNumber}或者版本{light.Version},在PBL中找不到。请维护PBL料架信息--{DateTime.Now}"; - notificationHubContext.Clients.All.SendAsync("PBL_bom_except", message); - + Context.Ado.RollbackTran(); return false; } - return result > 0; + } /// @@ -159,7 +238,27 @@ namespace DOAN.Service.PBL CreatedTime = DateTime.Now }; result += Context.Insertable(light_Log).ExecuteCommand(); + notificationHubContext.Clients.All.SendAsync("PBL_storagelocation_change"); return result > 0; } + + /// + /// 交换合并料架报警配置 + /// + /// + /// + /// + public int DoChangeAlarmStatus(Storagelocation leftShelf, Storagelocation rightShelf) + { + int result = 0; + // 都判断完后切换报警 + leftShelf.IsLackAlarm = leftShelf.IsLackAlarm == 1 ? 0 : 1; + leftShelf.AlarmNum = leftShelf.IsLackAlarm == 1 ? rightShelf.AlarmNum : 0; + result += Context.Updateable(leftShelf).ExecuteCommand(); + rightShelf.IsLackAlarm = rightShelf.IsLackAlarm == 1 ? 0 : 1; + rightShelf.AlarmNum = rightShelf.IsLackAlarm == 1 ? leftShelf.AlarmNum : 0; + result += Context.Updateable(rightShelf).ExecuteCommand(); + return result; + } } } diff --git a/DOAN.ServiceCore/DoanBackgroundService.cs b/DOAN.ServiceCore/DoanBackgroundService.cs index 4d69059..3d91b76 100644 --- a/DOAN.ServiceCore/DoanBackgroundService.cs +++ b/DOAN.ServiceCore/DoanBackgroundService.cs @@ -1,15 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography.X509Certificates; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using DOAN.Infrastructure.PLC; using DOAN.Model.PBL; using DOAN.ServiceCore.Signalr; +using JinianNet.JNTemplate; using Microsoft.AspNetCore.SignalR; +using Microsoft.Data.SqlClient; using Microsoft.Extensions.Hosting; using SqlSugar.IOC; +using static System.Formats.Asn1.AsnWriter; namespace DOAN.ServiceCore { @@ -25,6 +29,7 @@ namespace DOAN.ServiceCore private PLCTool pLCTool; private List storagelocationList = new List(); private List pointPositionList = new List(); + private List buttonPositionList = new List(); private Timer refreshTimer; public DoanBackgroundService(IHubContext hubContext) @@ -34,17 +39,18 @@ namespace DOAN.ServiceCore public Task StartAsync(CancellationToken cancellationToken) { + + Console.WriteLine($"PCL定时任务开启!"); pLCTool = new PLCTool(); pLCTool.ConnectPLC(); // 初始化料架层和点位表数据 RefreshData(null); - // 启动后台任务 _executingTask = ExecuteAsync(_cancellationTokenSource.Token); - // 设置定时器每分钟刷新一次料架层和点位表 - refreshTimer = new Timer(RefreshData, null, TimeSpan.Zero, TimeSpan.FromMinutes(1)); + // 设置定时器每10分钟刷新一次料架层和点位表 .FromMinutes(1) + refreshTimer = new Timer(RefreshData, null, TimeSpan.Zero, TimeSpan.FromMinutes(10)); return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; } @@ -66,7 +72,7 @@ namespace DOAN.ServiceCore { if (position < 0 || position > 7) { - throw new ArgumentOutOfRangeException(nameof(position), "Position must be between 0 and 7."); + throw new ArgumentOutOfRangeException(nameof(position), "Position must be between 0 and 7" + position); } byte mask = (byte)(1 << position); @@ -81,6 +87,7 @@ namespace DOAN.ServiceCore /// private async Task ExecuteAsync(CancellationToken stoppingToken) { + Console.WriteLine($"PCL定时任务ExecuteAsync!"); try { while (!stoppingToken.IsCancellationRequested) @@ -89,13 +96,97 @@ namespace DOAN.ServiceCore byte[] plcSensorValues; try { - plcSensorValues = pLCTool.ReadAllValue("VB100", 12); + // 读取1开头100,101。。。等全部地址数据 + + plcSensorValues = pLCTool.ReadAllValue("VB100", 13); } catch (Exception ex) { Console.WriteLine($"读取PLC数据异常: {ex.Message}\n堆栈跟踪: {ex.StackTrace}"); continue; } + // XX 按钮处理 + + + // 读取按钮PLC I/O状态 + foreach (PlcButton button in buttonPositionList) + { + + int row = button.Address - 100; + int col = button.Index; + + if (plcSensorValues != null && row >= 0 && row < plcSensorValues.Length && !GetInvertedBit(plcSensorValues[row], col)) + { + // 按钮按下灭灯 + if (button.Code == "灭灯按钮") + { + + Storagelocation offLightstoragelocation = storagelocationList.Where(it => it.Id == button.StoragelocationId).First(); + bool isSuccess = pLCTool.WriteBit(offLightstoragelocation.PlcAddress, false); + if (isSuccess) + { + offLightstoragelocation.IsLight = 0; + using (var scope = DbScoped.SugarScope.CopyNew()) + { + await scope.Updateable(offLightstoragelocation).ExecuteCommandAsync(); + //灭灯日志 + Light_Log light_Log = new Light_Log + { + Id = SnowFlakeSingle.Instance.NextId().ToString(), + LightOperation = 2, + LayerNum = offLightstoragelocation.LayerNum, + ShelfCode = offLightstoragelocation.RackCode, + IsSuccess = isSuccess, + Operationer = "按钮手动灭灯", + CreatedTime = DateTime.Now + }; + await scope.Insertable(light_Log).ExecuteCommandAsync(); + } + } + await notificationHubContext.Clients.All.SendAsync("PBL_storagelocation_change", "手动灭灯"); + } + // 镜体补料按钮 + if (button.Code == "镜体补料按钮") + { + bool lightStatus = pLCTool.ReadBit("V208.4"); + // 原本灭灯,亮灯补料 + if (!lightStatus) + { + using (var scope = DbScoped.SugarScope.CopyNew()) + { + AlarmLog alarmLog = new AlarmLog + { + Id = SnowFlakeSingle.Instance.NextId().ToString(), + Name = "镜体需要补料", + Code = "镜体补料", + StoragelocationId = 0, + Type = 2, + Status = 1, + ActionTime = DateTime.Now, + EndTime = DateTime.Now + }; + scope.Insertable(alarmLog).ExecuteCommand(); + var alarmData = new + { + RackCode = "镜体需要补料", + LayerNum = 1, + CurrentPackageCount = 0, + AlarmThreshold = 1, + ActionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss").ToString(), + }; + string alarmMessage = System.Text.Json.JsonSerializer.Serialize(alarmData); + await notificationHubContext.Clients.All.SendAsync("PBL_lack_alarm", alarmMessage); + } + } + else + { + // 原本亮灯,按一下灭灯 + pLCTool.WriteBit("V208.4", false); + } + } + } + } + var updateStoragelocationList = new List(); var inventoryLogs = new List(); @@ -134,17 +225,46 @@ namespace DOAN.ServiceCore } // 补料报警触发 - if (layerItem.IsLackAlarm == 1 && currentPackageCount <= layerItem.AlarmNum.GetValueOrDefault()) + if (layerItem.IsLackAlarm == 1 && currentPackageCount < layerItem.AlarmNum) { - var alarmData = new + using (var scope = DbScoped.SugarScope.CopyNew()) { - RackCode = layerItem.RackCode, - LayerNum = layerItem.LayerNum, - CurrentPackageCount = currentPackageCount, - AlarmThreshold = layerItem.AlarmNum - }; - string alarmMessage = System.Text.Json.JsonSerializer.Serialize(alarmData); - await notificationHubContext.Clients.All.SendAsync("PBL_lack_alarm", alarmMessage); + // 是否已经报警过,并且 + bool hasLastAlarm = await scope.Queryable() + .Where(it => it.StoragelocationId == layerItem.Id) + .Where(it => it.Type == 1) + .Where(it => it.Status == 1) + .Where(it => DateTime.Now <= it.EndTime) + .OrderBy(it => it.ActionTime, OrderByType.Desc) + .AnyAsync(); + if (!hasLastAlarm) + { + string layerName = layerItem.LayerNum == 1 ? "上层" : "中层"; + AlarmLog alarmLog = new AlarmLog + { + Id = SnowFlakeSingle.Instance.NextId().ToString(), + Name = $"镜壳需补料:{layerItem.RackCode} {layerName} 阈值{layerItem.AlarmNum}", + Code = "镜壳补料", + StoragelocationId = layerItem.Id, + Type = 1, + Status = 1, + ActionTime = DateTime.Now, + // 过X分钟超时jishi + EndTime = DateTime.Now.AddMinutes(2) + }; + await scope.Insertable(alarmLog).ExecuteCommandAsync(); + var alarmData = new + { + RackCode = layerItem.RackCode, + LayerNum = layerItem.LayerNum, + CurrentPackageCount = currentPackageCount, + AlarmThreshold = layerItem.AlarmNum, + ActionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss").ToString(), + }; + string alarmMessage = System.Text.Json.JsonSerializer.Serialize(alarmData); + await notificationHubContext.Clients.All.SendAsync("PBL_lack_alarm", alarmMessage); + } + } } } } @@ -152,7 +272,12 @@ namespace DOAN.ServiceCore // 更新库存 if (updateStoragelocationList.Any()) { - await DbScoped.SugarScope.CopyNew().Updateable(updateStoragelocationList).ExecuteCommandAsync(); + using (var scope = DbScoped.SugarScope.CopyNew()) + { + await scope.Updateable(updateStoragelocationList) + .IgnoreColumns(it => new { it.IsLight, it.IsLackAlarm, it.AlarmNum, it.MaxCapacity }) + .ExecuteCommandAsync(); + } // 发送库存变更Socket通知 string changeMessage = "库存变动"; await notificationHubContext.Clients.All.SendAsync("PBL_storagelocation_change", changeMessage); @@ -163,8 +288,8 @@ namespace DOAN.ServiceCore { await DbScoped.SugarScope.CopyNew().Insertable(inventoryLogs).ExecuteCommandAsync(); } - // 添加延迟以避免频繁查询 - await Task.Delay(200, stoppingToken); + // 添加延迟以避免频繁查询(暂定3秒防误触) + await Task.Delay(2000, stoppingToken); } } catch (OperationCanceledException) @@ -181,14 +306,16 @@ namespace DOAN.ServiceCore /// 刷新料架层和点位表数据 /// /// - private void RefreshData(object state) + private async void RefreshData(object state) { try { + Console.WriteLine($"刷新料架点位和按钮"); using (var scope = DbScoped.SugarScope.CopyNew()) { - storagelocationList = scope.Queryable().ToListAsync().Result; - pointPositionList = scope.Queryable().ToListAsync().Result; + storagelocationList = await scope.Queryable().ToListAsync(); + pointPositionList = await scope.Queryable().ToListAsync(); + buttonPositionList = await scope.Queryable().ToListAsync(); } } catch (Exception ex) @@ -235,12 +362,12 @@ namespace DOAN.ServiceCore { try { - pLCTool.ConnectClose(); _cancellationTokenSource.Cancel(); - _executingTask?.Wait(); + _executingTask?.GetAwaiter().GetResult(); // 确保任务完成 _cancellationTokenSource.Dispose(); refreshTimer?.Change(Timeout.Infinite, Timeout.Infinite); refreshTimer?.Dispose(); + pLCTool.ConnectClose(); } catch (Exception ex) {