You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

571 lines
25 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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 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
{
/// <summary>
/// 设备产出业务逻辑
/// </summary>
public class ProdCompletionBusiness : BaseBusiness
{
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<BaseBusiness> logger, AppConfig appConfig, List<PlcAbsractFactory> plcFactories, List<RfidAbsractFactory> rfidFactories, IMesProductPlanService mesProductPlanService, IMesPrdBarCodeService barCodeTaskService, IServiceProvider serviceProvider) : base(logger, appConfig, plcFactories, rfidFactories, serviceProvider)
{
sqlSugarClient = _sqlSugarClient;
_mesProductPlanService = mesProductPlanService;
_barCodeTaskService = barCodeTaskService;
_realPalletTaskService = serviceProvider.GetRequiredService<IRealPalletTaskService>();
_mesBaseBarcodeInfoService = serviceProvider.GetRequiredService<IMesBaseBarcodeInfoService>();
_mesPrdBarCodeService = serviceProvider.GetRequiredService<IMesPrdBarCodeService>();
_mesProductOrderService = serviceProvider.GetRequiredService<IMesProductOrderService>();
_baseMaterialInfoService = serviceProvider.GetRequiredService<IBaseMaterialInfoService>();
Init();
}
private void test()
{
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();
});
}
/// <summary>
/// 设备产出逻辑处理
/// </summary>
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 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);
//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();
_logger.LogError($"DeviceOutPutHandle异常:{ex.Message}");
}
Thread.Sleep(1000 * 5);
}
}
/// <summary>
/// 推送报警,并生成虚拟托盘号暂时使用,虚拟托盘号储存到报警信息,人工处理可以将虚拟托盘号替换为正常托盘号
/// <return> 返回生产的虚拟托盘号
/// </summary>
private string NoReadRFIDAlarm()
{
string epcStr;
try
{
epcStr = "TEMP" + DateTime.Now.ToString("MMddHHmm");
sqlSugarClient.AsTenant().BeginTran();
// 有报警,找出该设备的报警记录,如果没有报警记录或者报警记录已结束,则新增报警记录
int warnRuleId = int.Parse(WarnStatusEnum.RFID5.GetDescription());
DmsRecordAlarmTime? dmsRecordAlarmTime = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<DmsRecordAlarmTime>().First(x => x.AlarmRuleId == warnRuleId);
if (dmsRecordAlarmTime == null)
{
DmsBaseAlarmRule dmsBaseAlarmRule = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<DmsBaseAlarmRule>().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.RFID5.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.RFID5.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<DmsRecordAlarmInfo>().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 "";
}
}
/// <summary>
/// 生成大条码插入mes_barcode_info
/// </summary>
/// <param name="productPlanDto"></param>
/// <param name="epcStr"></param>
/// <returns></returns>
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";
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;
}
/// <summary>
/// 40个小条码及大条码绑定托盘号小条码绑定大条码
/// </summary>
/// <param name="bigBarcode"></param>
/// <param name="epcStr"></param>
private void BindingBarCode(string bigBarcode, string epcStr)
{
List<MesPrdBarcodeInfo> 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();
}
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
}
/// <summary>
/// 小包产出绑定托盘小包喷码数量小于40低级别报警
/// <return> 返回生产的虚拟托盘号
/// </summary>
private void PrintLessThan40Alarm()
{
try
{
sqlSugarClient.AsTenant().BeginTran();
// 有报警,找出该设备的报警记录,如果没有报警记录或者报警记录已结束,则新增报警记录
int warnRuleId = int.Parse(WarnStatusEnum.40.GetDescription());
DmsRecordAlarmTime? dmsRecordAlarmTime = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<DmsRecordAlarmTime>().First(x => x.AlarmRuleId == warnRuleId);
if (dmsRecordAlarmTime == null)
{
DmsBaseAlarmRule dmsBaseAlarmRule = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<DmsBaseAlarmRule>().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<DmsRecordAlarmInfo>().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}");
}
}
/// <summary>
/// 空托盘进入码垛位,读取RFID备用
/// </summary>
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);
}
}
/// <summary>
/// 根据条码提取序列
/// </summary>
/// <param name="barCode"></param>
/// <param name="result"></param>
/// <exception cref="InvalidOperationException"></exception>
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}提取当前序列异常:未找到匹配的数字");
}
}
}
}