first commit
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.Common.Dto.Http.In;
|
||||
using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.IService
|
||||
{
|
||||
public interface IStockInService
|
||||
{
|
||||
/// <summary>
|
||||
/// 入库
|
||||
/// </summary>
|
||||
/// <param name="stockInParam"></param>
|
||||
/// <returns></returns>
|
||||
Task<string> StockIn(PDAInParam stockInParam);
|
||||
/// <summary>
|
||||
/// 从ERPTask生成AgvTask(pda入库)
|
||||
/// </summary>
|
||||
/// <param name="erpTask"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> CreateAgvTaskFirstFloor(ERPTaskDto erpTask);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.IService
|
||||
{
|
||||
/// <summary>
|
||||
/// 入库策略
|
||||
/// </summary>
|
||||
public interface IStockInStrategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 计算目标库位
|
||||
/// </summary>
|
||||
/// <param name="exceptColumnId">要排除的列(出库掏货再回库不可以回原来的列)</param>
|
||||
/// <returns></returns>
|
||||
Task<StorageRackDto> CalCulateWarehouse();
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.ServiceImpl;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using NPOI.OpenXmlFormats.Dml;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
|
||||
{
|
||||
/// <summary>
|
||||
/// 放置在已有相同批次的列上
|
||||
/// </summary>
|
||||
public class StockInByExist : StockInHandler
|
||||
{
|
||||
public override async Task<StorageRackDto> CalculateWarehouse(
|
||||
TransportEnum startArea,
|
||||
FloorEnum floor,
|
||||
string materialBatch,
|
||||
List<StorageRackDto> storageRacks
|
||||
)
|
||||
{
|
||||
//1.查找有没有同批次待执行的任务
|
||||
//2.有待执行的任务,直接进入中转区域
|
||||
//3.没有待执行的任务,则采用现有逻辑获取一个同批次库位
|
||||
|
||||
//非中转区的任务,需要先判断是否有同批次未完成的任务,如有则需要等待,确保同批次的货物放满同一列
|
||||
|
||||
await _SemaphoreSlim.WaitAsync();
|
||||
StorageRackDto destStorageRack = null;
|
||||
|
||||
try
|
||||
{
|
||||
storageRacks = await base.GetStorageRackExceptStockout(floor);
|
||||
|
||||
if (storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
LogHelper.Info("没有可用库位");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<AGVTaskDto> uncompletedTasks = await _agvTaskService.GetListByExpression(p =>
|
||||
p.CreationTime > DateTime.Now.Date &&
|
||||
p.MateriaBatch == materialBatch &&
|
||||
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn || p.TaskType == TaskTypeEnum.TransferStockIn) &&
|
||||
(p.TaskStatus != TaskStatusEnum.Completed)
|
||||
);
|
||||
|
||||
List<long> sameBatchColIds = storageRacks
|
||||
.Where(p =>
|
||||
p.MateriaBatch == materialBatch ||
|
||||
(!uncompletedTasks.IsNullOrEmpty() && uncompletedTasks.Select(x => x.EndPositionCode).Contains(p.StorageRackNo))
|
||||
)
|
||||
.Select(p => p.ReservoirAreaColumnId)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (sameBatchColIds.IsNullOrEmpty())
|
||||
{
|
||||
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<StorageRackDto> sameBatchInclude = storageRacks
|
||||
.Where(p => sameBatchColIds.Contains(p.ReservoirAreaColumnId))
|
||||
.ToList();
|
||||
|
||||
var sameBatchIncludeCol = sameBatchInclude.GroupBy(p => p.ReservoirAreaColumnId);
|
||||
|
||||
// 筛选还有空位的列(包含锁定的列)
|
||||
// 有未锁定的列优先取该列
|
||||
// 没有未锁定的列,则任取一列做中转(如果物料所处位置已经是中转区,则不执行)
|
||||
|
||||
var useableStorageCol = sameBatchIncludeCol
|
||||
.FirstOrDefault(p =>
|
||||
p.All(x => !x.IsLock) &&
|
||||
p.Any(x => !x.IsDisabled && string.IsNullOrEmpty(x.BarCode)));
|
||||
|
||||
if (useableStorageCol != null && useableStorageCol.Any())
|
||||
{
|
||||
destStorageRack = useableStorageCol
|
||||
.Where(p => !p.IsDisabled && string.IsNullOrEmpty(p.BarCode))
|
||||
.OrderByDescending(p => p.NumSort)
|
||||
.First();
|
||||
}
|
||||
else
|
||||
{
|
||||
var emptyCols = sameBatchIncludeCol.Where(p => p.Any(x => !x.IsLock && !x.IsDisabled && string.IsNullOrEmpty(x.BarCode)));
|
||||
if (emptyCols.Any())
|
||||
{
|
||||
//如果仅有一个空列,则可以开一个新列,保证最多只开2列
|
||||
if (emptyCols.Count() <= 1)
|
||||
{
|
||||
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
|
||||
}
|
||||
else
|
||||
{
|
||||
//一楼不允许混放或进入中转区。同批次已开启两列后,等待现有任务完成并解锁列。
|
||||
if (floor == FloorEnum.FirstFloor)
|
||||
{
|
||||
LogHelper.Warn("一楼同批次已开启两列且目标列被锁定,入库任务等待列解锁");
|
||||
return null;
|
||||
}
|
||||
|
||||
//物料已在中转区,则不可以继续在中转区生成任务
|
||||
if (startArea == TransportEnum.TransitArea)
|
||||
{
|
||||
LogHelper.Warn("目标库位所在列被锁定,中转入库任调度需要等待列解锁");
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
destStorageRack = await _storageRackService.GetUsableTransferStorageRack();
|
||||
|
||||
if (destStorageRack == null)
|
||||
{
|
||||
LogHelper.Warn($"中转区已满,尝试放货到空白列或者和其他货物混放");
|
||||
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (destStorageRack != null)
|
||||
{
|
||||
// 先完成数据库锁定,确保数据一致性
|
||||
await _storageRackService.Lock(destStorageRack);
|
||||
}
|
||||
|
||||
return destStorageRack;
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.Error($"入库责任链处理异常:{ex.Message} \n {ex.StackTrace}");
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_SemaphoreSlim.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
|
||||
{
|
||||
/// <summary>
|
||||
/// 和其他不同批次的货物混合放置
|
||||
/// </summary>
|
||||
public class StockInByMixing : StockInHandler
|
||||
{
|
||||
public override async Task<StorageRackDto> CalculateWarehouse(
|
||||
TransportEnum startArea,
|
||||
FloorEnum floor,
|
||||
string materialBatch,
|
||||
List<StorageRackDto> storageRacks)
|
||||
{
|
||||
if (storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
storageRacks = await base.GetStorageRackExceptStockout(floor);
|
||||
}
|
||||
|
||||
if (storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
LogHelper.Info("没有可用库位");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<StorageRackDto> wareHousesSorted = storageRacks.OrderBy(p => p.AreaNumSort).ToList();
|
||||
|
||||
IEnumerable<IGrouping<long, StorageRackDto>> groupByColumn = wareHousesSorted.GroupBy(p => p.ReservoirAreaColumnId);
|
||||
|
||||
StorageRackDto destStorageRack = null;
|
||||
foreach (var columnGroup in groupByColumn)
|
||||
{
|
||||
bool unlockedColumn = columnGroup.All(p => !p.IsLock);
|
||||
if (unlockedColumn)
|
||||
{
|
||||
destStorageRack = columnGroup
|
||||
.Where(p => string.IsNullOrEmpty(p.BarCode))
|
||||
.OrderByDescending(p => p.NumSort)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (destStorageRack != null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (destStorageRack == null && startArea != TransportEnum.TransitArea)
|
||||
{
|
||||
destStorageRack = await _storageRackService.GetUsableTransferStorageRack();
|
||||
|
||||
if (destStorageRack == null)
|
||||
{
|
||||
LogHelper.Warn($"存储区和中转区均无可用的库位");
|
||||
}
|
||||
}
|
||||
|
||||
return destStorageRack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
|
||||
{
|
||||
/// <summary>
|
||||
/// 放置在一个空白列列上
|
||||
/// </summary>
|
||||
public class StockInByNew : StockInHandler
|
||||
{
|
||||
public override async Task<StorageRackDto> CalculateWarehouse(
|
||||
TransportEnum startArea,
|
||||
FloorEnum floor,
|
||||
string materialBatch,
|
||||
List<StorageRackDto> storageRacks)
|
||||
{
|
||||
if (storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
storageRacks = await base.GetStorageRackExceptStockout(floor);
|
||||
}
|
||||
|
||||
if (storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
LogHelper.Info("没有可用库位");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<StorageRackDto> storageRacksSorted = storageRacks
|
||||
.OrderBy(p => p.AreaNumSort)
|
||||
.ThenBy(p => p.ReservoirAreaColumnId).ToList();
|
||||
|
||||
var groupByColumn = storageRacksSorted.GroupBy(p => p.ReservoirAreaColumnId);
|
||||
|
||||
StorageRackDto destWarehouse = null;
|
||||
foreach (IGrouping<long, StorageRackDto> column in groupByColumn)
|
||||
{
|
||||
if (column.All(p => p.IsEmpty()))
|
||||
{
|
||||
destWarehouse = column.OrderByDescending(p => p.NumSort).First();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (destWarehouse == null)
|
||||
{
|
||||
return await HandleByNext(startArea, floor,materialBatch, storageRacksSorted);
|
||||
}
|
||||
else
|
||||
{
|
||||
return destWarehouse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.IService;
|
||||
using JSMachine.WMS.Domain.Entity;
|
||||
using JSMachine.WMS.Infrastructure;
|
||||
using NPOI.SS.Formula.Functions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
|
||||
{
|
||||
/// <summary>
|
||||
/// 责任链模式。入库处理基类 。责任流动方向:StockInByExist=》StockInByNew=》StockInByMixing
|
||||
/// </summary>
|
||||
public abstract class StockInHandler
|
||||
{
|
||||
protected StockInHandler _nextHandler;
|
||||
protected static IStorageRackService _storageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
|
||||
protected static IAGVTaskService _agvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
|
||||
protected static IERPTaskService _erpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
|
||||
|
||||
protected static IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
|
||||
protected static readonly SemaphoreSlim _SemaphoreSlim = new(1,1);
|
||||
|
||||
public void SetNextHandler(StockInHandler nextHandler)
|
||||
{
|
||||
_nextHandler = nextHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算目标库位
|
||||
/// </summary>
|
||||
/// <param name="startStorageRack">起点</param>
|
||||
/// <param name="materialBatch">批次</param>
|
||||
/// <param name="storageRacks">所有未禁用的库位</param>
|
||||
/// <returns></returns>
|
||||
public abstract Task<StorageRackDto> CalculateWarehouse(
|
||||
TransportEnum startArea, FloorEnum floor, string materialBatch, List<StorageRackDto> storageRacks);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 有下一个处理者,则继续执行下一个处理者的逻辑
|
||||
/// </summary>
|
||||
/// <param name="startStorageRack">起点</param>
|
||||
/// <param name="materialBatch">批次</param>
|
||||
/// <param name="storageRacks">所有未禁用的库位</param>
|
||||
/// <returns></returns>
|
||||
public async Task<StorageRackDto> HandleByNext(
|
||||
TransportEnum startArea,
|
||||
FloorEnum floor,
|
||||
string materialBatch,
|
||||
List<StorageRackDto> storageRacks)
|
||||
{
|
||||
if (_nextHandler != null)
|
||||
{
|
||||
return await _nextHandler.CalculateWarehouse(startArea, floor, materialBatch, storageRacks);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<StorageRackDto>> GetStorageRackExceptStockout(FloorEnum floor)
|
||||
{
|
||||
List<StorageRackDto> storageRacks = await StorageRackService.GetListByExpression(p =>
|
||||
p.Floor == floor &&
|
||||
p.Transport == TransportEnum.NormalArea);
|
||||
|
||||
if(storageRacks.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ERPTaskDto> erpTaskStockOuts = await _erpTaskService
|
||||
.GetListByExpression(p =>
|
||||
p.CreationTime > DateTime.Now.AddDays(-1) &&
|
||||
p.TaskStatus == TaskStatusEnum.InExecution &&
|
||||
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut)
|
||||
);
|
||||
|
||||
if (!erpTaskStockOuts.IsNullOrEmpty())
|
||||
{
|
||||
List<AGVTaskDto> agvTaks = await _agvTaskService.GetListByExpression(p =>
|
||||
p.CreationTime > DateTime.Now.AddDays(-1) &&
|
||||
erpTaskStockOuts.Select(x => x.Id).Contains(p.ErpTaskId)
|
||||
);
|
||||
|
||||
if (!agvTaks.IsNullOrEmpty())
|
||||
{
|
||||
List<StorageRackDto> startStorageRacks = await StorageRackService.GetListByExpression(p =>
|
||||
agvTaks.Select(x => x.StartPositionCode).Contains(p.StorageRackNo));
|
||||
|
||||
List<long> exceptCols = startStorageRacks.Select(p => p.ReservoirAreaColumnId).Distinct().ToList();
|
||||
|
||||
storageRacks.RemoveAll(p => exceptCols.Contains(p.ReservoirAreaColumnId));
|
||||
}
|
||||
}
|
||||
|
||||
return storageRacks;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.IService;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Util;
|
||||
using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult;
|
||||
using JSMachine.WMS.RPC.ErpRPC.Dto.Out.XRBusinessResult;
|
||||
using JSMachine.WMS.RPC.ErpRPC.IService;
|
||||
using NPOI.OpenXmlFormats.Dml.Chart;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// PDA入库业务类
|
||||
/// </summary>
|
||||
public class StockInService : IStockInService
|
||||
{
|
||||
private IPaperStorageService _paperStorageService;
|
||||
private IStorageRackService _storageRackService;
|
||||
private IERPTaskService _erpTaskService;
|
||||
private IAGVTaskService _agvTaskService;
|
||||
private IElevatorPlcQueueService _elevatorPlcQueueService;
|
||||
public StockInService(
|
||||
IPaperStorageService paperStorageService,
|
||||
IStorageRackService storageRackService,
|
||||
IERPTaskService erpTaskService,
|
||||
IAGVTaskService agvTaskService,
|
||||
IElevatorPlcQueueService elevatorPlcQueueService
|
||||
)
|
||||
{
|
||||
_paperStorageService = paperStorageService;
|
||||
_storageRackService = storageRackService;
|
||||
_erpTaskService = erpTaskService;
|
||||
_agvTaskService = agvTaskService;
|
||||
_elevatorPlcQueueService = elevatorPlcQueueService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PDA入库
|
||||
/// </summary>
|
||||
/// <param name="stockInParam"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> StockIn(PDAInParam stockInParam)
|
||||
{
|
||||
// 入库流程依次校验重复任务、物料和起点库位,再计算目标库位并创建任务。
|
||||
//1.查询物料信息是否存在
|
||||
//2.查询库位是否存在
|
||||
//3.计算目标库位并锁定
|
||||
//4.写入ErpTask表
|
||||
|
||||
bool existUnCompleteTask = await _erpTaskService.Exist(p =>
|
||||
p.CreationTime > DateTime.Now.Date &&
|
||||
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType==TaskTypeEnum.TailIn)&&
|
||||
(p.TaskStatus == TaskStatusEnum.Initial || p.TaskStatus == TaskStatusEnum.InExecution) &&
|
||||
p.BarCode == stockInParam.BarCode);
|
||||
|
||||
if (existUnCompleteTask)
|
||||
{
|
||||
LogHelper.Warn($"合格证号 {stockInParam.BarCode} 已有待入库的任务,请检查是否扫错");
|
||||
return $"合格证号 {stockInParam.BarCode} 已有待入库的任务,请检查是否扫错";
|
||||
}
|
||||
|
||||
MateriaInfoDto materiaInfo = await CheckMaterialInfo(stockInParam.BarCode);
|
||||
if (materiaInfo == null)
|
||||
{
|
||||
string msg = $"未在ERP中查询到合格证号为 {stockInParam.BarCode} 的物料信息";
|
||||
|
||||
LogHelper.Warn(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
StorageRackDto startStoragRack = await _storageRackService.GetSingalByExpression(p =>
|
||||
p.StorageRackNo == stockInParam.StorageRackNo &&
|
||||
!p.IsDisabled);
|
||||
|
||||
if (startStoragRack == null)
|
||||
{
|
||||
string msg = $"入库起点库位 {stockInParam.StorageRackNo} 不可用";
|
||||
|
||||
LogHelper.Warn(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
//如果是尾托回库,需要自动计算任务楼层
|
||||
if (stockInParam.TaskType == TaskTypeEnum.TailIn)
|
||||
{
|
||||
AGVTaskDto outTask = await _agvTaskService.GetSingalByExpression(p =>
|
||||
p.BarCode == stockInParam.BarCode &&
|
||||
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.TransferStockOut) &&
|
||||
!p.StartPositionCode.Contains("TSJ"),
|
||||
p => p.CreationTime,
|
||||
SqlSugar.OrderByType.Desc);
|
||||
|
||||
if (outTask == null)
|
||||
{
|
||||
string msg = $"合格证号 {stockInParam.BarCode} 批次 {materiaInfo.MateriaBatch} 没有对应的出库任务," +
|
||||
$"因此不知道该物料原来所属楼层,请人工将该货物放置到合适的库位上,然后使用pda更新物料信息";
|
||||
LogHelper.Error(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
stockInParam.Floor = outTask.Floor;
|
||||
}
|
||||
|
||||
//预先计算可用的库位(所有未禁用,空白库位-未完成的入库任务)
|
||||
int usableWarehouseCout = await _storageRackService.Count(p =>
|
||||
p.Floor == stockInParam.Floor &&
|
||||
p.Transport == TransportEnum.NormalArea &&
|
||||
!p.IsDisabled);
|
||||
int unCompleteErpTaskCount = await _erpTaskService.Count(p =>
|
||||
p.CreationTime > DateTime.Now.Date &&
|
||||
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn) &&
|
||||
!(p.TaskStatus == TaskStatusEnum.Completed || p.TaskStatus == TaskStatusEnum.Cancelled)
|
||||
);
|
||||
|
||||
if (usableWarehouseCout- unCompleteErpTaskCount <=0)
|
||||
{
|
||||
string msg = $"没有空余库位,无法入库";
|
||||
|
||||
LogHelper.Warn(msg);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
ERPTaskDto erpTask = CreateErpTask(stockInParam, materiaInfo);
|
||||
bool bRet = await _erpTaskService.Add(erpTask);
|
||||
if (!bRet)
|
||||
{
|
||||
string msg = $"数据库操作失败";
|
||||
|
||||
LogHelper.Warn(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ERPTask生成AgvTask(pda入库)
|
||||
/// </summary>
|
||||
/// <param name="erpTask"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> CreateAgvTaskFirstFloor(ERPTaskDto erpTask)
|
||||
{
|
||||
//1.入库到一楼寻找一楼库位
|
||||
//2.入库到二楼寻找一楼提升机库位
|
||||
|
||||
StorageRackDto destWareHouse = await CalculateDestWarehouse(erpTask, erpTask.MateriaBatch, erpTask.Floor);
|
||||
if (destWareHouse == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AGVTaskDto agvTask = new()
|
||||
{
|
||||
ErpTaskId = erpTask.Id,
|
||||
BarCode = erpTask.BarCode,
|
||||
OrderNo = erpTask.OrderNo,
|
||||
CustomName = erpTask.CustomName,
|
||||
MateriaName = erpTask.MateriaName,
|
||||
MateriaType = erpTask.MateriaType,
|
||||
MateriaNum = erpTask.ErpOrderNum,
|
||||
MateriaBatch = erpTask.MateriaBatch,
|
||||
MateriaCode = erpTask.MateriaCode,
|
||||
|
||||
TaskType = erpTask.TaskType,
|
||||
TaskStatus = TaskStatusEnum.Initial,
|
||||
Priority = PriorityEnum.High,
|
||||
Floor = FloorEnum.FirstFloor,
|
||||
|
||||
StartPositionCode = erpTask.StorageRackNo,
|
||||
EndPositionCode = destWareHouse.StorageRackNo,
|
||||
|
||||
ErpUploadStatus = destWareHouse.Transport == TransportEnum.Elevator ? ErpUploadStatusEnum.Unnecessary : ErpUploadStatusEnum.WaittingToUpload,
|
||||
|
||||
Transport = TransportEnum.NormalArea
|
||||
};
|
||||
|
||||
destWareHouse.IsLock = true;
|
||||
|
||||
await _storageRackService.Lock(destWareHouse);
|
||||
return await _agvTaskService.Add(agvTask);
|
||||
}
|
||||
|
||||
private async Task<MateriaInfoDto> CheckMaterialInfo(string barcode)
|
||||
{
|
||||
XRResultDto xRResult = await _paperStorageService.GetMateriaInfo(barcode);
|
||||
if (xRResult?.status == 200)
|
||||
return xRResult.data;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算目标库位
|
||||
/// </summary>
|
||||
/// <param name="materiaInfo"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<StorageRackDto> CalculateDestWarehouse(ERPTaskDto erpTask, string materiaBatch, FloorEnum floor)
|
||||
{
|
||||
//1.入库到一楼寻找一楼库位
|
||||
//2.入库到二楼寻找一楼提升机库位
|
||||
|
||||
if (floor == FloorEnum.FirstFloor)
|
||||
{
|
||||
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(TransportEnum.ManualArea, floor, materiaBatch);
|
||||
|
||||
StorageRackDto destWarehouse = await stockInStrategy.CalCulateWarehouse();
|
||||
return destWarehouse;
|
||||
}
|
||||
|
||||
StorageRackDto elevatorWarehouse = await ElevatorUtil.GetUsableElevatorChannel(erpTask.Id, FloorEnum.FirstFloor);
|
||||
return elevatorWarehouse;
|
||||
}
|
||||
|
||||
private ERPTaskDto CreateErpTask(PDAInParam pdaInParam, MateriaInfoDto materiaInfoDto)
|
||||
{
|
||||
ERPTaskDto eRPTaskDto = new()
|
||||
{
|
||||
StorageRackNo = pdaInParam.StorageRackNo,
|
||||
OrderNo = materiaInfoDto.OrderNo,
|
||||
BarCode = materiaInfoDto.BarCode,
|
||||
MateriaName = materiaInfoDto.MateriaName,
|
||||
ErpOrderNum = pdaInParam.TaskType == TaskTypeEnum.PdaIn ? int.Parse(materiaInfoDto.MateriaNum) : pdaInParam.MertialNum,
|
||||
MateriaBatch = materiaInfoDto.MateriaBatch,
|
||||
MateriaCode = materiaInfoDto.MateriaCode,
|
||||
|
||||
TaskStatus = TaskStatusEnum.Initial,
|
||||
TaskType = pdaInParam.TaskType,
|
||||
Floor = pdaInParam.Floor
|
||||
};
|
||||
|
||||
return eRPTaskDto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.IService;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using JSMachine.WMS.Infrastructure;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static JSMachine.WMS.App.Dto.StorageRackDto;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 完全混放策略
|
||||
/// </summary>
|
||||
public class CompleteMixingStrategy : IStockInStrategy
|
||||
{
|
||||
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
|
||||
private FloorEnum _floor;
|
||||
private TransportEnum _startArea;
|
||||
private string _materiaBatch;
|
||||
|
||||
public CompleteMixingStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
|
||||
{
|
||||
_startArea = startArea;
|
||||
_materiaBatch = materiaBatch;
|
||||
_floor = floor;
|
||||
}
|
||||
|
||||
public async Task<StorageRackDto> CalCulateWarehouse()
|
||||
{
|
||||
//如果某一列有一个库位被锁定,说明该列有未完成的任务(任务完成会解锁库位),因此该列不可以再生成新的任务
|
||||
|
||||
//获取楼层的所有库位
|
||||
List<StorageRackDto> wareHouses = await StorageRackService.GetListByExpression(p =>
|
||||
p.Floor == _floor && p.Transport == TransportEnum.NormalArea);
|
||||
|
||||
if (wareHouses.IsNullOrEmpty())
|
||||
{
|
||||
LogHelper.Warn($"{_floor} 层没有空余库位");
|
||||
return null;
|
||||
}
|
||||
|
||||
wareHouses = wareHouses.OrderBy(p => p.AreaNumSort).ToList();
|
||||
|
||||
IEnumerable<IGrouping<long, StorageRackDto>> groupByColumn = wareHouses.GroupBy(p => p.ReservoirAreaColumnId);
|
||||
|
||||
StorageRackDto destStorageRack = null;
|
||||
foreach (var columnGroup in groupByColumn)
|
||||
{
|
||||
bool emptyColumn = columnGroup.All(p => !p.IsLock && !p.IsDisabled && string.IsNullOrEmpty(p.BarCode));
|
||||
if (emptyColumn)
|
||||
{
|
||||
destStorageRack = columnGroup.OrderByDescending(p => p.NumSort).First();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return destStorageRack;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 混合模式策略。同批次的优先放同一列,如果没有库位则混放
|
||||
/// </summary>
|
||||
public class MixedModeStrategy : IStockInStrategy
|
||||
{
|
||||
private FloorEnum _floor;
|
||||
private TransportEnum _startArea;
|
||||
private string _materiaBatch;
|
||||
public MixedModeStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
|
||||
{
|
||||
_floor = floor;
|
||||
_startArea = startArea;
|
||||
_materiaBatch = materiaBatch;
|
||||
}
|
||||
|
||||
public async Task<StorageRackDto> CalCulateWarehouse()
|
||||
{
|
||||
StockInByExist stockInByExist = new();
|
||||
StockInByNew stockInByNew = new();
|
||||
StockInByMixing stockInByMixing = new();
|
||||
|
||||
stockInByExist.SetNextHandler(stockInByNew);
|
||||
stockInByNew.SetNextHandler(stockInByMixing);
|
||||
|
||||
return await stockInByExist.CalculateWarehouse(_startArea, _floor, _materiaBatch, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.IService;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain;
|
||||
using JSMachine.WMS.Infrastructure;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static JSMachine.WMS.App.Dto.StorageRackDto;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 不混放策略
|
||||
/// </summary>
|
||||
public class NoMixingStrategy : IStockInStrategy
|
||||
{
|
||||
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
|
||||
private FloorEnum _floor;
|
||||
private TransportEnum _startArea;
|
||||
private string _materiaBatch;
|
||||
public NoMixingStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
|
||||
{
|
||||
_floor = floor;
|
||||
_startArea = startArea;
|
||||
_materiaBatch = materiaBatch;
|
||||
}
|
||||
|
||||
public async Task<StorageRackDto> CalCulateWarehouse()
|
||||
{
|
||||
StockInByExist stockInByExist = new();
|
||||
StockInByNew stockInByNew = new();
|
||||
|
||||
stockInByExist.SetNextHandler(stockInByNew);
|
||||
|
||||
return await stockInByExist.CalculateWarehouse(_startArea, _floor, _materiaBatch, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JSMachine.WMS.Common;
|
||||
using JSMachine.WMS.Infrastructure;
|
||||
using JSMachine.WMS.App.IService;
|
||||
|
||||
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据楼层和配置的混放模式选择入库库位计算策略。
|
||||
/// </summary>
|
||||
public class StrategyFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// 0-完全不混放 1-混合模式,优先同批次放一列,没有空库位再混放 2-完全混放模式,不用批次货物放一起
|
||||
/// </summary>
|
||||
/// <param name="startArea">入库起始区域。</param>
|
||||
/// <param name="floor">目标楼层。</param>
|
||||
/// <param name="materiaBatch">物料批次。</param>
|
||||
/// <returns>与当前入库条件匹配的库位计算策略。</returns>
|
||||
public static IStockInStrategy CreateStockInStrategy(TransportEnum startArea, FloorEnum floor, string materiaBatch)
|
||||
{
|
||||
switch (floor == FloorEnum.FirstFloor ? 0 : Global.AppSettings.PlcaementMod)
|
||||
{
|
||||
case 0:
|
||||
return new NoMixingStrategy(startArea, floor, materiaBatch);
|
||||
case 1:
|
||||
return new MixedModeStrategy(startArea, floor, materiaBatch);
|
||||
case 2:
|
||||
return new CompleteMixingStrategy(startArea, floor, materiaBatch);
|
||||
default:
|
||||
return new NoMixingStrategy(startArea, floor, materiaBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user