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
{
///
/// 层配装箱帮助类
///
public class SxLayerPackingHelp
{
private readonly Repository _wareCell;
private readonly Repository _invNow;
private readonly Repository _boxRule;
private readonly Repository _sysconfing;
private readonly RepositoryTask _timeoutrecord;
private readonly ILogger _logger;
private readonly Repository _palletLayerMath;
private readonly Repository _invFlow;
private readonly RepositoryTask _wcsTaskOld;
private readonly Repository _wareHouse;
private readonly RepositoryTask _taskDetail;
private readonly RepositoryTask _task;
private readonly Repository _palletiz;
private readonly ITenant _db;
private readonly IMapper _mapper;
private readonly object _lockerPalletizingPackTask;
private readonly object _lockerApplyLoc;
///
/// 构造函数
///
public SxLayerPackingHelp(
Repository wareCell,
Repository invNow,
Repository boxRule,
Repository sysconfing,
RepositoryTask timeoutrecord,
ILogger logger,
Repository palletLayerMath,
Repository invFlow,
RepositoryTask wcsTaskOld,
Repository wareHouse,
RepositoryTask taskDetail,
RepositoryTask task,
Repository 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 获取可用库存
///
/// 获取可用库存
///
///
public IEnumerable 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);
}
///
/// 获取非检测非重绕盘
///
/// 超时小时
///
private IEnumerable 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);
}
///
/// 获取非检测重绕盘
///
/// 超时小时
///
private IEnumerable 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);
}
///
/// 获取已检测非重绕盘
///
/// 超时小时
///
private IEnumerable 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);
}
///
/// 获取已检测重绕盘
///
/// 超时小时
///
private IEnumerable 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);
}
///
/// 创建库存临时对象
///
/// 货位信息
/// 库存信息
/// sku
///
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
};
}
///
/// 过滤禁用巷道
///
/// 可用产品
///
public IEnumerable FilterByTunnelRestrictions(IEnumerable 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 装箱逻辑
///
/// 按SKU执行装箱
///
///
///
///
///
public SRes ProcessSkuGroup(
IGrouping skuGroup,
List boxRules,
IEnumerable 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;
}
///
/// 处理装箱规则组
///
/// 箱号
/// 产品
/// sku
///
public SRes ProcessRuleGroup(
IGrouping ruleGroup,
IEnumerable 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);
}
///
/// 处理特殊SKU --指不管控机器组
///
/// sku
///
private bool IsSpecialSku(string skuCode)
{
return skuCode == "6210010401" || skuCode == "6210120101";
}
///
/// 处理不需要管控机器组的SKU
///
///
///
///
public SRes ProcessSpecialSku(
BillPboxrule rule,
IEnumerable 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;
}
///
/// 装箱
///
/// 箱号
/// 有效库存
/// 是否控制机器组
/// 对应的分组sku
///
public StockAllocationResult AllocateStock(
BillPboxrule rule,
IEnumerable availableStock,
bool isSpecialSku = false,
IGrouping? 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;
}
///
/// 过滤库存
///
/// 产品
/// 装箱规则
/// 是否管控机器组
/// 管控机器组时用于过滤的机器组信息
///
private IEnumerable FilterStock(
IEnumerable stock,
BillPboxrule rule,
bool isSpecialSku,
IGrouping? 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 箱号验证
///
/// 箱号验证
///
/// 待验证的箱号
/// 批号
///
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(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;
}
///
/// 创建ESB请求
///
/// 请求ID
///
private EsbReq CreateEsbRequest(string requestId)
{
return new EsbReq
{
headers = new HeadersReq
{
serviceCode = "163K162003",
requestId = requestId,
TrackId = requestId,
sourceCode = "163K"
}
};
}
///
/// 发送MES请求
///
/// 请求参数
/// 请求ID
/// esb请求信息
/// esb地址
///
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 更新装箱/库存信息
///
/// 预占用库存
///
/// 箱号
/// 产品信息
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;
}
///
/// 更新箱号状态
///
/// 箱号
/// 状态
/// 备注
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 处理无箱号可用的情况
///
/// 处理没有箱号的情况
///
/// 产品信息
///
public SRes HandleNoBoxRuleError(IEnumerable 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
};
}
///
/// 处理没有箱号可用的情况 --SKU
///
/// SKU
/// 产品信息
///
public SRes HandleNoBoxRuleForSku(string skuCode, IEnumerable 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 处理正在码垛的任务
///
/// 处理正在码垛的任务
///
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;
}
///
/// 按任务数量排序码垛列表
///
/// 码垛列表
/// 排序后的码垛列表
private List SortPalletListByTaskCount(List 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;
}
///
/// 创建码垛任务
///
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();
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;
}
///
/// 创建单个码垛任务
///
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;
}
///
/// 获取任务配置
///
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)
};
}
///
/// 获取码垛工位配置
///
/// 工位编码
/// 工位配置信息
private sxSysConfig GetPalletizingEquipConfig(string equipCode)
{
try
{
// 从视图中查询码垛工位配置
var config = _sysconfing.Context.Ado
.SqlQuery($"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;
}
}
///
/// 检查任务下发顺序
///
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;
}
///
/// 检查第3箱限制
///
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;
}
///
/// 构建码垛任务请求
///
/// 码垛层数学信息
/// 库位信息
/// 库存信息
/// 码垛主信息
/// 文档ID
/// 码垛任务请求对象
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 处理正在码垛的任务
///
/// 更新码垛状态为正在码垛
///
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 辅助方法
///
/// 设置Redis错误信息
///
private void SetRedisError(string equipCode, string message)
{
RedisHelper.Set($"Sx:WMSErrorInfo:{equipCode}", new RedisErrorInfo
{
Equip = equipCode,
Con = message,
Time = DateTime.Now
});
}
///
/// 检查机械手任务数量限制
///
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;
}
///
/// 判断是否有未完成的任务
///
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;
}
///
/// 更新任务标识
///
private void UpdateTaskFlag(string robot, string equipCode)
{
var currentFlag = _sysconfing.Context.Ado
.SqlQuery("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}'");
}
///
/// 记录超时记录
///
private void WreTimeOutRecord(List containerBarcodes, string reason)
{
// 实现超时记录逻辑
_logger.LogWarning($"超时记录 - 容器条码: {string.Join(",", containerBarcodes)}, 原因: {reason}");
}
///
/// 生成文档ID
///
/// 码垛ID
/// 文档ID
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 下发装箱任务及缓存托盘
}
}