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.Plc; using SlnMesnac.Repository.service; using SlnMesnac.Rfid; 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 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 readonly IMesProductPlanService _mesProductPlanService; private readonly IMesProductOrderService _mesProductOrderService; private readonly IMesPrdBarCodeService _barCodeTaskService; 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) { sqlSugarClient = _sqlSugarClient; _mesProductPlanService = mesProductPlanService; _barCodeTaskService = barCodeTaskService; _realPalletTaskService = serviceProvider.GetRequiredService(); _mesBaseBarcodeInfoService = serviceProvider.GetRequiredService(); _mesPrdBarCodeService = serviceProvider.GetRequiredService(); _mesProductOrderService = serviceProvider.GetRequiredService(); Init(); } private void test() { string epc = "A123456"; _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto); string bigCode = GenerateBigBarcode(productPlanDto, epc); BindingBarCode(bigCode, epc); } public void Init() { Task.Run(() => { EmptyPalletHandle(); }); Task.Run(() => { DeviceOutPutHandle(); }); } /// /// 设备产出逻辑处理 /// 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; } if (plc.readByteByAddress(GetPlcAddressByConfigKey("2楼小包出口到位信号"))!=1) { _logger.LogInformation("等待小包出口信号触发......"); 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) { //TODO 推送报警 _logger.LogError("小包出口到位信号触发,读取RFID失败超过5次......"); } 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); //3.更新mes_product_plan及mes_product_order表,更新已生产数量 UpdatePlanInfoAndOrder(productPlanDto); // 删除正在使用的数据库托盘号 RealPalletTask realPallet = _realPalletTaskService.Query(x => x.PalletCode == epcStr).FirstOrDefault(); if (realPallet != null) { sqlSugarClient.AsTenant().GetConnection("mes").Deleteable(realPallet).ExecuteCommand(); } // 写入PLC RFID号供调度使用 plc.writeStringByAddress(GetPlcAddressByConfigKey("2楼码垛出口RFID条码地址"), epcStr); plc.writeByteByAddress(GetPlcAddressByConfigKey("2楼小包出口到位信号"),2); readRfidAmount = 0; sqlSugarClient.AsTenant().CommitTran(); #endregion } catch (Exception ex) { sqlSugarClient.AsTenant().RollbackTran(); // TODO:推送报警 _logger.LogError($"DeviceOutPutHandle异常:{ex.Message}"); } Thread.Sleep(1000 * 5); } } /// /// 生成大条码插入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; 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"; // 改成spec mesBaseBarcodeInfo.barcodeSpec = productPlanDto.MaterialName; sqlSugarClient.AsTenant().GetConnection("mes").Insertable(mesBaseBarcodeInfo).ExecuteCommand(); return bigBarcode; } /// /// 40个小条码及大条码绑定托盘号,小条码绑定大条码 /// /// /// private void BindingBarCode(string bigBarcode,string epcStr) { List list = _mesPrdBarCodeService.GetUseBarCodeList(); if(list==null || list.Count == 0) { throw new InvalidOperationException("托盘未找到可以绑定的小条码"); } if (list.Where(x => x.PrintFlag == "1").ToList().Count < 40) { // TODO推送低级别报警 } 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(); } 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 } /// /// 空托盘进入码垛位,读取RFID备用 /// private async void 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; } //读取RFID、获取当前正在执行的计划、将当前计划、工单等信息与托盘绑定 RefreshMessage("空托盘到位信号触发成功,读取托盘RFID信息"); string epcStr = await ReadEpcStrByRfidKeyAsync("secondFloorPallet"); if (string.IsNullOrEmpty(epcStr)) { _logger.LogError("空托盘到位信号触发,读取RFID失败......"); Thread.Sleep(1000*10); continue; } RefreshMessage($"空托盘RFID信息读取成功,标签信息:{epcStr}"); // 插入一条托盘记录 RealPalletTask realPalletTask = new RealPalletTask() { PalletCode = epcStr, RecordTime = DateTime.Now, }; bool result = _realPalletTaskService.Insert(realPalletTask); if (result) { plc.writeBoolByAddress(GetPlcAddressByConfigKey("2楼码垛到位信号"), false); } } catch (Exception e) { RefreshMessage($"空托盘进入码垛位处理异常:{e.Message}"); } Thread.Sleep(1000*5); } } /// /// 根据条码提取序列 /// /// /// /// 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}提取当前序列异常:未找到匹配的数字"); } } } }