|
|
@@ -0,0 +1,1239 @@
|
|
|
+using AutoMapper;
|
|
|
+using Microsoft.Extensions.Logging;
|
|
|
+using Newtonsoft.Json;
|
|
|
+using SqlSugar;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using WCS.Entity.sx;
|
|
|
+using wms.dto;
|
|
|
+using wms.dto.request;
|
|
|
+using wms.dto.request.share;
|
|
|
+using wms.dto.request.sx;
|
|
|
+using wms.dto.response;
|
|
|
+using wms.dto.response.sx;
|
|
|
+using wms.service.Help.LayerPacking;
|
|
|
+using wms.service.Help.LayerPacking.model;
|
|
|
+using wms.service.Service;
|
|
|
+using wms.sqlsugar;
|
|
|
+using wms.sqlsugar.model.sx;
|
|
|
+using wms.util.Ext;
|
|
|
+using wms.util.Http;
|
|
|
+using TaskStatus = wms.dto.TaskStatus;
|
|
|
+
|
|
|
+namespace wms.service.Help.Packing
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// 层配装箱帮助类
|
|
|
+ /// </summary>
|
|
|
+ public class SxLayerPackingHelp
|
|
|
+ {
|
|
|
+ private readonly Repository<BaseWarecell> _wareCell;
|
|
|
+ private readonly Repository<BillInvnow> _invNow;
|
|
|
+ private readonly Repository<BillPboxrule> _boxRule;
|
|
|
+ private readonly Repository<sxSysConfig> _sysconfing;
|
|
|
+ private readonly RepositoryTask<BillTimeOutRecord> _timeoutrecord;
|
|
|
+ private readonly ILogger<SXService> _logger;
|
|
|
+ private readonly Repository<PalletLayerMath> _palletLayerMath;
|
|
|
+ private readonly Repository<BillInvflow> _invFlow;
|
|
|
+ private readonly RepositoryTask<WCS_TaskOld> _wcsTaskOld;
|
|
|
+ private readonly Repository<BaseWarehouse> _wareHouse;
|
|
|
+ private readonly RepositoryTask<WCS_TaskDtl> _taskDetail;
|
|
|
+ private readonly RepositoryTask<WCS_TaskInfo> _task;
|
|
|
+ private readonly Repository<Palletizing> _palletiz;
|
|
|
+ private readonly ITenant _db;
|
|
|
+ private readonly IMapper _mapper;
|
|
|
+ private readonly object _lockerPalletizingPackTask;
|
|
|
+ private readonly object _lockerApplyLoc;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 构造函数
|
|
|
+ /// </summary>
|
|
|
+ public SxLayerPackingHelp(
|
|
|
+ Repository<BaseWarecell> wareCell,
|
|
|
+ Repository<BillInvnow> invNow,
|
|
|
+ Repository<BillPboxrule> boxRule,
|
|
|
+ Repository<sxSysConfig> sysconfing,
|
|
|
+ RepositoryTask<BillTimeOutRecord> timeoutrecord,
|
|
|
+ ILogger<SXService> logger,
|
|
|
+ Repository<PalletLayerMath> palletLayerMath,
|
|
|
+ Repository<BillInvflow> invFlow,
|
|
|
+ RepositoryTask<WCS_TaskOld> wcsTaskOld,
|
|
|
+ Repository<BaseWarehouse> wareHouse,
|
|
|
+ RepositoryTask<WCS_TaskDtl> taskDetail,
|
|
|
+ RepositoryTask<WCS_TaskInfo> task,
|
|
|
+ Repository<Palletizing> palletiz,
|
|
|
+ ITenant db,
|
|
|
+ IMapper mapper,
|
|
|
+ object lockerPalletizingPackTask,
|
|
|
+ object lockerApplyLoc)
|
|
|
+ {
|
|
|
+ _wareCell = wareCell;
|
|
|
+ _invNow = invNow;
|
|
|
+ _boxRule = boxRule;
|
|
|
+ _sysconfing = sysconfing;
|
|
|
+ _timeoutrecord = timeoutrecord;
|
|
|
+ _logger = logger;
|
|
|
+ _palletLayerMath = palletLayerMath;
|
|
|
+ _invFlow = invFlow;
|
|
|
+ _wcsTaskOld = wcsTaskOld;
|
|
|
+ _wareHouse = wareHouse;
|
|
|
+ _taskDetail = taskDetail;
|
|
|
+ _task = task;
|
|
|
+ _palletiz = palletiz;
|
|
|
+ _db = db;
|
|
|
+ _mapper = mapper;
|
|
|
+ _lockerPalletizingPackTask = lockerPalletizingPackTask;
|
|
|
+ _lockerApplyLoc = lockerApplyLoc;
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 获取可用库存
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取可用库存
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ public IEnumerable<LayerPackingProduct> GetAvailableInventory()
|
|
|
+ {
|
|
|
+ int timeOutHoldHours = 72;
|
|
|
+ var timeOut = _sysconfing.GetFirst(p => p.Code == "CP_TimeOutHoldHours");
|
|
|
+ if (timeOut != null) timeOutHoldHours = int.Parse(timeOut.SContent);
|
|
|
+
|
|
|
+ var normalInventory = GetNormalInventory(timeOutHoldHours);
|
|
|
+ var reworkInventory = GetReworkInventory(timeOutHoldHours);
|
|
|
+ var torsInventory = GetTorsInventory(timeOutHoldHours);
|
|
|
+ var torsReworkInventory = GetTorsReworkInventory(timeOutHoldHours);
|
|
|
+
|
|
|
+ var allInventory = normalInventory
|
|
|
+ .Union(reworkInventory)
|
|
|
+ .Union(torsInventory)
|
|
|
+ .Union(torsReworkInventory)
|
|
|
+ .Distinct(new CompareProduct());
|
|
|
+
|
|
|
+ return FilterByTunnelRestrictions(allInventory);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取非检测非重绕盘
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="timeOutHoldHours">超时小时</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IEnumerable<LayerPackingProduct> GetNormalInventory(int timeOutHoldHours)
|
|
|
+ {
|
|
|
+ return from loc in _wareCell.GetList(p =>
|
|
|
+ p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
|
|
|
+ p.StateNum == LocationState.LocationState_Full)
|
|
|
+ join stock in _invNow.GetList(p =>
|
|
|
+ p.InvStateCode == InvState.InvEcecState_In.ToString() &&
|
|
|
+ p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
|
|
|
+ p.Grade == "A" &&
|
|
|
+ !p.IsControlpanel &&
|
|
|
+ !p.IsTorsChk &&
|
|
|
+ string.IsNullOrEmpty(p.PreStock) &&
|
|
|
+ p.ContUsageQty <= 0 &&
|
|
|
+ !p.ProductMachCode.Contains("R") &&
|
|
|
+ (DateTime.Now - p.ProductTime).TotalHours < timeOutHoldHours)
|
|
|
+ on loc.ContGrpBarCode equals stock.ContGrpBarCode
|
|
|
+ join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
|
|
|
+ on stock.InDocsNo equals rule.DocsNo
|
|
|
+ select CreateStockTemp(loc, stock, rule.SkuCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取非检测重绕盘
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="timeOutHoldHours">超时小时</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IEnumerable<LayerPackingProduct> GetReworkInventory(int timeOutHoldHours)
|
|
|
+ {
|
|
|
+ return from loc in _wareCell.GetList(p =>
|
|
|
+ p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
|
|
|
+ p.StateNum == LocationState.LocationState_Full)
|
|
|
+ join stock in _invNow.GetList(p =>
|
|
|
+ p.InvStateCode == InvState.InvEcecState_In.ToString() &&
|
|
|
+ p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
|
|
|
+ p.Grade == "A" &&
|
|
|
+ !p.IsControlpanel &&
|
|
|
+ !p.IsTorsChk &&
|
|
|
+ string.IsNullOrEmpty(p.PreStock) &&
|
|
|
+ p.ContUsageQty <= 0 &&
|
|
|
+ p.ProductMachCode.Contains("R") &&
|
|
|
+ (DateTime.Now - p.OneInTime).TotalHours < timeOutHoldHours)
|
|
|
+ on loc.ContGrpBarCode equals stock.ContGrpBarCode
|
|
|
+ join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
|
|
|
+ on stock.InDocsNo equals rule.DocsNo
|
|
|
+ select CreateStockTemp(loc, stock, rule.SkuCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取已检测非重绕盘
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="timeOutHoldHours">超时小时</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IEnumerable<LayerPackingProduct> GetTorsInventory(int timeOutHoldHours)
|
|
|
+ {
|
|
|
+ return from loc in _wareCell.GetList(p =>
|
|
|
+ p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
|
|
|
+ p.StateNum == LocationState.LocationState_Full)
|
|
|
+ join stock in _invNow.GetList(p =>
|
|
|
+ p.InvStateCode == InvState.InvEcecState_In.ToString() &&
|
|
|
+ p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
|
|
|
+ p.Grade == "A" &&
|
|
|
+ p.IsControlpanel &&
|
|
|
+ p.IsTorsChk &&
|
|
|
+ p.TorsChkQty > 0 &&
|
|
|
+ string.IsNullOrEmpty(p.PreStock) &&
|
|
|
+ !p.ProductMachCode.Contains("R") &&
|
|
|
+ p.ContUsageQty <= 0 &&
|
|
|
+ (DateTime.Now - p.ProductTime).TotalHours < timeOutHoldHours)
|
|
|
+ on loc.ContGrpBarCode equals stock.ContGrpBarCode
|
|
|
+ join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
|
|
|
+ on stock.InDocsNo equals rule.DocsNo
|
|
|
+ select CreateStockTemp(loc, stock, rule.SkuCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取已检测重绕盘
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="timeOutHoldHours">超时小时</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IEnumerable<LayerPackingProduct> GetTorsReworkInventory(int timeOutHoldHours)
|
|
|
+ {
|
|
|
+ return from loc in _wareCell.GetList(p =>
|
|
|
+ p.IsStop == LocationStop.LocationInvoke.GetHashCode() &&
|
|
|
+ p.StateNum == LocationState.LocationState_Full)
|
|
|
+ join stock in _invNow.GetList(p =>
|
|
|
+ p.InvStateCode == InvState.InvEcecState_In.ToString() &&
|
|
|
+ p.ExecStateCode == InvLockState.InvState_Normal.ToString() &&
|
|
|
+ p.Grade == "A" &&
|
|
|
+ p.IsControlpanel &&
|
|
|
+ p.IsTorsChk &&
|
|
|
+ p.TorsChkQty > 0 &&
|
|
|
+ string.IsNullOrEmpty(p.PreStock) &&
|
|
|
+ p.ProductMachCode.Contains("R") &&
|
|
|
+ p.ContUsageQty <= 0 &&
|
|
|
+ (DateTime.Now - p.OneInTime).TotalHours < timeOutHoldHours)
|
|
|
+ on loc.ContGrpBarCode equals stock.ContGrpBarCode
|
|
|
+ join rule in _boxRule.AsQueryable().Where(p => p.PackRule == "CP").Select(p => new { p.DocsNo, p.SkuCode }).Distinct().ToList()
|
|
|
+ on stock.InDocsNo equals rule.DocsNo
|
|
|
+ select CreateStockTemp(loc, stock, rule.SkuCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 创建库存临时对象
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="loc">货位信息</param>
|
|
|
+ /// <param name="stock">库存信息</param>
|
|
|
+ /// <param name="skuCode">sku</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private LayerPackingProduct CreateStockTemp(BaseWarecell loc, BillInvnow stock, string skuCode)
|
|
|
+ {
|
|
|
+ return new LayerPackingProduct
|
|
|
+ {
|
|
|
+ MatCode = stock.MatCode,
|
|
|
+ InvBarCode = stock.InvBarCode,
|
|
|
+ Grade = stock.Grade,
|
|
|
+ InvStateCode = stock.InvStateCode,
|
|
|
+ ProductTime = stock.ProductTime,
|
|
|
+ WbGroupCode = stock.WbGroupCode,
|
|
|
+ IsTorsChk = stock.IsTorsChk,
|
|
|
+ TorsChkQty = stock.TorsChkQty,
|
|
|
+ TorsChkValue = stock.TorsChkValue == null ? 0 : (decimal)stock.TorsChkValue,
|
|
|
+ HoldTime = stock.HoldTime,
|
|
|
+ ProductMachCode = stock.ProductMachCode,
|
|
|
+ IsControlpanel = stock.IsControlpanel,
|
|
|
+ HWTypeCode = stock.HWTypeCode,
|
|
|
+ SolderCount = stock.SolderCount,
|
|
|
+ IsRework = stock.IsRework,
|
|
|
+ IsBlack = stock.IsBlack,
|
|
|
+ Col = loc.Col,
|
|
|
+ Layer = loc.Layer,
|
|
|
+ Shelf = loc.Shelf,
|
|
|
+ Depth = loc.Depth,
|
|
|
+ Code = loc.Code,
|
|
|
+ Tunnel = loc.Tunnel,
|
|
|
+ SCRel = loc.SCRel,
|
|
|
+ Floor = loc.Floor,
|
|
|
+ WarehouseCode = loc.WarehouseCode,
|
|
|
+ ContGrpBarCode = loc.ContGrpBarCode,
|
|
|
+ ContGrpId = loc.ContGrpId,
|
|
|
+ Id = loc.Id,
|
|
|
+ StateNum = loc.StateNum,
|
|
|
+ SkuCode = skuCode,
|
|
|
+ Wind = stock.Wind,
|
|
|
+ InDocsNo = stock.InDocsNo,
|
|
|
+ BatchNo = stock.BatchNo
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 过滤禁用巷道
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="inventory">可用产品</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public IEnumerable<LayerPackingProduct> FilterByTunnelRestrictions(IEnumerable<LayerPackingProduct> inventory)
|
|
|
+ {
|
|
|
+ var tunnelRestrictions = _sysconfing.GetList(p =>
|
|
|
+ p.SType == "OutStop" && !string.IsNullOrEmpty(p.SContent));
|
|
|
+
|
|
|
+ if (!tunnelRestrictions.Any())
|
|
|
+ {
|
|
|
+ return inventory;
|
|
|
+ }
|
|
|
+
|
|
|
+ var filteredInventory = inventory.ToList();
|
|
|
+
|
|
|
+ // 按楼层过滤禁用巷道
|
|
|
+ for (int floor = 1; floor <= 3; floor++)
|
|
|
+ {
|
|
|
+ var floorRestrictions = tunnelRestrictions
|
|
|
+ .Where(p => p.Default2 == floor.ToString())
|
|
|
+ .Select(x => x.Default1)
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ if (floorRestrictions.Any())
|
|
|
+ {
|
|
|
+ var restrictedContainers = filteredInventory
|
|
|
+ .Where(p => p.Floor == floor &&
|
|
|
+ floorRestrictions.Contains(p.Tunnel.ToString()))
|
|
|
+ .Select(p => p.ContGrpBarCode)
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ filteredInventory = filteredInventory
|
|
|
+ .Where(p => !restrictedContainers.Contains(p.ContGrpBarCode))
|
|
|
+ .ToList();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return filteredInventory;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 获取可用库存
|
|
|
+
|
|
|
+ #region 装箱逻辑
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 按SKU执行装箱
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="skuGroup"></param>
|
|
|
+ /// <param name="boxRules"></param>
|
|
|
+ /// <param name="inventory"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public SRes ProcessSkuGroup(
|
|
|
+ IGrouping<string, LayerPackingProduct> skuGroup,
|
|
|
+ List<BillPboxrule> boxRules,
|
|
|
+ IEnumerable<LayerPackingProduct> inventory)
|
|
|
+ {
|
|
|
+ var res = new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode() };
|
|
|
+ var skuCode = skuGroup.Key;
|
|
|
+ var skuBoxRules = boxRules.Where(p => p.SkuCode == skuCode).OrderBy(p => p.BatchNo).ThenBy(p => p.PBoxCode);
|
|
|
+
|
|
|
+ if (!skuBoxRules.Any())
|
|
|
+ {
|
|
|
+ return HandleNoBoxRuleForSku(skuCode, inventory);
|
|
|
+ }
|
|
|
+
|
|
|
+ var ruleGroups = skuBoxRules.GroupBy(p => new { p.Wind, p.SpoolType, p.FullCountQty });
|
|
|
+
|
|
|
+ foreach (var ruleGroup in ruleGroups)
|
|
|
+ {
|
|
|
+ var result = ProcessRuleGroup(ruleGroup, inventory, skuCode);
|
|
|
+ if (result.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
|
|
|
+ {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ res.ResMsg += $"[{ruleGroup.Key}:{result.ResMsg}]";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理装箱规则组
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="ruleGroup">箱号</param>
|
|
|
+ /// <param name="inventory">产品</param>
|
|
|
+ /// <param name="skuCode">sku</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public SRes ProcessRuleGroup(
|
|
|
+ IGrouping<dynamic, BillPboxrule> ruleGroup,
|
|
|
+ IEnumerable<LayerPackingProduct> inventory,
|
|
|
+ string skuCode)
|
|
|
+ {
|
|
|
+ var rule = ruleGroup.OrderBy(p => p.BatchNo).ThenBy(p => p.PBoxCode).FirstOrDefault();
|
|
|
+ if (rule == null) return new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode() };
|
|
|
+
|
|
|
+ return ProcessSpecialSku(rule, inventory);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理特殊SKU --指不管控机器组
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="skuCode">sku</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private bool IsSpecialSku(string skuCode)
|
|
|
+ {
|
|
|
+ return skuCode == "6210010401" || skuCode == "6210120101";
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理不需要管控机器组的SKU
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="rule"></param>
|
|
|
+ /// <param name="inventory"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public SRes ProcessSpecialSku(
|
|
|
+ BillPboxrule rule,
|
|
|
+ IEnumerable<LayerPackingProduct> inventory)
|
|
|
+ {
|
|
|
+ var res = new SRes();
|
|
|
+
|
|
|
+ // 1. 分配库存
|
|
|
+ var allocationResult = AllocateStock(rule, inventory, isSpecialSku: true);
|
|
|
+
|
|
|
+ if (!allocationResult.IsSuccess)
|
|
|
+ {
|
|
|
+ _logger.LogError($"特殊SKU库存分配失败: {allocationResult.ErrorMessage}");
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = allocationResult.ErrorMessage;
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. MES验证
|
|
|
+ var mesResult = VerifyBoxWithMes(rule.PBoxCode, rule.BatchNo);
|
|
|
+
|
|
|
+ if (!mesResult.IsSuccess)
|
|
|
+ {
|
|
|
+ UpdateBoxStatus(rule, 2, mesResult.Message);
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.ErrParam.GetHashCode();
|
|
|
+ res.ResMsg = $"MES验证失败: {mesResult.Message}";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 预占库存
|
|
|
+ PreOccupyInventory(rule, allocationResult.BoxInfo);
|
|
|
+
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 装箱
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="rule">箱号</param>
|
|
|
+ /// <param name="availableStock">有效库存</param>
|
|
|
+ /// <param name="isSpecialSku">是否控制机器组</param>
|
|
|
+ /// <param name="skuGroup">对应的分组sku</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public StockAllocationResult AllocateStock(
|
|
|
+ BillPboxrule rule,
|
|
|
+ IEnumerable<LayerPackingProduct> availableStock,
|
|
|
+ bool isSpecialSku = false,
|
|
|
+ IGrouping<string, LayerPackingProduct>? skuGroup = null)
|
|
|
+ {
|
|
|
+ var result = new StockAllocationResult();
|
|
|
+
|
|
|
+ // 过滤符合条件的库存
|
|
|
+ var filteredStock = FilterStock(availableStock, rule, isSpecialSku, skuGroup).ToList();
|
|
|
+
|
|
|
+ if (!filteredStock.Any())
|
|
|
+ {
|
|
|
+ result.IsSuccess = false;
|
|
|
+ result.ErrorMessage = "没有符合条件的库存";
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ //构建初始装箱参数
|
|
|
+ var constraints = new LayerPackingConstraints
|
|
|
+ {
|
|
|
+ P = rule.TorsChkValue,
|
|
|
+ P1 = rule.TorsChkValueRange,
|
|
|
+ SolderMaxCount = rule.SolderMaxCount,
|
|
|
+ PerSolderMaxCount = rule.PerSolderMaxCount,
|
|
|
+ ProductsPerBox = rule.FullCountQty
|
|
|
+ };
|
|
|
+ var config = _sysconfing.GetFirst(x => x.Code == "CP_RelaxationRatio");
|
|
|
+ if (config != null)
|
|
|
+ {
|
|
|
+ constraints.BR = decimal.Parse(config.SContent);
|
|
|
+ constraints.LR = decimal.Parse(config.Default1);
|
|
|
+ }
|
|
|
+
|
|
|
+ constraints.UpdateTargetValues(filteredStock);
|
|
|
+ //开始装箱
|
|
|
+ var layerPacking = new LayerPackingService(filteredStock, constraints);
|
|
|
+ var layerPackingResult = layerPacking.PackBoxe();
|
|
|
+
|
|
|
+ //装箱结果为空
|
|
|
+ if (layerPackingResult.Boxes.Count < 1)
|
|
|
+ {
|
|
|
+ result.IsSuccess = false;
|
|
|
+ result.ErrorMessage = JsonConvert.SerializeObject(layerPackingResult.FailureReasons);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!layerPackingResult.IsSuccess)
|
|
|
+ {
|
|
|
+ result.ErrorMessage = JsonConvert.SerializeObject(layerPackingResult.FailureReasons);
|
|
|
+ }
|
|
|
+
|
|
|
+ result.IsSuccess = true;
|
|
|
+ result.BoxInfo = layerPackingResult.Boxes.First();
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 过滤库存
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="stock">产品</param>
|
|
|
+ /// <param name="rule">装箱规则</param>
|
|
|
+ /// <param name="isSpecialSku">是否管控机器组</param>
|
|
|
+ /// <param name="skuGroup">管控机器组时用于过滤的机器组信息</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private IEnumerable<LayerPackingProduct> FilterStock(
|
|
|
+ IEnumerable<LayerPackingProduct> stock,
|
|
|
+ BillPboxrule rule,
|
|
|
+ bool isSpecialSku,
|
|
|
+ IGrouping<string, LayerPackingProduct>? skuGroup = null)
|
|
|
+ {
|
|
|
+ var filtered = stock.Where(p => p.SkuCode == rule.SkuCode);
|
|
|
+
|
|
|
+ if (!isSpecialSku && skuGroup != null)
|
|
|
+ {
|
|
|
+ filtered = filtered.Where(p => p.WbGroupCode == skuGroup.Key);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(rule.SpoolType))
|
|
|
+ {
|
|
|
+ filtered = filtered.Where(p => p.HWTypeCode == rule.SpoolType);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(rule.Wind))
|
|
|
+ {
|
|
|
+ filtered = filtered.Where(p => p.Wind == rule.Wind);
|
|
|
+ }
|
|
|
+
|
|
|
+ return filtered;
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 箱号验证
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 箱号验证
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="boxCode">待验证的箱号</param>
|
|
|
+ /// <param name="batchNo">批号</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public MesVerificationResult VerifyBoxWithMes(string boxCode, string batchNo)
|
|
|
+ {
|
|
|
+ var result = new MesVerificationResult();
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var boxVerifyRequest = new MesBoxVerify
|
|
|
+ {
|
|
|
+ HuNr = boxCode,
|
|
|
+ Batch = batchNo
|
|
|
+ };
|
|
|
+
|
|
|
+ var requestId = Guid.NewGuid().ToString();
|
|
|
+ var esbRequest = CreateEsbRequest(requestId);
|
|
|
+ var url = wms.util.AppSettings.GetConfig("EsbMesPushUrl");
|
|
|
+ var response = SendMesRequest(boxVerifyRequest, requestId, esbRequest, url);
|
|
|
+ var mesResponse = JsonConvert.DeserializeObject<TorschMesResponse>(response);
|
|
|
+
|
|
|
+ _logger.LogInformation($"MES箱号验证返回: {response}");
|
|
|
+
|
|
|
+ result.IsSuccess = mesResponse.success;
|
|
|
+ result.Message = mesResponse.message;
|
|
|
+ result.RawResponse = response;
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError($"MES验证异常: {ex}");
|
|
|
+ result.IsSuccess = false;
|
|
|
+ result.Message = "MES验证异常: " + ex.Message;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 创建ESB请求
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="requestId">请求ID</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private EsbReq CreateEsbRequest(string requestId)
|
|
|
+ {
|
|
|
+ return new EsbReq
|
|
|
+ {
|
|
|
+ headers = new HeadersReq
|
|
|
+ {
|
|
|
+ serviceCode = "163K162003",
|
|
|
+ requestId = requestId,
|
|
|
+ TrackId = requestId,
|
|
|
+ sourceCode = "163K"
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 发送MES请求
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="request">请求参数</param>
|
|
|
+ /// <param name="requestId">请求ID</param>
|
|
|
+ /// <param name="esbRequest">esb请求信息</param>
|
|
|
+ /// <param name="_mesUrl">esb地址</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private string SendMesRequest(MesBoxVerify request, string requestId, EsbReq esbRequest, string _mesUrl)
|
|
|
+ {
|
|
|
+ return HttpUtil.PostRequest(
|
|
|
+ _mesUrl,
|
|
|
+ JsonConvert.SerializeObject(request),
|
|
|
+ 30000,
|
|
|
+ "UTF-8",
|
|
|
+ "application/json",
|
|
|
+ requestId,
|
|
|
+ esbRequest.headers.sourceCode,
|
|
|
+ esbRequest.headers.serviceCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 箱号验证
|
|
|
+
|
|
|
+ #region 更新装箱/库存信息
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 预占用库存
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="rule">箱号</param>
|
|
|
+ /// <param name="boxInfo">产品信息</param>
|
|
|
+ public SRes PreOccupyInventory(
|
|
|
+ BillPboxrule rule,
|
|
|
+ LayerPackingBoxInfo boxInfo)
|
|
|
+ {
|
|
|
+ var res = new SRes() { ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode(), ResMsg = ResponseStatusCodeEnum.Sucess.GetDescription() };
|
|
|
+ if (boxInfo.Layers.SelectMany(x=>x.Products).Count() != rule.FullCountQty)
|
|
|
+ {
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = "成箱产品数与满箱产品数不符";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _db.BeginTran();
|
|
|
+
|
|
|
+ // 更新箱号状态为预占用
|
|
|
+ UpdateBoxStatus(rule, 3, "箱号预占用");
|
|
|
+
|
|
|
+ // 更新库存预占用标记
|
|
|
+ var presign = rule.Id.ToString();
|
|
|
+ var layerNumber = 0;
|
|
|
+ foreach (var layer in boxInfo.Layers.OrderByDescending(x => x.TotalTorsion))
|
|
|
+ {
|
|
|
+ var layerProductsIds = layer.Products.Select(x => x.ContGrpId).ToList();
|
|
|
+ _invNow.UpdateSetColumnsTrue(p => new BillInvnow
|
|
|
+ {
|
|
|
+ PreStock = presign,
|
|
|
+ EditTime = DateTime.Now,
|
|
|
+ PackingLayer = layerNumber,
|
|
|
+ Memo = $"箱号预占用:{rule.PBoxCode}"
|
|
|
+ }, p => layerProductsIds.Contains(p.ContGrpId));
|
|
|
+ layerNumber++;
|
|
|
+ }
|
|
|
+
|
|
|
+ var containerIds = boxInfo.TotalProduct.Select(x => x.ContGrpId).ToList();
|
|
|
+ _db.CommitTran();
|
|
|
+ _logger.LogInformation($"{rule.PBoxCode}预占:{JsonConvert.SerializeObject(containerIds)}");
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError($"箱号预占用异常: {ex}");
|
|
|
+ _db.RollbackTran();
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = ex.Message;
|
|
|
+ }
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新箱号状态
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="rule">箱号</param>
|
|
|
+ /// <param name="status">状态</param>
|
|
|
+ /// <param name="memo">备注</param>
|
|
|
+ public void UpdateBoxStatus(BillPboxrule rule, int status, string memo)
|
|
|
+ {
|
|
|
+ _boxRule.UpdateSetColumnsTrue(p => new BillPboxrule
|
|
|
+ {
|
|
|
+ ZXStateCode = status,
|
|
|
+ EditTime = DateTime.Now,
|
|
|
+ Memo = memo
|
|
|
+ }, p => p.Id == rule.Id);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 更新装箱/库存信息
|
|
|
+
|
|
|
+ #endregion 装箱逻辑
|
|
|
+
|
|
|
+ #region 处理无箱号可用的情况
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理没有箱号的情况
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="inventory">产品信息</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public SRes HandleNoBoxRuleError(IEnumerable<LayerPackingProduct> inventory)
|
|
|
+ {
|
|
|
+ string msg = "SPC" + ResponseStatusCodeEnum.NotBoxRule.GetDescription();
|
|
|
+ RedisHelper.Set("Sx:WMSErrorInfo:NoControlOut8",
|
|
|
+ new RedisErrorInfo { Equip = "NoControlOut8", Con = msg, Time = DateTime.Now });
|
|
|
+ _logger.LogInformation("非控制不满足装箱:" + msg);
|
|
|
+
|
|
|
+ var docNos = inventory.GroupBy(v => v.InDocsNo).Select(p => p.Key).Distinct().ToList();
|
|
|
+ SxPackingHelp.WreTimeOutRecord(inventory.Select(p => p.ContGrpBarCode).ToList(), $"没有箱号可以使用,单号:{JsonConvert.SerializeObject(docNos)}", _timeoutrecord, _logger);
|
|
|
+
|
|
|
+ return new SRes
|
|
|
+ {
|
|
|
+ ResCode = ResponseStatusCodeEnum.NotBoxRule.GetHashCode(),
|
|
|
+ ResMsg = msg
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理没有箱号可用的情况 --SKU
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="skuCode">SKU</param>
|
|
|
+ /// <param name="inventory">产品信息</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public SRes HandleNoBoxRuleForSku(string skuCode, IEnumerable<LayerPackingProduct> inventory)
|
|
|
+ {
|
|
|
+ var skuInventory = inventory.Where(p => p.SkuCode == skuCode);
|
|
|
+ var docNos = inventory.GroupBy(v => v.InDocsNo).Select(p => p.Key).Distinct().ToList();
|
|
|
+ string msg = $"没有箱号可以使用,SKU:{skuCode},单号:{JsonConvert.SerializeObject(docNos)}";
|
|
|
+ RedisHelper.Set("Sx:WMSErrorInfo:NoControlOut9", new RedisErrorInfo { Equip = "NoControlOut9", Con = msg, Time = DateTime.Now });
|
|
|
+ _logger.LogInformation($"非控制不满足装箱:{msg}");
|
|
|
+ SxPackingHelp.WreTimeOutRecord(skuInventory.Select(p => p.ContGrpBarCode).ToList(), $"没有箱号可以使用,SKU:{skuCode},单号:{JsonConvert.SerializeObject(docNos)}", _timeoutrecord, _logger);
|
|
|
+
|
|
|
+ return new SRes { ResCode = ResponseStatusCodeEnum.Fail.GetHashCode(), ResMsg = msg };
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 处理无箱号可用的情况
|
|
|
+
|
|
|
+ #region 下发装箱任务及缓存托盘
|
|
|
+
|
|
|
+ #region 处理正在码垛的任务
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 处理正在码垛的任务
|
|
|
+ /// </summary>
|
|
|
+ public SRes ProcessActivePalletizingTasks()
|
|
|
+ {
|
|
|
+ var res = new SRes();
|
|
|
+
|
|
|
+ var activePalletList = _palletiz.GetList(p =>
|
|
|
+ p.PalletizState == 0 &&
|
|
|
+ p.BoxRule == "CP" &&
|
|
|
+ p.IsControlpanel == false);
|
|
|
+
|
|
|
+ if (!activePalletList.Any())
|
|
|
+ {
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
|
|
|
+ res.ResMsg = "没有正在码垛的任务";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 按任务数量排序
|
|
|
+ activePalletList = SortPalletListByTaskCount(activePalletList);
|
|
|
+
|
|
|
+ var taskConfig = GetTaskConfiguration();
|
|
|
+
|
|
|
+ foreach (var pallet in activePalletList)
|
|
|
+ {
|
|
|
+ // 检查机械手任务数量限制
|
|
|
+ if (!CheckRobotTaskLimit(pallet.Robot, taskConfig.MaxTaskCount))
|
|
|
+ {
|
|
|
+ _logger.LogInformation($"{pallet.Robot}当前机械手任务数量已经超最大值{taskConfig.MaxTaskCount}");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查任务下发顺序
|
|
|
+ if (!CheckTaskSequence(pallet, taskConfig.TaskStatus))
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下发码垛任务
|
|
|
+ var taskResult = CreatePalletizingTasks(pallet);
|
|
|
+ if (taskResult.ResCode != ResponseStatusCodeEnum.Sucess.GetHashCode())
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新任务标识
|
|
|
+ UpdateTaskFlag(pallet.Robot, pallet.Equip);
|
|
|
+ }
|
|
|
+
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
|
|
|
+ res.ResMsg = "正在码垛任务处理完成";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 按任务数量排序码垛列表
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="palletList">码垛列表</param>
|
|
|
+ /// <returns>排序后的码垛列表</returns>
|
|
|
+ private List<Palletizing> SortPalletListByTaskCount(List<Palletizing> palletList)
|
|
|
+ {
|
|
|
+ if (!palletList.Any())
|
|
|
+ {
|
|
|
+ return palletList;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取每个码垛工位的未完成任务数量
|
|
|
+ var palletIds = palletList.Select(p => p.Id).ToList();
|
|
|
+
|
|
|
+ var taskCountQuery = _palletLayerMath
|
|
|
+ .GetList(p => p.Istask == 0 && palletIds.Contains(p.PalletizingId))
|
|
|
+ .GroupBy(p => p.Palletequip);
|
|
|
+
|
|
|
+ if (!taskCountQuery.Any())
|
|
|
+ {
|
|
|
+ return palletList;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 统计每个工位的任务数量
|
|
|
+ var taskCountList = (from taskGroup in taskCountQuery
|
|
|
+ select new TunnelCountTemp
|
|
|
+ {
|
|
|
+ Tunnel = taskGroup.Key,
|
|
|
+ Count = taskGroup.Count()
|
|
|
+ }).ToList();
|
|
|
+
|
|
|
+ // 按任务数量降序排序码垛列表
|
|
|
+ var sortedList = palletList.OrderByDescending(p =>
|
|
|
+ {
|
|
|
+ var matchedCount = taskCountList.FirstOrDefault(q => q.Tunnel == p.Equip);
|
|
|
+ return matchedCount?.Count ?? 0;
|
|
|
+ }).ToList();
|
|
|
+
|
|
|
+ return sortedList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 创建码垛任务
|
|
|
+ /// </summary>
|
|
|
+ private SRes CreatePalletizingTasks(Palletizing pallet)
|
|
|
+ {
|
|
|
+ var res = new SRes();
|
|
|
+
|
|
|
+ // 获取码垛工位配置
|
|
|
+ var equipConfig = GetPalletizingEquipConfig(pallet.Equip);
|
|
|
+ if (equipConfig == null)
|
|
|
+ {
|
|
|
+ SetRedisError("NoControlOut2", $"不存在该{pallet.Equip}的码垛工位");
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = $"不存在该{pallet.Equip}的码垛工位";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取待下发的码垛任务
|
|
|
+ var palletMathList = _palletLayerMath
|
|
|
+ .GetList(p => p.PalletizingId == pallet.Id && p.Istask == 0)
|
|
|
+ .OrderBy(p => p.Depth)
|
|
|
+ .Take(72);
|
|
|
+
|
|
|
+ if (!palletMathList.Any())
|
|
|
+ {
|
|
|
+ SetRedisError("NoControlOut3", $"{pallet.Equip}没有新的码垛任务需要下发");
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
|
|
|
+ res.ResMsg = "没有新的码垛任务需要下发";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取DocID
|
|
|
+ var docId = GenerateDocId(pallet.Id);
|
|
|
+
|
|
|
+ // 批量创建任务
|
|
|
+ var taskResults = new List<SRes>();
|
|
|
+ foreach (var palletMath in palletMathList)
|
|
|
+ {
|
|
|
+ var taskRes = CreateSinglePalletizingTask(palletMath, pallet, equipConfig, docId);
|
|
|
+ if (taskRes.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
|
|
|
+ {
|
|
|
+ taskResults.Add(taskRes);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Sucess.GetHashCode();
|
|
|
+ res.ResMsg = $"成功创建{taskResults.Count}个码垛任务";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 创建单个码垛任务
|
|
|
+ /// </summary>
|
|
|
+ private SRes CreateSinglePalletizingTask(
|
|
|
+ PalletLayerMath palletMath,
|
|
|
+ Palletizing pallet,
|
|
|
+ sxSysConfig equipConfig,
|
|
|
+ int docId)
|
|
|
+ {
|
|
|
+ var res = new SRes();
|
|
|
+
|
|
|
+ // 获取库位信息
|
|
|
+ var cell = _wareCell.GetSingle(p =>
|
|
|
+ p.ContGrpBarCode == palletMath.ContBarCode &&
|
|
|
+ p.StateNum == LocationState.LocationState_Full &&
|
|
|
+ p.ContGrpId == palletMath.ContGrpId);
|
|
|
+
|
|
|
+ if (cell == null)
|
|
|
+ {
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = "库位信息不存在";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取库存信息
|
|
|
+ var inventory = _invNow.GetSingle(p =>
|
|
|
+ p.ContGrpBarCode == palletMath.ContBarCode &&
|
|
|
+ p.InvStateCode == InvState.InvEcecState_In.ToString());
|
|
|
+
|
|
|
+ if (inventory == null)
|
|
|
+ {
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = "库存信息不存在";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理二深位移库
|
|
|
+ if (cell.Depth == 2)
|
|
|
+ {
|
|
|
+ var moveRes = SxServiceHelp.MoveTask(cell.Code, _wcsTaskOld, _wareHouse, _invNow, _wareCell, _task, _taskDetail, _db, _logger, _mapper, _lockerPalletizingPackTask, _lockerApplyLoc);
|
|
|
+ if (!string.IsNullOrEmpty(moveRes.Memo1) && moveRes.Memo1 == "1")
|
|
|
+ {
|
|
|
+ SetRedisError("NoControlOut23", $"{cell.Code}库位下发移库任务失败,请检查一深货位状态");
|
|
|
+ res.ResCode = ResponseStatusCodeEnum.Fail.GetHashCode();
|
|
|
+ res.ResMsg = "移库任务失败";
|
|
|
+ return res;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建码垛任务请求
|
|
|
+ var taskRequest = BuildPalletizingTaskRequest(palletMath, cell, inventory, pallet, docId);
|
|
|
+
|
|
|
+ // 调用任务创建接口
|
|
|
+ var taskResponse = SxServiceHelp.PalletizingPackTask(taskRequest, _wareCell, _invNow, _invFlow, _task, _wcsTaskOld, _taskDetail, _db, _mapper, _lockerPalletizingPackTask);
|
|
|
+
|
|
|
+ if (taskResponse.ResCode == ResponseStatusCodeEnum.Sucess.GetHashCode())
|
|
|
+ {
|
|
|
+ // 更新码垛层任务状态
|
|
|
+ _palletLayerMath.UpdateSetColumnsTrue(
|
|
|
+ p => new PalletLayerMath() { Istask = 1, Layer = inventory.PackingLayer },
|
|
|
+ p => p.ContBarCode == inventory.ContGrpBarCode);
|
|
|
+ }
|
|
|
+
|
|
|
+ return taskResponse;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取任务配置
|
|
|
+ /// </summary>
|
|
|
+ private TaskConfiguration GetTaskConfiguration()
|
|
|
+ {
|
|
|
+ return new TaskConfiguration
|
|
|
+ {
|
|
|
+ TaskStatus = (TaskStatus)int.Parse(_sysconfing.GetSingle(p => p.Code == "PreNextTaskStatus").SContent),
|
|
|
+ MaxTaskCount = int.Parse(_sysconfing.GetSingle(p => p.Code == "PreNextTaskCount").SContent)
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取码垛工位配置
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="equipCode">工位编码</param>
|
|
|
+ /// <returns>工位配置信息</returns>
|
|
|
+ private sxSysConfig GetPalletizingEquipConfig(string equipCode)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 从视图中查询码垛工位配置
|
|
|
+ var config = _sysconfing.Context.Ado
|
|
|
+ .SqlQuery<sxSysConfig>($"select * from WMS_ZT_01SX.dbo.V_PalletizingPack where code = '{equipCode}'")
|
|
|
+ .FirstOrDefault();
|
|
|
+
|
|
|
+ if (config == null)
|
|
|
+ {
|
|
|
+ _logger.LogWarning($"未找到工位 {equipCode} 的配置信息");
|
|
|
+ }
|
|
|
+
|
|
|
+ return config;
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, $"获取工位 {equipCode} 配置信息时发生异常");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查任务下发顺序
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckTaskSequence(
|
|
|
+ Palletizing currentPallet,
|
|
|
+ TaskStatus taskStatus)
|
|
|
+ {
|
|
|
+ // 找到当前机械手最早的码垛信息
|
|
|
+ var earliestPallet = _palletiz
|
|
|
+ .GetList(p => p.PalletizState != 1 && p.Robot == currentPallet.Robot)
|
|
|
+ .OrderBy(p => p.Id)
|
|
|
+ .FirstOrDefault();
|
|
|
+
|
|
|
+ if (earliestPallet == null || currentPallet.Id == earliestPallet.Id)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否是第3箱
|
|
|
+ var result = CheckThirdBoxRestriction(currentPallet, earliestPallet, taskStatus);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查第3箱限制
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckThirdBoxRestriction(
|
|
|
+ Palletizing currentPallet,
|
|
|
+ Palletizing earliestPallet,
|
|
|
+ TaskStatus taskStatus)
|
|
|
+ {
|
|
|
+ var robotPallets = _palletiz.GetList(p =>
|
|
|
+ p.PalletizState != 1 &&
|
|
|
+ p.Robot == currentPallet.Robot);
|
|
|
+
|
|
|
+ if (robotPallets.Count < 3)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ var thirdBoxId = robotPallets.OrderBy(p => p.Id).Take(3).Last().Id;
|
|
|
+
|
|
|
+ // 如果是第3箱
|
|
|
+ if (currentPallet.Id == thirdBoxId)
|
|
|
+ {
|
|
|
+ // 检查最早的箱子任务是否过了卡控点
|
|
|
+ if (HasUnfinishedTasks(earliestPallet.Id, taskStatus))
|
|
|
+ {
|
|
|
+ _logger.LogInformation($"第3箱卡控同一机械手最早的箱子的码垛任务没有全部过卡控点{taskStatus.GetDescription()}");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查前两箱是否都是同一目标地址
|
|
|
+ var firstTwoBoxes = robotPallets.Where(p => p.Id < thirdBoxId).ToList();
|
|
|
+ if (firstTwoBoxes.Count == 2 && firstTwoBoxes.Select(p => p.Equip).Distinct().Count() == 1)
|
|
|
+ {
|
|
|
+ _logger.LogInformation("第3箱卡控同一机械手前面两箱都是同一目标地址,则不下第3箱任务");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (currentPallet.Id > thirdBoxId)
|
|
|
+ {
|
|
|
+ _logger.LogInformation($"同一机械手第3箱之后的任务不允许下发,码垛主表id:{currentPallet.Id}");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 构建码垛任务请求
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="palletMath">码垛层数学信息</param>
|
|
|
+ /// <param name="cell">库位信息</param>
|
|
|
+ /// <param name="inventory">库存信息</param>
|
|
|
+ /// <param name="pallet">码垛主信息</param>
|
|
|
+ /// <param name="docId">文档ID</param>
|
|
|
+ /// <returns>码垛任务请求对象</returns>
|
|
|
+ private PalletizingPackTaskRequest BuildPalletizingTaskRequest(
|
|
|
+ PalletLayerMath palletMath,
|
|
|
+ BaseWarecell cell,
|
|
|
+ BillInvnow inventory,
|
|
|
+ Palletizing pallet,
|
|
|
+ int docId)
|
|
|
+ {
|
|
|
+ // 确定机械手
|
|
|
+ var robot = SxServiceHelp.DetermineRoot(palletMath.Palletequip);
|
|
|
+
|
|
|
+ // 构建任务请求
|
|
|
+ var request = new PalletizingPackTaskRequest
|
|
|
+ {
|
|
|
+ // 容器条码
|
|
|
+ Code = palletMath.ContBarCode,
|
|
|
+
|
|
|
+ // 库位信息
|
|
|
+ CellCode = cell.Code,
|
|
|
+ Srm = cell.SCRel,
|
|
|
+ Tunnel = cell.Tunnel.ToString(),
|
|
|
+ Floor = cell.Floor,
|
|
|
+
|
|
|
+ // 产品信息
|
|
|
+ Grade = inventory.Grade,
|
|
|
+ Mater = inventory.MatCode,
|
|
|
+ SkuCode = palletMath.SkuCode,
|
|
|
+ ProductMachCode = inventory.ProductMachCode,
|
|
|
+
|
|
|
+ // 码垛信息
|
|
|
+ PalletLayer = inventory.PackingLayer,
|
|
|
+ Equip = pallet.Equip,
|
|
|
+ PalletizingId = pallet.Id,
|
|
|
+ Robot = robot,
|
|
|
+
|
|
|
+ // 货物类型:1-BS80/33, 2-其他
|
|
|
+ GoodsType = inventory.HWTypeCode == "BS80/33" ? 1 : 2,
|
|
|
+
|
|
|
+ // 文档ID
|
|
|
+ DocId = docId
|
|
|
+ };
|
|
|
+
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 处理正在码垛的任务
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新码垛状态为正在码垛
|
|
|
+ /// </summary>
|
|
|
+ private void UpdatePalletStateToActive(long palletId)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ _palletiz.AsUpdateable()
|
|
|
+ .SetColumns(p => new Palletizing() { PalletizState = 0 })
|
|
|
+ .Where(p => p.Id == palletId)
|
|
|
+ .ExecuteCommand();
|
|
|
+
|
|
|
+ _logger.LogInformation($"码垛任务 {palletId} 状态已更新为正在码垛");
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, $"更新码垛任务 {palletId} 状态时发生异常");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #region 辅助方法
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置Redis错误信息
|
|
|
+ /// </summary>
|
|
|
+ private void SetRedisError(string equipCode, string message)
|
|
|
+ {
|
|
|
+ RedisHelper.Set($"Sx:WMSErrorInfo:{equipCode}", new RedisErrorInfo
|
|
|
+ {
|
|
|
+ Equip = equipCode,
|
|
|
+ Con = message,
|
|
|
+ Time = DateTime.Now
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查机械手任务数量限制
|
|
|
+ /// </summary>
|
|
|
+ private bool CheckRobotTaskLimit(string robot, int maxCount)
|
|
|
+ {
|
|
|
+ var currentCount = _task.AsQueryable()
|
|
|
+ .With(SqlWith.NoLock)
|
|
|
+ .Where(p => p.Status < TaskStatus.码垛抓取完成 &&
|
|
|
+ p.Robot == robot &&
|
|
|
+ p.BusType == "装箱码垛")
|
|
|
+ .Count();
|
|
|
+
|
|
|
+ return currentCount < maxCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 判断是否有未完成的任务
|
|
|
+ /// </summary>
|
|
|
+ private bool HasUnfinishedTasks(long palletizingId, TaskStatus taskStatus)
|
|
|
+ {
|
|
|
+ var hasUnfinishedMath = _palletLayerMath
|
|
|
+ .GetList(p => p.PalletizingId == palletizingId && p.Istask == 0)
|
|
|
+ .Any();
|
|
|
+
|
|
|
+ var hasUnfinishedTask = _task.AsQueryable()
|
|
|
+ .With(SqlWith.NoLock)
|
|
|
+ .Where(p => p.PalletizingId == palletizingId && p.Status < taskStatus)
|
|
|
+ .Any();
|
|
|
+
|
|
|
+ return hasUnfinishedMath || hasUnfinishedTask;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新任务标识
|
|
|
+ /// </summary>
|
|
|
+ private void UpdateTaskFlag(string robot, string equipCode)
|
|
|
+ {
|
|
|
+ var currentFlag = _sysconfing.Context.Ado
|
|
|
+ .SqlQuery<sxSysConfig>("select * from WMS_ZT_01SX.dbo.V_PalletizingPack")
|
|
|
+ .Where(p => p.Robot == robot)
|
|
|
+ .Max(p => p.TaskFlag);
|
|
|
+
|
|
|
+ var newFlag = currentFlag + 1;
|
|
|
+
|
|
|
+ _sysconfing.Context.Ado.ExecuteCommand($"update WMS_ZT_01CP.dbo.sys_config set taskflag={newFlag} where code='{equipCode}'");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 记录超时记录
|
|
|
+ /// </summary>
|
|
|
+ private void WreTimeOutRecord(List<string> containerBarcodes, string reason)
|
|
|
+ {
|
|
|
+ // 实现超时记录逻辑
|
|
|
+ _logger.LogWarning($"超时记录 - 容器条码: {string.Join(",", containerBarcodes)}, 原因: {reason}");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 生成文档ID
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="palletizingId">码垛ID</param>
|
|
|
+ /// <returns>文档ID</returns>
|
|
|
+ private int GenerateDocId(long palletizingId)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 获取装箱码垛类型任务的最大DocID
|
|
|
+ var maxDocId = _task.AsQueryable()
|
|
|
+ .Where(p => p.BusType == "装箱码垛")
|
|
|
+ .Max(p => (int?)p.DocID) ?? 0;
|
|
|
+
|
|
|
+ // 默认使用最大值+1
|
|
|
+ var currentDocId = maxDocId + 1;
|
|
|
+
|
|
|
+ // 检查当前码垛是否已有任务,如果有则使用相同的DocID
|
|
|
+ var existingDocId = _task.AsQueryable()
|
|
|
+ .With(SqlWith.NoLock)
|
|
|
+ .Where(p => p.PalletizingId == palletizingId && p.DocID > 0)
|
|
|
+ .Max(p => (int?)p.DocID);
|
|
|
+
|
|
|
+ if (existingDocId.HasValue)
|
|
|
+ {
|
|
|
+ currentDocId = existingDocId.Value;
|
|
|
+ }
|
|
|
+
|
|
|
+ return currentDocId;
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ _logger.LogError(ex, $"生成文档ID时发生异常,码垛ID:{palletizingId}");
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion 辅助方法
|
|
|
+
|
|
|
+ #endregion 下发装箱任务及缓存托盘
|
|
|
+ }
|
|
|
+}
|