first commit
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.Business.Common.Models;
|
||||
|
||||
namespace JSMachine.WMS.Business.Common.IService
|
||||
{
|
||||
/// <summary>
|
||||
/// WMS 公共业务服务,协调库位、ERP 任务和 AGV 任务之间的调度关系。
|
||||
/// </summary>
|
||||
public interface ICommonService
|
||||
{
|
||||
/// <summary>
|
||||
/// 库位所在列是否被锁定(有待完成的任务)
|
||||
/// </summary>
|
||||
/// <param name="storageRack"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> IsColLock(StorageRackDto storageRack);
|
||||
/// <summary>
|
||||
/// 跟据erp任务获取已经生成的agv任务列表(不包含已经取消的任务)
|
||||
/// </summary>
|
||||
/// <param name="erpTask"></param>
|
||||
/// <returns></returns>
|
||||
Task<List<AGVTaskDto>> GetCreatedAgvTasksFromErpTask(ERPTaskDto erpTask,TaskTypeEnum taskType);
|
||||
/// <summary>
|
||||
/// 生成中转任务。指定起点,终点为中转区
|
||||
/// </summary>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> CreateAgvTaskToTransferArea(StorageRackDto startPosition, TaskTypeEnum taskType, ERPTaskDto erpTask);
|
||||
/// <summary>
|
||||
/// 中转区作业调度 起点为中转区域 终点需计算
|
||||
/// </summary>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> CreateAgvTaskFromTransferArea(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask);
|
||||
/// <summary>
|
||||
/// 生成agv任务并插入数据库
|
||||
/// </summary>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <param name="endPosition"></param>
|
||||
/// <param name="erpTask"></param>
|
||||
/// <returns></returns>
|
||||
Task CreateAndInsertAgvTask(AgvTaskCreationParam agvTaskCreationParam);
|
||||
/// <summary>
|
||||
/// 获取可用提升机库位
|
||||
/// </summary>
|
||||
/// <param name="floor">楼层</param>
|
||||
/// <param name="taskType">任务类型 1-入库 2-出库</param>
|
||||
/// <returns></returns>
|
||||
Task<StorageRackDto> GetUsableElevator(FloorEnum floor, int taskType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
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.Common.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// AGV任务创建参数模型
|
||||
/// </summary>
|
||||
public class AgvTaskCreationParam
|
||||
{
|
||||
/// <summary>
|
||||
/// 起点
|
||||
/// </summary>
|
||||
public StorageRackDto StartPosition { get; set; }
|
||||
/// <summary>
|
||||
/// 终点
|
||||
/// </summary>
|
||||
public StorageRackDto EndPosition { get; set; }
|
||||
/// <summary>
|
||||
/// 关联的erp任务
|
||||
/// </summary>
|
||||
public Guid ErpTaskId { get; set; }
|
||||
/// <summary>
|
||||
/// 任务类型
|
||||
/// </summary>
|
||||
public TaskTypeEnum TaskType { get; set; }
|
||||
/// <summary>
|
||||
/// 最终出库码头(仅针对出库有效)
|
||||
/// </summary>
|
||||
public TransportEnum StockOutWharf { get; set; }
|
||||
/// <summary>
|
||||
/// erp上传状态
|
||||
/// </summary>
|
||||
public ErpUploadStatusEnum ErpUploadStatus { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
using JSMachine.WMS.App.Dto;
|
||||
using JSMachine.WMS.App.Dto.Enum;
|
||||
using JSMachine.WMS.App.IService;
|
||||
using JSMachine.WMS.App.ServiceImpl;
|
||||
using JSMachine.WMS.Business.Common.IService;
|
||||
using JSMachine.WMS.Business.Common.Models;
|
||||
using JSMachine.WMS.Business.SSE;
|
||||
using JSMachine.WMS.Business.StockIn.IService;
|
||||
using JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy;
|
||||
using JSMachine.WMS.Infrastructure.Helper;
|
||||
using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read;
|
||||
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace JSMachine.WMS.Business.Common.ServiceImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// WMS 通用业务服务,协调 ERP 任务、库位和 AGV 任务,
|
||||
/// 负责中转区及提升机相关任务的生成。
|
||||
/// </summary>
|
||||
public class CommonService : ICommonService
|
||||
{
|
||||
private IERPTaskService _erpTaskService;
|
||||
private IStorageRackService _storageRackService;
|
||||
private IAGVTaskService _agvTaskService;
|
||||
public CommonService(
|
||||
IERPTaskService erpTaskService,
|
||||
IStorageRackService storageRackService,
|
||||
IAGVTaskService agvTaskService)
|
||||
{
|
||||
_erpTaskService = erpTaskService;
|
||||
_storageRackService = storageRackService;
|
||||
_agvTaskService = agvTaskService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定库位所在列是否存在锁定库位。
|
||||
/// 该判断通过查询同列全部库位完成,任一库位被锁定即视为整列锁定。
|
||||
/// </summary>
|
||||
/// <param name="storageRack">用于确定库位列的库位信息。</param>
|
||||
/// <returns>同列存在锁定库位时返回 <see langword="true"/>。</returns>
|
||||
public async Task<bool> IsColLock(StorageRackDto storageRack)
|
||||
{
|
||||
List<StorageRackDto> storageRacksThisCol = await _storageRackService
|
||||
.GetListByExpression(p => p.ReservoirAreaColumnId == storageRack.ReservoirAreaColumnId);
|
||||
|
||||
if (storageRacksThisCol.IsNullOrEmpty())
|
||||
return false;
|
||||
|
||||
if (storageRacksThisCol.Any(p => p.IsLock))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 跟据erp任务获取已经生成的agv任务列表(不包含已经取消的任务)
|
||||
/// </summary>
|
||||
/// <param name="materialBatch"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<AGVTaskDto>> GetCreatedAgvTasksFromErpTask(ERPTaskDto erpTask, TaskTypeEnum taskType)
|
||||
{
|
||||
List<AGVTaskDto> agvTasks = await _agvTaskService
|
||||
.GetListByExpression(p => p.ErpTaskId == erpTask.Id &&
|
||||
p.TaskType == taskType &&
|
||||
p.TaskStatus != TaskStatusEnum.Cancelled &&
|
||||
p.TaskStatus != TaskStatusEnum.Failed);
|
||||
|
||||
agvTasks = agvTasks
|
||||
.GroupBy(p => p.BarCode)
|
||||
.Select(p => p.First())
|
||||
.ToList();
|
||||
|
||||
return agvTasks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成到中转区域的任务。指定起点,终点为中转区
|
||||
/// </summary>
|
||||
/// <param name="startPosition"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> CreateAgvTaskToTransferArea(StorageRackDto startPosition, TaskTypeEnum taskType, ERPTaskDto erpTask)
|
||||
{
|
||||
StorageRackDto endPosition = await _storageRackService.GetUsableTransferStorageRack();
|
||||
|
||||
if (endPosition == null)
|
||||
{
|
||||
LogHelper.Info("中转区域库位已满");
|
||||
return false;
|
||||
}
|
||||
|
||||
await CreateAndInsertAgvTask(new AgvTaskCreationParam
|
||||
{
|
||||
StartPosition = startPosition,
|
||||
EndPosition = endPosition,
|
||||
ErpTaskId = erpTask.Id,
|
||||
TaskType = taskType,
|
||||
StockOutWharf = erpTask.Transport,
|
||||
ErpUploadStatus = ErpUploadStatusEnum.Unnecessary
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中转区作业调度 起点为中转区域 终点需计算
|
||||
/// </summary>
|
||||
/// <param name="startPosition">要调度的中转区物料</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> CreateAgvTaskFromTransferArea(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask)
|
||||
{
|
||||
// 中转区任务按出库、入库、清理出库阻塞物的顺序生成;任一环节失败即停止后续调度。
|
||||
bool bRet = await CreateTransferStockOutAgvTask(startPosition, lastAgvTaskRelated, erpTask);
|
||||
if (!bRet)
|
||||
return false;
|
||||
|
||||
bRet = await CreateTransferStockInAgvTask(startPosition, lastAgvTaskRelated, erpTask);
|
||||
if (!bRet)
|
||||
return false;
|
||||
|
||||
bRet = await CreateRemoveStockOutObstructionAgvTask(startPosition, lastAgvTaskRelated, erpTask);
|
||||
if (!bRet)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> CreateTransferStockOutAgvTask(
|
||||
StorageRackDto startPosition,
|
||||
AGVTaskDto lastAgvTaskRelated,
|
||||
ERPTaskDto erpTask)
|
||||
{
|
||||
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.TransferStockOut)
|
||||
return true;
|
||||
|
||||
StorageRackDto elevator = await GetUsableElevator(FloorEnum.SecondFloor, 2);
|
||||
|
||||
if (elevator == null)
|
||||
{
|
||||
LogHelper.Warn($"中转区出库业务 所有提升机出库库位均被WMS或者PLC锁定,erpTask {lastAgvTaskRelated.ErpTaskId} 无法生成AgvTask");
|
||||
return false;
|
||||
}
|
||||
|
||||
await CreateAndInsertAgvTask(new AgvTaskCreationParam
|
||||
{
|
||||
StartPosition = startPosition,
|
||||
EndPosition = elevator,
|
||||
ErpTaskId = erpTask.Id,
|
||||
TaskType = TaskTypeEnum.TransferStockOut,
|
||||
StockOutWharf = erpTask.Transport,
|
||||
ErpUploadStatus = ErpUploadStatusEnum.Unnecessary
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> CreateTransferStockInAgvTask(StorageRackDto startPosition, AGVTaskDto lastAgvTaskRelated, ERPTaskDto erpTask)
|
||||
{
|
||||
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.TransferStockIn)
|
||||
return true;
|
||||
|
||||
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(
|
||||
TransportEnum.TransitArea,
|
||||
FloorEnum.SecondFloor,
|
||||
startPosition.MateriaBatch);
|
||||
|
||||
StorageRackDto endPosition = await stockInStrategy.CalCulateWarehouse();
|
||||
if (endPosition == null)
|
||||
return false;
|
||||
|
||||
await CreateAndInsertAgvTask(new AgvTaskCreationParam
|
||||
{
|
||||
StartPosition = startPosition,
|
||||
EndPosition = endPosition,
|
||||
ErpTaskId = erpTask.Id,
|
||||
TaskType = lastAgvTaskRelated.TaskType,
|
||||
ErpUploadStatus =
|
||||
(erpTask.TaskType == TaskTypeEnum.PdaIn || erpTask.TaskType == TaskTypeEnum.TailIn) ?
|
||||
ErpUploadStatusEnum.WaittingToUpload :
|
||||
ErpUploadStatusEnum.Unnecessary
|
||||
});
|
||||
return true;
|
||||
}
|
||||
private async Task<bool> CreateRemoveStockOutObstructionAgvTask(
|
||||
StorageRackDto startPosition,
|
||||
AGVTaskDto lastAgvTaskRelated,
|
||||
ERPTaskDto erpTask)
|
||||
{
|
||||
if (lastAgvTaskRelated.TaskType != TaskTypeEnum.RemoveStockOutObstruction)
|
||||
return true;
|
||||
|
||||
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(
|
||||
TransportEnum.TransitArea,
|
||||
FloorEnum.SecondFloor,
|
||||
startPosition.MateriaBatch);
|
||||
|
||||
StorageRackDto endPosition = await stockInStrategy.CalCulateWarehouse();
|
||||
|
||||
if (endPosition == null)
|
||||
return false;
|
||||
|
||||
await CreateAndInsertAgvTask(new AgvTaskCreationParam
|
||||
{
|
||||
StartPosition = startPosition,
|
||||
EndPosition = endPosition,
|
||||
ErpTaskId = erpTask.Id,
|
||||
TaskType = lastAgvTaskRelated.TaskType,
|
||||
ErpUploadStatus =
|
||||
(erpTask.TaskType == TaskTypeEnum.PdaIn || erpTask.TaskType == TaskTypeEnum.TailIn) ?
|
||||
ErpUploadStatusEnum.WaittingToUpload :
|
||||
ErpUploadStatusEnum.Unnecessary
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取可用提升机库位
|
||||
/// </summary>
|
||||
/// <param name="floor">楼层</param>
|
||||
/// <param name="taskType">任务类型 1-入库 2-出库</param>
|
||||
/// <returns></returns>
|
||||
public async Task<StorageRackDto> GetUsableElevator(FloorEnum floor, int taskType)
|
||||
{
|
||||
List<StorageRackDto> elevatorWarehouse = await _storageRackService.GetListByExpression(p =>
|
||||
p.Transport == TransportEnum.Elevator &&
|
||||
p.Floor == floor &&
|
||||
!p.IsLock
|
||||
);
|
||||
|
||||
if (elevatorWarehouse.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ElevatorPlcStatus> elevatorPlcStatuses = await ElevatorStatusRead.ReadAllElevatorStatus();
|
||||
|
||||
StorageRackDto elevator = null;
|
||||
foreach (StorageRackDto elvatorWarehouse in elevatorWarehouse)
|
||||
{
|
||||
int elevatorNo = elvatorWarehouse.StorageRackName.Contains("1号") ? 1 : 2;
|
||||
|
||||
ElevatorPlcStatus elevatorPlcStatus = elevatorPlcStatuses.First(p => p.ElevatorNo == elevatorNo);
|
||||
|
||||
//入库
|
||||
if (taskType == 1)
|
||||
{
|
||||
if (elvatorWarehouse.ElevatorChannel == 1001 && elevatorPlcStatus.StockInPutdownChanelTwo ||
|
||||
elvatorWarehouse.ElevatorChannel == 1011 && elevatorPlcStatus.StockInPutdownChanelOne)
|
||||
{
|
||||
elevator = elvatorWarehouse;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//出库
|
||||
if (taskType == 2)
|
||||
{
|
||||
if (elvatorWarehouse.ElevatorChannel == 2001 && elevatorPlcStatus.StockOutPutdownChanelTwo ||
|
||||
elvatorWarehouse.ElevatorChannel == 2011 && elevatorPlcStatus.StockOutPutdownChanelOne)
|
||||
{
|
||||
elevator = elvatorWarehouse;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elevator;
|
||||
}
|
||||
|
||||
public async Task CreateAndInsertAgvTask(AgvTaskCreationParam agvTaskCreationParam)
|
||||
{
|
||||
AGVTaskDto agvTask = new()
|
||||
{
|
||||
ErpTaskId = agvTaskCreationParam.ErpTaskId,
|
||||
BarCode = agvTaskCreationParam.StartPosition.BarCode,
|
||||
OrderNo = agvTaskCreationParam.StartPosition.OrderNo,
|
||||
CustomName = agvTaskCreationParam.StartPosition.CustomName,
|
||||
MateriaName = agvTaskCreationParam.StartPosition.MateriaName,
|
||||
MateriaType = agvTaskCreationParam.StartPosition.MateriaType,
|
||||
MateriaNum = agvTaskCreationParam.StartPosition.MateriaNum,
|
||||
MateriaBatch = agvTaskCreationParam.StartPosition.MateriaBatch,
|
||||
MateriaCode = agvTaskCreationParam.StartPosition.MateriaCode,
|
||||
TaskType = agvTaskCreationParam.TaskType,
|
||||
TaskStatus = TaskStatusEnum.Initial,
|
||||
Priority = agvTaskCreationParam.TaskType == TaskTypeEnum.SaleOut ? PriorityEnum.High : PriorityEnum.Middle,
|
||||
Floor = agvTaskCreationParam.StartPosition.Floor,
|
||||
|
||||
StartPositionCode = agvTaskCreationParam.StartPosition.StorageRackNo,
|
||||
EndPositionCode = agvTaskCreationParam.EndPosition.StorageRackNo,
|
||||
|
||||
Transport = agvTaskCreationParam.StockOutWharf,
|
||||
|
||||
ErpUploadStatus = agvTaskCreationParam.ErpUploadStatus
|
||||
};
|
||||
|
||||
await _storageRackService.Lock(agvTaskCreationParam.StartPosition);
|
||||
await _storageRackService.Lock(agvTaskCreationParam.EndPosition);
|
||||
await _agvTaskService.Add(agvTask);
|
||||
|
||||
//发送广播通知前端网站刷新页面
|
||||
SseEngine.BrodCast(agvTaskCreationParam.StartPosition.Floor.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user