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.

363 lines
13 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 HslCommunication.Secs.Types;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SlnMesnac.Business.@base;
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Core;
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* 版权所有 (c) 2024 WenJY 保留所有权利。
* CLR版本4.0.30319.42000
* 机器名称LAPTOP-E0N2L34V
* 命名空间SlnMesnac.Business
* 唯一标识655997bf-2bdb-4426-bc49-6fd8def3b395
*
* 创建者WenJY
* 电子邮箱wenjy@mesnac.com
* 创建时间2024-04-07 16:58:59
* 版本V1.0.0
* 描述:
*
*--------------------------------------------------------------------
* 修改人:
* 时间:
* 修改说明:
*
* 版本V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
namespace SlnMesnac.Business
{
/// <summary>
/// 设备生产管理业务类
/// </summary>
public class ProdMgmtBusiness: BaseBusiness
{
public readonly IMesProductPlanService _mesProductPlanService;
public readonly IBasePalletInfoService _basePalletInfoService;
private List<RealPalletTask> _palletTasks;
public ProdMgmtBusiness(ILogger<ProdMgmtBusiness> logger, AppConfig appConfig, List<PlcAbsractFactory> plcFactories, List<RfidAbsractFactory> rfidFactories, IMesProductPlanService mesProductPlanService, IBasePalletInfoService basePalletInfoService, List<RealPalletTask> palletTasks,IServiceProvider serviceProvider) : base(logger, appConfig, plcFactories, rfidFactories, serviceProvider)
{
_mesProductPlanService = mesProductPlanService;
_basePalletInfoService = basePalletInfoService;
_palletTasks = palletTasks;
}
#region 委托事件
/// <summary>
/// 刷新计划列表
/// </summary>
public delegate void RefreshProdPlanList(List<MesProductPlanDto> list);
public event RefreshProdPlanList? RefreshProdPlanListEvent;
/// <summary>
/// 刷新执行计划
/// </summary>
public delegate void RefreshProdPlanExec(MesProductPlan productPlan);
public event RefreshProdPlanExec? RefreshProdPlanExecEvent;
/// <summary>
/// 投料确认,validType确认类型1-读取失败重新读取2-校验失败是否投料
/// </summary>
public delegate bool MatPutInValid(int validType, MesProductPlan productPlan, string materialName, string msg);
public event MatPutInValid? MatPutInValidEvent;
#endregion
public void InitProdPlan()
{
RefreshMesProdList();
Task.Run(() => { ProdPlanExecHandle(); });
Task.Run(() => { MaterialPutInCheck(); });
}
/// <summary>
/// 刷新计划执行列表
/// </summary>
private void RefreshMesProdList()
{
try
{
var info = _mesProductPlanService.GetPlanDtos();
RefreshProdPlanListEvent?.Invoke(info);
}
catch (Exception ex)
{
_logger.LogError($"MES生产计划获取异常{ex.Message}");
}
}
/// <summary>
/// 计划执行逻辑处理
/// </summary>
private void ProdPlanExecHandle()
{
//每5秒执行一次获取是否有已开始的计划获取已开始的计划下发给WCS进行叫料获取货架到位信息进行投料校验持续获取设备要料信号
while (true)
{
try
{
var plc = base.GetPlcByKey("plc");
if (plc == null)
{
throw new ArgumentException($"PLC连接信息为空");
}
MesProductPlan prodPlan = _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto);
if (prodPlan == null || productPlanDto == null)
{
throw new ArgumentException($"未获取到需要执行的生产计划");
}
if (plc.readInt16ByAddress(GetPlcAddressByConfigKey("设备叫料")) != 1)
{
_logger.LogInformation("等待设备叫料信号触发......");
Thread.Sleep(5000);
continue;
}
RefreshMessage("设备要料信号触发成功");
RefreshProdPlanExecEvent?.Invoke(prodPlan);
string palletCode = GetPalletInfoByTask();
RefreshMessage($"执行计划:{prodPlan.PlanCode};计划数量:{Math.Round(prodPlan.PlanAmount,2)};完成数量:{Math.Round(prodPlan.CompleteAmount,2)};发起叫料申请等待AGV送料......");
//进行叫料
if (ApplyDeliveryHandle(productPlanDto.MaterialId.ToString()))
{
RefreshMessage("根据计划自动申请叫料成功");
plc.writeInt16ByAddress(GetPlcAddressByConfigKey("设备叫料"), 0);
}
else
{
RefreshMessage("根据计划自动申请叫料失败");
}
}
catch (Exception e)
{
_logger.LogInformation($"计划执行逻辑处理异常:{e.Message}");
}
Thread.Sleep(5000);
}
}
/// <summary>
/// 投料校验:轮询物料到位信号
/// </summary>
/// <exception cref="ArgumentException"></exception>
public void MaterialPutInCheck()
{
bool result = false;
while (true)
{
//获取当前计划
var prodPlan = _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto);
try
{
var plc = base.GetPlcByKey("plc");
if(plc == null)
{
_logger.LogInformation("读取物料到位信号,PLC连接信息为空......");
Thread.Sleep(3000);
continue;
}
if (plc.readInt16ByAddress(GetPlcAddressByConfigKey("物料到位")) != 1)
{
_logger.LogInformation("等待物料到位信号触发......");
Thread.Sleep(3000);
continue;
}
if (productPlanDto == null || prodPlan == null)
{
RefreshMessage($"收到物料到位信号,未获取到正在执行的生产计划,请开始执行计划");
plc.writeInt16ByAddress(GetPlcAddressByConfigKey("物料到位"), 0);
Thread.Sleep(3000);
continue;
}
plc.writeInt16ByAddress(GetPlcAddressByConfigKey("物料到位"), 0);
RefreshMessage("物料到位信号触发成功,读取托盘RFID信息进行投料校验");
ReadEpcStrByRfidKey("test", out string epcStr);
RefreshMessage($"投料校验RFID标签读取成功标签信息{epcStr}");
//获取物料托盘上的物料信息
BasePalletInfo palletInfo = _basePalletInfoService.GetPalletInfoByPalletCode(epcStr);
if (palletInfo == null)
{
result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, "投料校验失败,未获取到托盘信息,是否继续投料");
}
else
{
if (palletInfo.MaterialId != prodPlan.MaterialId)
{
result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, "投料校验失败,物料信息不匹配,是否继续投料");
}
else
{
result = true;
}
}
if (result)
{
bool isRes = _basePalletInfoService.UnBindPalletInfo(palletInfo);
RefreshMessage($"物料校验通过,托盘信息解绑{(isRes ? "" : "")}");
}
}
catch (Exception e)
{
_logger.LogInformation($"投料校验异常:{e.Message}");
result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, $"投料校验异常:{e.Message},是否继续投料");
}
PutInResutlHandle(prodPlan, result);
Thread.Sleep(3000);
}
}
/// <summary>
/// 投料结果处理
/// </summary>
/// <param name="prodPlan"></param>
/// <param name="isRes"></param>
private void PutInResutlHandle(MesProductPlan prodPlan,bool isRes)
{
try
{
if (isRes)
{
RefreshMessage("投料校验完成,允许投料,更新生产计划");
prodPlan.CompleteAmount += 1;
if (prodPlan.CompleteAmount >= prodPlan.PlanAmount)
{
prodPlan.PlanStatus = PlanStatusEnum.;
}
_mesProductPlanService.Update(prodPlan);
RefreshMesProdList();
}
else
{
RefreshMessage("投料校验失败,人工确认不许投料");
}
}catch(Exception e)
{
_logger.LogInformation($"投料结果处理异常:{e.Message}");
}
}
/// <summary>
/// 投料校验失败,人工确认是否继续投料
/// </summary>
/// <param name="validType"></param>
/// <param name="productPlan"></param>
/// <param name="materialName"></param>
/// <param name="msg"></param>
/// <returns></returns>
private bool MatPutInValidTriggerEvent(int validType, MesProductPlan productPlan, string materialName, string msg)
{
return MatPutInValidEvent.Invoke(validType, productPlan, materialName, msg);
}
/// <summary>
/// 申请叫料
/// </summary>
/// <param name="materialCode"></param>
/// <param name="result"></param>
/// <exception cref="InvalidOperationException"></exception>
public bool ApplyDeliveryHandle(string materialCode)
{
bool result = false;
try
{
var plc = base.GetPlcByKey("plc");
if (plc != null)
{
if (string.IsNullOrEmpty(materialCode))
{
throw new ArgumentException("申请叫料处理异常:物料参数为空");
}
plc.writeInt16ByAddress(base.GetPlcAddressByConfigKey("WCS叫料"), 1);
plc.writeStringByAddress(base.GetPlcAddressByConfigKey("物料型号"), materialCode);
result = true;
}
}
catch (Exception e)
{
throw new InvalidOperationException($"申请叫料处理异常:{e.Message}");
}
return result;
}
/// <summary>
/// 更新生产计划
/// </summary>
/// <param name="planInfo"></param>
public void UpdateProdPlan(MesProductPlanDto planInfo)
{
try
{
if (planInfo == null)
{
throw new ArgumentException("更新生产计划异常:参数为空");
}
var info = _mesProductPlanService.GetProdPlanByPlanCode(planInfo.PlanCode);
if(info == null)
{
throw new ArgumentException("更新生产计划异常:根据计划编号获取计划信息为空");
}
info.PlanStatus = planInfo.PlanStatus;
if (!_mesProductPlanService.UpdateProdPlan(info))
{
throw new InvalidOperationException("生产计划更新失败");
}
RefreshMesProdList();
}
catch (Exception e)
{
throw new InvalidOperationException($"生产计划更新异常:{e.Message}");
}
}
}
}