using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SlnMesnac.Business.@base; using SlnMesnac.Common; using SlnMesnac.Config; using SlnMesnac.Model.domain; using SlnMesnac.Model.dto; using SlnMesnac.Model.enums; using SlnMesnac.Plc; using SlnMesnac.Repository.service; using SlnMesnac.Rfid; using SlnMesnac.TouchSocket; using SqlSugar; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using TouchSocket.Core; using static System.Net.Mime.MediaTypeNames; #region << 版 本 注 释 >> /*-------------------------------------------------------------------- * 版权所有 (c) 2024 WenJY 保留所有权利。 * CLR版本:4.0.30319.42000 * 机器名称:LAPTOP-E0N2L34V * 命名空间:SlnMesnac.Business * 唯一标识:f79d6d97-d9b0-4b0b-a442-e6bb1c1c79e6 * * 创建者:WenJY * 电子邮箱:wenjy@mesnac.com * 创建时间:2024-04-10 16:36:01 * 版本:V1.0.0 * 描述: * *-------------------------------------------------------------------- * 修改人: * 时间: * 修改说明: * * 版本:V1.0.0 *--------------------------------------------------------------------*/ #endregion << 版 本 注 释 >> namespace SlnMesnac.Business { /// /// 设备产出业务逻辑 /// public class ProdCompletionBusiness : BaseBusiness { private FJ500Comm.FJ500SP fj500s = new FJ500Comm.FJ500SP(); private readonly IMesProductPlanService _mesProductPlanService; private readonly IMesProductOrderService _mesProductOrderService; private readonly IMesPrdBarCodeService _barCodeTaskService; private readonly IBaseMaterialInfoService _baseMaterialInfoService; private readonly IMesBaseBarcodeInfoService _mesBaseBarcodeInfoService; public readonly IMesPrdBarCodeService _mesPrdBarCodeService; private readonly IRealPalletTaskService _realPalletTaskService; private ISqlSugarClient sqlSugarClient; public ProdCompletionBusiness(ISqlSugarClient _sqlSugarClient, ILogger logger, AppConfig appConfig, List plcFactories, List rfidFactories, IMesProductPlanService mesProductPlanService, IMesPrdBarCodeService barCodeTaskService, IServiceProvider serviceProvider) : base(logger, appConfig, plcFactories, rfidFactories, serviceProvider) { UdpServer.ReceivedPMCodeEvent += PmCodeHandler; sqlSugarClient = _sqlSugarClient; _mesProductPlanService = mesProductPlanService; _barCodeTaskService = barCodeTaskService; _realPalletTaskService = serviceProvider.GetRequiredService(); _mesBaseBarcodeInfoService = serviceProvider.GetRequiredService(); _mesPrdBarCodeService = serviceProvider.GetRequiredService(); _mesProductOrderService = serviceProvider.GetRequiredService(); _baseMaterialInfoService = serviceProvider.GetRequiredService(); Init(); } private void test() { // MesProductPlanDetail? mesProductPlanDetail = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.PlanCode == "20240724144533JL001"); string epc = "A123456"; _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto); string bigCode = GenerateBigBarcode(productPlanDto, epc); // BindingBarCode(bigCode, epc); } private static bool isEmptyPalletHandleRunning = false; public void Init() { Task.Run(async () => { await EmptyPalletHandle(); }); Task.Run(() => { DeviceOutPutHandle(); }); } /// /// 喷码机返回信息解析处理 /// 1.传输完成及喷码完成 /// 2.喷码机状态检查 /// /// private void PmCodeHandler(string str) { //设备状态30s一次 if (str.Substring(0, 4) == "0A0C") { //推送最新需要打印条码,防止无可打印条码 var result = fj500s.GetState(str); if (result == "0E") { // 喷码机设备故障,todo 推送报警 _logger.LogError("喷码机设备故障,待推送报警"); } } else // 传输完成及喷码完成结果 { var result = fj500s.GetAck(str); if(result == "00") { // 发送数据成功 } else if(result =="0E") { // 发送数据失败,重新发送,超过10次失败推送报警 }else if(result == "0F") { //打印完成,更新当前打印条码信息,并推送下一条信息 } } } /// /// 设备产出逻辑处理 /// private async void DeviceOutPutHandle() { // 读取RFID失败重试次数,超过5次推送报警 int readRfidAmount = 0; while (true) { try { var plc = base.GetPlcByKey("plc"); if (plc == null) { _logger.LogInformation("读取小包出口信号,PLC连接信息为空......"); Thread.Sleep(3000); continue; } // plc.writeStringByAddress(GetPlcAddressByConfigKey("2楼码垛出口RFID条码地址"), "JYHB01010125", 12); if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("2楼小包出口到位信号"))) { _logger.LogInformation("等待小包出口信号触发......"); Thread.Sleep(5000); continue; } string rfid = plc.readStringByAddress(GetPlcAddressByConfigKey("2楼码垛出口RFID条码地址"),12).Replace("\0",""); if (!string.IsNullOrWhiteSpace(rfid)) { _logger.LogInformation("小包出口已经有RFID了,无需再读"); Thread.Sleep(5000); continue; } string epcStr = await ReadEpcStrByRfidKeyAsync("secondFloorOut"); if (string.IsNullOrEmpty(epcStr)) { _logger.LogError("小包出口到位信号触发,读取RFID失败......"); //判断前一位置是否有托盘缓存 RealPalletTask realPalletTask = _realPalletTaskService.Query().FirstOrDefault(); if (realPalletTask != null) { epcStr = realPalletTask.PalletCode; } else { readRfidAmount++; if (readRfidAmount == 5) { string virtualEpc = NoReadRFIDAlarm(); _logger.LogError("小包出口到位信号触发,读取RFID失败超过5次......"); if (!string.IsNullOrEmpty(virtualEpc)) { epcStr = virtualEpc; _logger.LogError("小包出口到位信号触发,读取RFID失败超过5次,生成虚拟托盘号:" + virtualEpc); } } else { Thread.Sleep(1000 * 5); continue; } } } RefreshMessage($"小包出口读取到RFID条码{epcStr},开始绑定流程:"); _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto); if (productPlanDto == null) { //TODO:没有计划的话,处理方式待定 throw new ArgumentException($"未获取到正在执行的生产计划"); } #region 开启事务 sqlSugarClient.AsTenant().BeginTran(); //1.生成大条码插入mes_barcode_info; string bigCode = GenerateBigBarcode(productPlanDto, epcStr); //2.40个小条码及大条码绑定托盘号,小条码绑定大条码,,托盘绑定大条码 BindingBarCode(bigCode, epcStr, productPlanDto); //3.更新mes_product_plan及mes_product_order表,更新已生产数量 UpdatePlanInfoAndOrder(productPlanDto); // 删除正在使用的数据库托盘号 List realPalletList = _realPalletTaskService.Query(x => x.PalletCode == epcStr); if (realPalletList != null && realPalletList.Count>0) { sqlSugarClient.AsTenant().GetConnection("mes").Deleteable(realPalletList).ExecuteCommand(); } // 写入PLC RFID号供调度使用 plc.writeStringByAddress(GetPlcAddressByConfigKey("2楼码垛出口RFID条码地址"), epcStr,12); // plc.writeByteByAddress(GetPlcAddressByConfigKey("2楼小包出口到位信号"), 2); readRfidAmount = 0; sqlSugarClient.AsTenant().CommitTran(); #endregion } catch (Exception ex) { sqlSugarClient.AsTenant().RollbackTran(); _logger.LogError($"DeviceOutPutHandle异常:{ex.Message}"); } Thread.Sleep(1000 * 5); } } /// /// 推送报警,并生成虚拟托盘号暂时使用,虚拟托盘号储存到报警信息,人工处理可以将虚拟托盘号替换为正常托盘号 /// 返回生产的虚拟托盘号 /// private string NoReadRFIDAlarm() { string epcStr; try { epcStr = "TEMP" + DateTime.Now.ToString("MMddHHmm"); sqlSugarClient.AsTenant().BeginTran(); // 有报警,找出该设备的报警记录,如果没有报警记录或者报警记录已结束,则新增报警记录 int warnRuleId = int.Parse(WarnStatusEnum.小包出口读取RFID失败5次.GetDescription()); DmsRecordAlarmTime? dmsRecordAlarmTime = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); if (dmsRecordAlarmTime == null) { DmsBaseAlarmRule dmsBaseAlarmRule = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); dmsRecordAlarmTime = new DmsRecordAlarmTime(); dmsRecordAlarmTime.DeviceId = dmsBaseAlarmRule.DeviceId; dmsRecordAlarmTime.AlarmRuleId = warnRuleId; dmsRecordAlarmTime.AlarmBeginTime = DateTime.Now; dmsRecordAlarmTime.ContinueTime = dmsBaseAlarmRule.ContinueTime; dmsRecordAlarmTime.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmTime.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmTime.AlarmData = WarnStatusEnum.小包出口读取RFID失败5次.ToString(); dmsRecordAlarmTime.HandleSuggest = dmsBaseAlarmRule.HandleSuggest + ";;虚拟托盘号为:" + epcStr; dmsRecordAlarmTime.CreateBy = "SlnMesnac"; dmsRecordAlarmTime.CreateTime = DateTime.Now; DmsRecordAlarmInfo dmsRecordAlarmInfo = new DmsRecordAlarmInfo(); dmsRecordAlarmInfo.DeviceId = dmsBaseAlarmRule.DeviceId; dmsRecordAlarmInfo.AlarmRuleId = warnRuleId; dmsRecordAlarmInfo.AlarmBeginTime = DateTime.Now; dmsRecordAlarmInfo.AlarmStatus = "0"; dmsRecordAlarmInfo.NoticeStatus = "0"; dmsRecordAlarmInfo.ContinueTime = dmsBaseAlarmRule.ContinueTime; dmsRecordAlarmInfo.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmInfo.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmInfo.AlarmData = WarnStatusEnum.小包出口读取RFID失败5次.ToString(); dmsRecordAlarmInfo.HandleSuggest = dmsBaseAlarmRule.HandleSuggest + ";;虚拟托盘号为:" + epcStr; dmsRecordAlarmInfo.CreateBy = "SlnMesnac"; dmsRecordAlarmInfo.CreateTime = DateTime.Now; sqlSugarClient.AsTenant().GetConnection("mes").Insertable(dmsRecordAlarmTime).ExecuteCommand(); sqlSugarClient.AsTenant().GetConnection("mes").Insertable(dmsRecordAlarmInfo).ExecuteCommand(); } else { dmsRecordAlarmTime.UpdateTime = DateTime.Now; dmsRecordAlarmTime.UpdateBy = "SlnMesnac"; DmsRecordAlarmInfo dmsRecordAlarmInfo = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); if (dmsRecordAlarmInfo != null) { dmsRecordAlarmInfo.UpdateTime = DateTime.Now; dmsRecordAlarmInfo.UpdateBy = "SlnMesnac"; } sqlSugarClient.AsTenant().GetConnection("mes").Updateable(dmsRecordAlarmTime).ExecuteCommand(); sqlSugarClient.AsTenant().GetConnection("mes").Updateable(dmsRecordAlarmInfo).ExecuteCommand(); } sqlSugarClient.AsTenant().CommitTran(); return epcStr; } catch (Exception ex) { sqlSugarClient.AsTenant().RollbackTran(); _logger.LogError($"NoReadRFIDAlarm异常:{ex.Message}"); return ""; } } /// /// 生成大条码插入mes_barcode_info; /// /// /// /// private string GenerateBigBarcode(MesProductPlanDto productPlanDto, string epcStr) { string bigBarcode = productPlanDto.PlanCode + DateTime.Now.ToString("_MMddHHmmss"); MesBaseBarcodeInfo mesBaseBarcodeInfo = new MesBaseBarcodeInfo(); mesBaseBarcodeInfo.printTime = DateTime.Now; mesBaseBarcodeInfo.printPerson = "SlnMesnac"; mesBaseBarcodeInfo.printFlag = "1"; mesBaseBarcodeInfo.batchFlag = "0"; mesBaseBarcodeInfo.barcodeType = "3"; mesBaseBarcodeInfo.barcodeInfo = bigBarcode; mesBaseBarcodeInfo.batchCode = bigBarcode; mesBaseBarcodeInfo.palletInfoCode = epcStr; mesBaseBarcodeInfo.materialId = productPlanDto.MaterialId; mesBaseBarcodeInfo.amount = 1; mesBaseBarcodeInfo.printNumber = 1; mesBaseBarcodeInfo.productionDate = DateTime.Now; mesBaseBarcodeInfo.acceptedDate = DateTime.Now; mesBaseBarcodeInfo.planCode = productPlanDto.PlanCode; MesProductPlanDetail? mesProductPlanDetail = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x=>x.PlanCode== productPlanDto.PlanCode); if(mesProductPlanDetail != null) { mesBaseBarcodeInfo.planDetailCode = mesProductPlanDetail.PlanDetailCode; } mesBaseBarcodeInfo.safeFlag = productPlanDto.SaleOrderId == 0 ? "1" : "0"; mesBaseBarcodeInfo.saleOrderId = productPlanDto.SaleOrderId; mesBaseBarcodeInfo.saleorderCode = productPlanDto.SaleorderCode; mesBaseBarcodeInfo.bindStatus = "1"; mesBaseBarcodeInfo.bindTime = DateTime.Now; mesBaseBarcodeInfo.updateBy = "SlnMesnac"; mesBaseBarcodeInfo.updateTime = DateTime.Now; mesBaseBarcodeInfo.completeFlag = "1"; mesBaseBarcodeInfo.singleFlag = "0"; var materialInfo = _baseMaterialInfoService.Query(x => x.MaterialId == productPlanDto.MaterialId).FirstOrDefault(); if (materialInfo != null) { mesBaseBarcodeInfo.barcodeSpec = materialInfo.MaterialSpec; } sqlSugarClient.AsTenant().GetConnection("mes").Insertable(mesBaseBarcodeInfo).ExecuteCommand(); return bigBarcode; } /// /// 40个小条码及大条码绑定托盘号,小条码绑定大条码 /// /// /// private void BindingBarCode(string bigBarcode, string epcStr, MesProductPlanDto productPlanDto) { List list = _mesPrdBarCodeService.GetUseBarCodeList(); if (list == null || list.Count == 0) { throw new InvalidOperationException("托盘未找到可以绑定的小条码"); } if (list.Where(x => x.PrintFlag == "1").ToList().Count < 40) { // TODO推送低级别报警 PrintLessThan40Alarm(); } foreach (var item in list) { item.PalletInfoCode = epcStr; item.MesBarcodeInfo = bigBarcode; item.BindTime = DateTime.Now; item.BindStatus = 1; } sqlSugarClient.AsTenant().GetConnection("mes").Updateable(list).ExecuteCommand(); //托盘绑定大条码信息 BasePalletInfo palletInfo = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().Where(x => x.PalletInfoCode == epcStr).First(); if (palletInfo != null) { palletInfo.MaterialBarcode = bigBarcode; palletInfo.MaterialId = productPlanDto.MaterialId; } sqlSugarClient.AsTenant().GetConnection("mes").Updateable(palletInfo).ExecuteCommand(); } private void UpdatePlanInfoAndOrder(MesProductPlanDto productPlanDto) { MesProductPlan productPlan = _mesProductPlanService.Query(x => x.PlanCode == productPlanDto.PlanCode).FirstOrDefault(); if (productPlan == null) { throw new InvalidOperationException("未找到该生产计划!"); } MesProductOrder productOrder = _mesProductOrderService.Query(x => x.ProductOrderId == productPlan.ProductOrderId).FirstOrDefault(); if (productPlan == null) { throw new InvalidOperationException("未找到该生产计划对应的生产工单!"); } #region 更新生产计划 if (productPlan.CompleteAmount == 0) { productPlan.RealBeginTime = DateTime.Now; } productPlan.CompleteAmount++; if (productPlan.CompleteAmount == productPlan.PlanAmount) { productPlan.RealEndTime = DateTime.Now; productPlan.PlanStatus = Model.enums.PlanStatusEnum.已完成; } productPlan.UpdateTime = DateTime.Now; productPlan.UpdateBy = "SlnMesnac"; sqlSugarClient.AsTenant().GetConnection("mes").Updateable(productPlan).ExecuteCommand(); #endregion #region 更新生产工单 if (productOrder.CompleteAmount == 0) { productOrder.RealBeginTime = DateTime.Now; } productOrder.CompleteAmount++; if (productOrder.CompleteAmount == productOrder.PlanAmount) { productOrder.RealEndTime = DateTime.Now; productOrder.OrderStatus = "2"; } productOrder.UpdateTime = DateTime.Now; productOrder.UpdateBy = "SlnMesnac"; sqlSugarClient.AsTenant().GetConnection("mes").Updateable(productOrder).ExecuteCommand(); #endregion } /// /// 小包产出绑定托盘,小包喷码数量小于40低级别报警 /// 返回生产的虚拟托盘号 /// private void PrintLessThan40Alarm() { try { sqlSugarClient.AsTenant().BeginTran(); // 有报警,找出该设备的报警记录,如果没有报警记录或者报警记录已结束,则新增报警记录 int warnRuleId = int.Parse(WarnStatusEnum.托盘绑定时已喷码数不足40.GetDescription()); DmsRecordAlarmTime? dmsRecordAlarmTime = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); if (dmsRecordAlarmTime == null) { DmsBaseAlarmRule dmsBaseAlarmRule = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); dmsRecordAlarmTime = new DmsRecordAlarmTime(); dmsRecordAlarmTime.DeviceId = dmsBaseAlarmRule.DeviceId; dmsRecordAlarmTime.AlarmRuleId = warnRuleId; dmsRecordAlarmTime.AlarmBeginTime = DateTime.Now; dmsRecordAlarmTime.ContinueTime = dmsBaseAlarmRule.ContinueTime; dmsRecordAlarmTime.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmTime.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmTime.AlarmData = WarnStatusEnum.托盘绑定时已喷码数不足40.ToString(); dmsRecordAlarmTime.HandleSuggest = dmsBaseAlarmRule.HandleSuggest; dmsRecordAlarmTime.CreateBy = "SlnMesnac"; dmsRecordAlarmTime.CreateTime = DateTime.Now; DmsRecordAlarmInfo dmsRecordAlarmInfo = new DmsRecordAlarmInfo(); dmsRecordAlarmInfo.DeviceId = dmsBaseAlarmRule.DeviceId; dmsRecordAlarmInfo.AlarmRuleId = warnRuleId; dmsRecordAlarmInfo.AlarmBeginTime = DateTime.Now; dmsRecordAlarmInfo.AlarmStatus = "0"; dmsRecordAlarmInfo.NoticeStatus = "0"; dmsRecordAlarmInfo.ContinueTime = dmsBaseAlarmRule.ContinueTime; dmsRecordAlarmInfo.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmInfo.AlarmReason = dmsBaseAlarmRule.AlarmReason; dmsRecordAlarmInfo.AlarmData = WarnStatusEnum.托盘绑定时已喷码数不足40.ToString(); dmsRecordAlarmInfo.HandleSuggest = dmsBaseAlarmRule.HandleSuggest; dmsRecordAlarmInfo.CreateBy = "SlnMesnac"; dmsRecordAlarmInfo.CreateTime = DateTime.Now; sqlSugarClient.AsTenant().GetConnection("mes").Insertable(dmsRecordAlarmTime).ExecuteCommand(); sqlSugarClient.AsTenant().GetConnection("mes").Insertable(dmsRecordAlarmInfo).ExecuteCommand(); } else { dmsRecordAlarmTime.UpdateTime = DateTime.Now; dmsRecordAlarmTime.UpdateBy = "SlnMesnac"; DmsRecordAlarmInfo dmsRecordAlarmInfo = sqlSugarClient.AsTenant().GetConnection("mes").Queryable().First(x => x.AlarmRuleId == warnRuleId); if (dmsRecordAlarmInfo != null) { dmsRecordAlarmInfo.UpdateTime = DateTime.Now; dmsRecordAlarmInfo.UpdateBy = "SlnMesnac"; } sqlSugarClient.AsTenant().GetConnection("mes").Updateable(dmsRecordAlarmTime).ExecuteCommand(); sqlSugarClient.AsTenant().GetConnection("mes").Updateable(dmsRecordAlarmInfo).ExecuteCommand(); } sqlSugarClient.AsTenant().CommitTran(); } catch (Exception ex) { sqlSugarClient.AsTenant().RollbackTran(); _logger.LogError($"PrintLessThan40Alarm异常:{ex.Message}"); } } /// /// 空托盘进入码垛位,读取RFID备用 /// private async Task EmptyPalletHandle() { //读取PLC空托盘流转信号 while (true) { try { var plc = base.GetPlcByKey("plc"); if (plc == null) { _logger.LogInformation("读取PLC空托盘码垛到位信号,PLC连接信息为空......"); Thread.Sleep(5000); continue; } if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("2楼码垛到位信号"))) { _logger.LogInformation("等待空托盘到位信号触发......"); Thread.Sleep(5000); continue; } RealPalletTask realPalletTask = _realPalletTaskService.Query().FirstOrDefault(); if (realPalletTask != null) { _logger.LogInformation("空托盘到位信号触发,已经有读取托盘,不再读取......"); Thread.Sleep(5000); continue; } //读取RFID、获取当前正在执行的计划、将当前计划、工单等信息与托盘绑定 RefreshMessage("空托盘到位信号触发成功,读取托盘RFID信息"); string epcStr = await ReadEpcStrByRfidKeyAsync("secondFloorPallet"); if (string.IsNullOrEmpty(epcStr)) { _logger.LogError("空托盘到位信号触发,读取RFID失败......"); Thread.Sleep(1000 * 10); continue; } RefreshMessage($"空托盘RFID信息读取成功,标签信息:{epcStr}"); // 插入一条托盘记录 realPalletTask = new RealPalletTask() { PalletCode = epcStr, RecordTime = DateTime.Now, }; _realPalletTaskService.Insert(realPalletTask); } catch (Exception e) { RefreshMessage($"空托盘进入码垛位处理异常:{e.Message}"); } Thread.Sleep(1000 * 10); } } /// /// 根据条码提取序列 /// /// /// /// private void ExtractNumber(string barCode, out int result) { string pattern = @"\b0*(\d{1,6})\b$"; Match match = Regex.Match(barCode, pattern); if (match.Success) { result = Convert.ToInt32(match.Groups[1].Value) + 1; } else { throw new InvalidOperationException($"通过条码:{barCode}提取当前序列异常:未找到匹配的数字"); } } } }