first commit

This commit is contained in:
2026-09-02 16:31:50 +08:00
commit a461727193
911 changed files with 692450 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Job.Enum
{
/// <summary>
/// 作业组(不同的系统应使用不同的作业组)
/// </summary>
public enum JobGroup
{
/// <summary>
/// 进纸系统作业组
/// </summary>
PaperFeedGroup,
/// <summary>
/// 备纸系统作业组
/// </summary>
PaperPreparationGroup,
/// <summary>
/// 退纸系统作业组
/// </summary>
PaperReturnGroup,
/// <summary>
/// 原纸入库系统作业组
/// </summary>
PaperStorageGroup
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Job.Enum
{
/// <summary>
/// 作业调度类型
/// </summary>
public enum ScheduleType
{
/// <summary>
/// 简单模式
/// </summary>
Simple,
/// <summary>
/// cron表达式
/// </summary>
Corn,
/// <summary>
/// 日历
/// </summary>
Calendar,
/// <summary>
/// XML配置文件
/// </summary>
XMLConfig,
/// <summary>
/// JSON配置文件
/// </summary>
JsonConfig,
}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PlatformTarget>x64</PlatformTarget>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Event\**" />
<EmbeddedResource Remove="Event\**" />
<None Remove="Event\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Prism.Core" Version="8.1.97" />
<PackageReference Include="Quartz" Version="3.3.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\JSMachine.WMS.Business\JSMachine.WMS.Business.csproj" />
<ProjectReference Include="..\JSMachine.WMS.Common\JSMachine.WMS.Common.csproj" />
<ProjectReference Include="..\JSMachine.WMS.Netty\JSMachine.WMS.Netty.csproj" />
<ProjectReference Include="..\JSMachine.WMS.PLC\JSMachine.WMS.PLC.csproj" />
<ProjectReference Include="..\JSMachine.WMS.RPC\JSMachine.WMS.RPC.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,88 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using JSMachine.WMS.RPC.ErpRPC.Dto.Out;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using JSMachine.WMS.RPC.RcsRPC.IService;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(AgvTaskJob)
, Group = nameof(AgvTaskJob) + "_Group"
, Description = "Agv任务作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 1000
, RepeatCount = 0
, StartTimeStr = null)]
/// <summary>
/// 周期扫描待执行的 AGV 任务,并向 RCS 下发任务。
/// 通过禁止并发执行,避免同一作业周期内重复领取待执行任务。
/// </summary>
public class AgvTaskJob : IJob
{
private static readonly IAGVTaskService AgvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IRcsService IRcsService = GlobalServericeProvidor.GetService<IRcsService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
/// <summary>
/// 获取未锁定的初始任务,成功下发后更新任务状态;RCS 失败时保留初始状态,
/// 由下一次调度继续尝试。
/// </summary>
/// <param name="context">Quartz 作业执行上下文。</param>
public async Task Execute(IJobExecutionContext context)
{
AGVTaskDto agvTask = await AgvTaskService.GetSingalByExpression(p =>
(p.TaskStatus == TaskStatusEnum.Initial) &&
!p.IsLock);
if (agvTask == null)
return;
StorageRackDto startWareHouse = await StorageRackService.GetByStorageRackNo(agvTask.StartPositionCode);
CreateTaskParam createTaskParam = new()
{
//从提升机出口取货和搬运要使用不同的模板
modelProcessCode = startWareHouse.Transport == TransportEnum.Elevator ? "RK" : "cattleCarry4",
orderId = agvTask.Id.ToString(),
taskOrderDetail = new TaskOrderDetailDto
{
taskPath = $"{agvTask.StartPositionCode},{agvTask.EndPositionCode}"
}
};
// 只有 RCS 返回成功后才推进本地状态,防止本地记录与外部执行状态不一致。
ApiReturnResult<bool> apiRet = await IRcsService.CreateTask(createTaskParam);
if (apiRet.ResultCode == "200")
{
LogHelper.Info($"向Rcs下发Agv任务 {agvTask.Id} 成功,起点 {agvTask.StartPositionCode} 终点 {agvTask.EndPositionCode}");
agvTask.TaskStatus = TaskStatusEnum.InExecution;
bool bRet = await AgvTaskService.EditSingal(agvTask);
if (!bRet)
{
LogHelper.Error($"更新AgvTask {agvTask.Id} 状态失败,稍后将重试一次");
await AgvTaskService.EditSingal(agvTask);
}
}
else
{
LogHelper.Error($"向Rcs下发Agv任务 {agvTask.Id} 失败,起点 {agvTask.StartPositionCode} 终点 {agvTask.EndPositionCode},原因 {apiRet.ErrorMsg}");
}
}
}
}
@@ -0,0 +1,56 @@
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Application.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(DataCleanJob)
, Group = nameof(DataCleanJob) + "_Group"
, Description = "过期数据清理任务"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 1000 * 3600 * 24
, RepeatCount = 0
, StartTimeStr = null)]
/// <summary>
/// 定期清理过期 ERP、AGV、操作日志、提升机计数和日志文件,避免历史数据无限增长。
/// </summary>
public class DataCleanJob : IJob
{
private static readonly IERPTaskService ERPTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IAGVTaskService AgvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IOperationLogService OperationLogService = GlobalServericeProvidor.GetService<IOperationLogService>();
private static readonly IElevatorCountService ElevatorCountService = GlobalServericeProvidor.GetService<IElevatorCountService>();
/// <summary>执行过期业务数据和日志文件清理。</summary>
/// <param name="context">Quartz 作业上下文。</param>
public async Task Execute(IJobExecutionContext context)
{
await ERPTaskService.DeleByExpression(p => p.CreationTime < DateTime.Now.AddMonths(-12));
await AgvTaskService.DeleByExpression(p => p.CreationTime < DateTime.Now.AddMonths(-12));
await OperationLogService.DeleByExpression(p => p.CreationTime < DateTime.Now.AddMonths(-12));
await ElevatorCountService.DeleByExpression(p=> p.CreationTime < DateTime.Now.AddDays(-10));
DirectoryInfo dir = new(Path.Combine($"{AppDomain.CurrentDomain.BaseDirectory}", "logs"));
if (dir.Exists)
{
FileSystemInfo[] fileInfos = dir.GetFileSystemInfos();
foreach (var fileInfo in fileInfos)
{
if (fileInfo.CreationTime <= DateTime.Now.AddMonths(-12))
fileInfo.Delete();
}
}
}
}
}
+220
View File
@@ -0,0 +1,220 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.App.ServiceImpl;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using NLog;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(ElevatorJob)
, Group = nameof(ElevatorJob) + "_Group"
, Description = "提升机监控作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 500
, RepeatCount = 0
, StartTimeStr = null)]
/// <summary>
/// 周期读取提升机 PLC 状态,依据边沿信号处理取货、队列和计数业务。
/// </summary>
public class ElevatorJob : IJob
{
private static readonly IElevatorPlcQueueService ElevatorPlcQueueService = GlobalServericeProvidor.GetService<IElevatorPlcQueueService>();
private static readonly IAGVTaskService AGVTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private static readonly IElevatorCountService ElevatorCountService = GlobalServericeProvidor.GetService<IElevatorCountService>();
/// <summary>
/// 1号提升机1号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorOneChannelOneAllowPickUp = false;
/// <summary>
/// 1号提升机2号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorOneChannelTwAollowPickUp = false;
/// <summary>
/// 2号提升机1号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorTwoChannelOneAllowPickUp = false;
/// <summary>
/// 2号提升机2号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorTwoChannelTwoAllowPickUp = false;
/// <summary>
/// 执行一次提升机状态轮询,并处理新产生的取货信号。
/// </summary>
/// <param name="context">Quartz 作业上下文。</param>
public async Task Execute(IJobExecutionContext context)
{
List<ElevatorPlcStatus> elevatorPlcStatuses = await ElevatorStatusRead.ReadAllElevatorStatus();
if (elevatorPlcStatuses.IsNullOrEmpty())
{
LogHelper.Warn($"入库:读取提升机PLC信号失败");
return;
}
ElevatorPlcStatus elevatorStatusOne = elevatorPlcStatuses.First(p => p.ElevatorNo == 1);
ElevatorPlcStatus elevatorStatusTwo = elevatorPlcStatuses.First(p => p.ElevatorNo == 2);
if (!PreElevatorOneChannelOneAllowPickUp && elevatorStatusOne.StockInPickupChannelOne)
{
LogHelper.Warn("入库:1号提升机通道2014可以取货");
_ = ElevatorCountService.IncrementCount(1, 2014);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 1 && p.ElevatorChannel == 1011,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"入库:1号提升机通道2014可以取货,但是PLC队列没有信息");
}
PreElevatorOneChannelOneAllowPickUp = elevatorStatusOne.StockInPickupChannelOne;
return;
}
else
{
PreElevatorOneChannelOneAllowPickUp = elevatorStatusOne.StockInPickupChannelOne;
}
if (!PreElevatorOneChannelTwAollowPickUp && elevatorStatusOne.StockInPickupChannelTwo)
{
LogHelper.Warn("入库:1号提升机通道2004可以取货");
_ = ElevatorCountService.IncrementCount(1, 2004);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 1 && p.ElevatorChannel == 1001,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"入库:1号提升机通道2004可以取货,但是PLC队列没有信息");
}
PreElevatorOneChannelTwAollowPickUp = elevatorStatusOne.StockInPickupChannelTwo;
return;
}
else
{
PreElevatorOneChannelTwAollowPickUp = elevatorStatusOne.StockInPickupChannelTwo;
}
if (!PreElevatorTwoChannelOneAllowPickUp && elevatorStatusTwo.StockInPickupChannelOne)
{
LogHelper.Warn("入库:2号提升机通道2014可以取货");
_ = ElevatorCountService.IncrementCount(2, 2014);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 2 && p.ElevatorChannel == 1011,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"入库:2号提升机通道2014可以取货,但是PLC队列没有信息");
}
PreElevatorTwoChannelOneAllowPickUp = elevatorStatusTwo.StockInPickupChannelOne;
return;
}
else
{
PreElevatorTwoChannelOneAllowPickUp = elevatorStatusTwo.StockInPickupChannelOne;
}
if (!PreElevatorTwoChannelTwoAllowPickUp && elevatorStatusTwo.StockInPickupChannelTwo)
{
LogHelper.Warn("入库:2号提升机通道2004可以取货");
_ = ElevatorCountService.IncrementCount(2, 2004);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 2 && p.ElevatorChannel == 1001,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"入库:2号提升机通道2004可以取货,但是PLC队列没有信息");
}
PreElevatorTwoChannelTwoAllowPickUp = elevatorStatusTwo.StockInPickupChannelTwo;
return;
}
else
{
PreElevatorTwoChannelTwoAllowPickUp = elevatorStatusTwo.StockInPickupChannelTwo;
}
}
private async Task HandlePickup(ElevatorPlcQueueDto elevatorQue)
{
AGVTaskDto agvTask = await AGVTaskService.GetSingalByExpression(p => p.Id == elevatorQue.AgvTaskId);
if (agvTask == null)
{
LogHelper.Error($"从PLC队列中获取的 AgvTask {elevatorQue.AgvTaskId} 不存在");
return;
}
StorageRackDto startWarehouseSecondFloor = await StorageRackService
.GetElevatorWareHouse(elevatorQue.ElevatorNo, elevatorQue.ElevatorChannel == 1001 ? 2004 : 2014);
StockInSecondFloorCache.StockInSecondFloorQue.Enqueue(new AllowPickUpInfo
{
AgvTask = agvTask,
ElevatorPlcQueue = elevatorQue,
StartWarehouseSecondFloor = startWarehouseSecondFloor
});
}
}
}
@@ -0,0 +1,257 @@
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.ServiceImpl;
using JSMachine.WMS.Business.StockOut.IService;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read;
using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model;
using Newtonsoft.Json;
using Quartz;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(ElevatorOutJob)
, Group = nameof(ElevatorOutJob) + "_Group"
, Description = "提升机监控作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 500
, RepeatCount = 0
, StartTimeStr = null)]
public class ElevatorOutJob : IJob
{
private static readonly IElevatorPlcQueueService ElevatorPlcQueueService = GlobalServericeProvidor.GetService<IElevatorPlcQueueService>();
private static readonly ICommonService CommonService = GlobalServericeProvidor.GetService<ICommonService>();
private static readonly IERPTaskService ERPTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IAGVTaskService AGVTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private static readonly IERPTaskService ErpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IStockOutService StockOutService= GlobalServericeProvidor.GetService<IStockOutService>();
private static readonly IElevatorCountService ElevatorCountService = GlobalServericeProvidor.GetService<IElevatorCountService>();
/// <summary>
/// 1号提升机1号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorOneChannelOneAllowPickUp = false;
/// <summary>
/// 1号提升机2号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorOneChannelTwAollowPickUp = false;
/// <summary>
/// 2号提升机1号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorTwoChannelOneAllowPickUp = false;
/// <summary>
/// 2号提升机2号取货通道前一个允许取货状态
/// </summary>
private static bool PreElevatorTwoChannelTwoAllowPickUp = false;
public async Task Execute(IJobExecutionContext context)
{
List<ElevatorPlcStatus> elevatorPlcStatuses = await ElevatorStatusRead.ReadAllElevatorStatus();
if (elevatorPlcStatuses.IsNullOrEmpty())
{
LogHelper.Warn($"出库:读取提升机PLC信号失败");
return;
}
ElevatorPlcStatus elevatorStatusOne = elevatorPlcStatuses.First(p => p.ElevatorNo == 1);
ElevatorPlcStatus elevatorStatusTwo = elevatorPlcStatuses.First(p => p.ElevatorNo == 2);
if (!PreElevatorOneChannelOneAllowPickUp && elevatorStatusOne.StockOutPickupChanelOne)
{
LogHelper.Info($"出库:1号提升机1016出库通道可以取货");
_ = ElevatorCountService.IncrementCount(1, 1016);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 1 && p.ElevatorChannel == 2011,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"出库:1号提升机1016出库通道可以取货,但是PLC队列没有信息");
}
PreElevatorOneChannelOneAllowPickUp = elevatorStatusOne.StockOutPickupChanelOne;
return;
}
else
{
PreElevatorOneChannelOneAllowPickUp = elevatorStatusOne.StockOutPickupChanelOne;
}
if (!PreElevatorOneChannelTwAollowPickUp && elevatorStatusOne.StockOutPickupChanelTwo)
{
LogHelper.Info($"出库:1号提升机1006号出库通道可以取货");
_ = ElevatorCountService.IncrementCount(1, 1006);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 1 && p.ElevatorChannel == 2001,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"出库:一号提升机1006号出库通道可以取货,但是PLC队列没有信息");
}
PreElevatorOneChannelTwAollowPickUp = elevatorStatusOne.StockOutPickupChanelTwo;
return;
}
else
{
PreElevatorOneChannelTwAollowPickUp = elevatorStatusOne.StockOutPickupChanelTwo;
}
if (!PreElevatorTwoChannelOneAllowPickUp && elevatorStatusTwo.StockOutPickupChanelOne)
{
LogHelper.Info($"出库:2号提升机1016出库通道可以取货");
_ = ElevatorCountService.IncrementCount(2, 1016);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 2 && p.ElevatorChannel == 2011,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"出库:二号提升机1016出库通道可以取货,但是PLC队列没有信息");
}
PreElevatorTwoChannelOneAllowPickUp = elevatorStatusTwo.StockOutPickupChanelOne;
return;
}
else
{
PreElevatorTwoChannelOneAllowPickUp = elevatorStatusTwo.StockOutPickupChanelOne;
}
if (!PreElevatorTwoChannelTwoAllowPickUp && elevatorStatusTwo.StockOutPickupChanelTwo)
{
LogHelper.Info($"出库:二号提升机1006出库通道可以取货");
_ = ElevatorCountService.IncrementCount(2, 1006);
List<ElevatorPlcQueueDto> elevatorQueue = await ElevatorPlcQueueService
.GetListByExpression(p =>
p.ElevatorNo == 2 && p.ElevatorChannel == 2001,
nameof(ElevatorPlcQueueDto.CreationTime),
SqlSugar.OrderByType.Asc);
if (!elevatorQueue.IsNullOrEmpty())
{
ElevatorPlcQueueDto destQueue = elevatorQueue.First();
await HandlePickup(destQueue);
}
else
{
LogHelper.Warn($"出库:二号提升机1006出库通道可以取货,但是PLC队列没有信息");
}
PreElevatorTwoChannelTwoAllowPickUp = elevatorStatusTwo.StockOutPickupChanelTwo;
return;
}
else
{
PreElevatorTwoChannelTwoAllowPickUp = elevatorStatusTwo.StockOutPickupChanelTwo;
}
}
/// <summary>
/// 收到取货完成信号
/// 根据二楼的提升机队列,生成一楼的AgvTask
/// 生成一楼的agvTask
/// </summary>
/// <param name="elevatorQue"></param>
/// <returns></returns>
private async Task HandlePickup(ElevatorPlcQueueDto elevatorQue)
{
AGVTaskDto agvTaskSecondFloor = await AGVTaskService.GetSingalByExpression(p => p.Id == elevatorQue.AgvTaskId);
if (agvTaskSecondFloor == null)
{
LogHelper.Error($"AgvTask {elevatorQue.AgvTaskId} 不存在");
return;
}
//获取起点 提升机库位信息
StorageRackDto startWarehouseFirstFloor = await StorageRackService.GetElevatorWareHouse(
elevatorQue.ElevatorNo, elevatorQue.ElevatorChannel == 2001 ? 1006 : 1016);
//计算终点出库码头
StorageRackDto destWarehouseFirstFloor = await StockOutService.CalculateDestWarehouse_SaleOut(agvTaskSecondFloor.Transport);
if (destWarehouseFirstFloor == null)
{
LogHelper.Warn($"一楼没有可用的库位,AgvTask {agvTaskSecondFloor.Id} 对应的一楼任务无法生成");
_ = ErpTaskService.UpdateTaskMsg(agvTaskSecondFloor.ErpTaskId, "一楼库位不足,请确保码头无货后在pda上点击清空码头按钮");
return;
}
ERPTaskDto erpTask = await ERPTaskService.GetSingalByExpression(p => p.Id == agvTaskSecondFloor.ErpTaskId);
if (erpTask == null)
{
LogHelper.Error($"出库关联的ERP任务 {agvTaskSecondFloor.ErpTaskId} 不存在");
return;
}
startWarehouseFirstFloor.BingdingMaterial(new StorageRackDto.MateriaInfo
{
BarCode = agvTaskSecondFloor.BarCode,
MateriaBatch = agvTaskSecondFloor.MateriaBatch,
MateriaName = agvTaskSecondFloor.MateriaName,
MateriaType = agvTaskSecondFloor.MateriaType,
MateriaCode = agvTaskSecondFloor.MateriaCode,
MateriaNum = agvTaskSecondFloor.MateriaNum.ToString(),
OrderNo = agvTaskSecondFloor.OrderNo
});
await CommonService.CreateAndInsertAgvTask(new Business.Common.Models.AgvTaskCreationParam
{
StartPosition = startWarehouseFirstFloor,
EndPosition = destWarehouseFirstFloor,
ErpTaskId = erpTask.Id,
StockOutWharf = erpTask.Transport,
TaskType = erpTask.TaskType
});
bool bRetTwo = await ElevatorPlcQueueService.DeleteById(elevatorQue.Id);
bool bRetThree = await StorageRackService.Lock(destWarehouseFirstFloor);
LogHelper.Info($"出库任务已生成,提升机队列被删除,队列原始信息 {JsonConvert.SerializeObject(elevatorQue)}");
}
}
}
+148
View File
@@ -0,0 +1,148 @@
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.StockOut.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using Quartz;
using SqlSugar;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(ErpTaskJob)
, Group = nameof(ErpTaskJob) + "_Group"
, Description = "Erp任务作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 2000
, RepeatCount = 0
, StartTimeStr = null)]
public class ErpTaskJob : IJob
{
private static readonly IERPTaskService ERPTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IStockInService IStockInService = GlobalServericeProvidor.GetService<IStockInService>();
private static readonly IStockOutService IStockOutService = GlobalServericeProvidor.GetService<IStockOutService>();
private static readonly IAGVTaskService AGVTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
public async Task Execute(IJobExecutionContext context)
{
//1.读取状态为初始化的erptask
//2.任务类型为入库
//2.1 计算目标库位
//2.2 目标库位为1楼,直接生成AgvTask(起点为入库提交的库位,终点为目标库位)
//2.3 目标库位为2楼,生成两个Agv任务:TaskOne(起点为入库提交的库位,终点一楼提升机入口) TaskTwo(起点为二楼提升机出口,终点为目标库位)
//3.任务类型为出库
//3.1 物料在1楼,直接生成AgvTask(起点为物料所在的库位,终点为出货码头)
//3.2 物料在2楼,生成两个Agv任务:TaskOne(起点为物料所在的库位,终点为二楼提升机入口) TaskTwo(起点到一楼提升机出口,终点为出货码头)
ERPTaskDto erpTaskStockIn = await ERPTaskService
.GetSingalByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
p.TaskStatus == TaskStatusEnum.Initial &&
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn),
p => p.CreationTime,
OrderByType.Asc);
List<ERPTaskDto> erpTaskStockOuts = await ERPTaskService
.GetListByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
(p.TaskStatus == TaskStatusEnum.Initial || p.TaskStatus == TaskStatusEnum.InExecution) &&
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut)
);
if (!erpTaskStockOuts.IsNullOrEmpty())
{
foreach (var erpTaskStockOut in erpTaskStockOuts)
{
await CreateAgvTask_SaleOut(erpTaskStockOut);
Thread.Sleep(2000);
}
}
if (erpTaskStockIn != null)
{
await CreateAgvTask_StockIn(erpTaskStockIn);
}
}
private async Task CreateAgvTask_StockIn(ERPTaskDto erpTask)
{
int taskNum = await AGVTaskService.Count(p =>
p.CreationTime >= DateTime.Now.Date &&
p.Floor == FloorEnum.FirstFloor &&
(p.TaskStatus == TaskStatusEnum.InExecution) &&
p.TaskType == TaskTypeEnum.PdaIn);
//一楼只有4个agv小车,因此当待完成的任务已经有4个以上的时候不再生成新的任务
if (taskNum >= 4)
{
await ERPTaskService.UpdateTaskMsg(erpTask.Id, "当前所有小车都有任务,等待空余车辆");
return;
}
bool bRet = await IStockInService.CreateAgvTaskFirstFloor(erpTask);
if (bRet)
{
erpTask.TaskStatus = TaskStatusEnum.InExecution;
erpTask.TaskMsg = "开始生成子任务";
erpTask.LastModifiedTime = DateTime.Now;
await ERPTaskService.EditSingalWithSpecificCols(erpTask, ["TaskStatus", "TaskMsg", "LastModifiedTime"]);
}
}
/// <summary>
/// 销售出库,生成agvTask
/// </summary>
/// <param name="erpTask"></param>
/// <returns></returns>
public async Task CreateAgvTask_SaleOut(ERPTaskDto erpTask)
{
//查询ErpTask关联的所有agvTask(二楼),
//agvTask出库总数量,是否大于等于erpTask出库数量
//是 ErpTask状态完成
//否 生成一个agvTask
var sumNum = 0; //已生成的数量
//不论agv任务什么状态,除了取消的任务,其余的都应该扣减,否则会导致少扣减,实际多出库(就算任务执行失败,后续通过PDA报警,人工处理也为成功状态)
List<AGVTaskDto> agvTaskList = await AGVTaskService.GetListByExpression(p =>
p.CreationTime >= DateTime.Now.AddDays(-1) &&
p.ErpTaskId == erpTask.Id &&
p.TaskType == erpTask.TaskType &&
p.TaskStatus != TaskStatusEnum.Cancelled);
if (!agvTaskList.IsNullOrEmpty())
{
var groupAgvTaskList = agvTaskList.GroupBy(p => p.BarCode);
foreach (var groupAgvTask in groupAgvTaskList)
{
sumNum += groupAgvTask.FirstOrDefault().MateriaNum;
}
}
//需求数量<=已生成数量 ErpTask状态改为结束
if (
((erpTask.ErpOrderNum - erpTask.ErpCancelNum) <= sumNum) ||
((erpTask.ErpOrderNum - erpTask.ErpCancelNum) == erpTask.AlreadyUploadNum)
)
{
erpTask.TaskStatus = TaskStatusEnum.Completed;
await ERPTaskService.UpdateTaskStatus(erpTask);
}
else
{
bool result = await IStockOutService.CreateAgvTaskSecondFloor_SaleOut(erpTask);
if (result)
{
erpTask.TaskStatus = TaskStatusEnum.InExecution;
await ERPTaskService.UpdateTaskStatus(erpTask);
}
}
}
}
}
@@ -0,0 +1,75 @@
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using JSMachine.WMS.RPC.RcsRPC.Dto;
using JSMachine.WMS.RPC.RcsRPC.Dto.In;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Business.RcsCallBack.IService;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(RcsCallbackJob)
, Group = nameof(RcsCallbackJob) + "_Group"
, Description = "rcs回调作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 1000
, RepeatCount = 0
, StartTimeStr = null)]
public class RcsCallbackJob : IJob
{
private static readonly IAGVTaskService AgvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IRcsCallBackService RcsCallBackService = GlobalServericeProvidor.GetService<IRcsCallBackService>();
public async Task Execute(IJobExecutionContext context)
{
if (RcsCallbackCache.RcsCallbackQueue.TryDequeue(out AgvExcutionInfo agvExcutionInfo))
{
AGVTaskDto agvTask = await AgvTaskService.GetSingalByExpression(p => p.Id == new Guid(agvExcutionInfo.OrderId));
if (agvTask == null)
{
LogHelper.Warn($"agv上报的任务 {agvExcutionInfo.OrderId} 不存在");
return;
}
string msg = $"agv上报的任务 ({agvExcutionInfo.OrderId}) 状态已经处理,属于重复上传,不再动作";
if (agvExcutionInfo.Status == 7 && agvTask.TaskStatus == TaskStatusEnum.Failed)
{
LogHelper.Info(msg);
return;
}
if (agvExcutionInfo.Status == 3 && agvTask.TaskStatus == TaskStatusEnum.Cancelled)
{
LogHelper.Info(msg);
return;
}
if (agvExcutionInfo.Status == 21 && agvTask.TaskStatus == TaskStatusEnum.PickupCompleted)
{
LogHelper.Info(msg);
return;
}
if (agvExcutionInfo.Status == 23 && agvTask.TaskStatus == TaskStatusEnum.Completed)
{
LogHelper.Info(msg);
return;
}
await RcsCallBackService.DealwithExcutionStatus(agvExcutionInfo, agvTask);
}
}
}
}
@@ -0,0 +1,82 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.Common.IService;
using JSMachine.WMS.Business.Common.Models;
using JSMachine.WMS.Business.StockIn.IService;
using JSMachine.WMS.Business.StockIn.ServiceImpl.Strategy;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using Newtonsoft.Json;
using Quartz;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(StockInSecondFloorJob)
, Group = nameof(StockInSecondFloorJob) + "_Group"
, Description = "入库二楼任务生成作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 2000
, RepeatCount = 0
, StartTimeStr = null)]
public class StockInSecondFloorJob : IJob
{
private static readonly ICommonService CommonService = GlobalServericeProvidor.GetService<ICommonService>();
private static readonly IAGVTaskService AgvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
private static readonly IERPTaskService ErpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IElevatorPlcQueueService ElevatorPlcQueueService = GlobalServericeProvidor.GetService<IElevatorPlcQueueService>();
public async Task Execute(IJobExecutionContext context)
{
if (StockInSecondFloorCache.StockInSecondFloorQue.TryDequeue(out AllowPickUpInfo allowPickUpInfo))
{
//二楼提升机出口可以取货,生成二楼agv任务
IStockInStrategy stockInStrategy = StrategyFactory.CreateStockInStrategy(
TransportEnum.Elevator,
FloorEnum.SecondFloor,
allowPickUpInfo.AgvTask.MateriaBatch);
StorageRackDto destWarehouseSecondFloor = await stockInStrategy.CalCulateWarehouse();
if (destWarehouseSecondFloor == null)
{
LogHelper.Warn($"二楼暂无可用的库位,AgvTask {allowPickUpInfo.AgvTask.Id} 对应的二楼任务暂无法生成");
//队列再写回去,没有库位的时候队列再写回去,此时整理出空余库位后任务会继续生成(可考虑给pda弹窗报警)
StockInSecondFloorCache.StockInSecondFloorQue.Enqueue(allowPickUpInfo);
return;
}
ERPTaskDto erpTask = await ErpTaskService.GetSingalByExpression(p => p.Id == allowPickUpInfo.AgvTask.ErpTaskId);
allowPickUpInfo.StartWarehouseSecondFloor.BingdingMaterial(new App.Dto.StorageRackDto.MateriaInfo
{
BarCode = allowPickUpInfo.AgvTask.BarCode,
MateriaBatch = allowPickUpInfo.AgvTask.MateriaBatch,
MateriaName = allowPickUpInfo.AgvTask.MateriaName,
MateriaType = allowPickUpInfo.AgvTask.MateriaType,
MateriaCode = allowPickUpInfo.AgvTask.MateriaCode,
MateriaNum = allowPickUpInfo.AgvTask.MateriaNum.ToString(),
OrderNo = allowPickUpInfo.AgvTask.OrderNo
});
await CommonService.CreateAndInsertAgvTask(new AgvTaskCreationParam()
{
StartPosition = allowPickUpInfo.StartWarehouseSecondFloor,
EndPosition = destWarehouseSecondFloor,
ErpTaskId = erpTask.Id,
TaskType = destWarehouseSecondFloor.Transport == TransportEnum.NormalArea ? erpTask.TaskType : TaskTypeEnum.TransferStockIn,
ErpUploadStatus = (erpTask.TaskType == TaskTypeEnum.TailIn || destWarehouseSecondFloor.Transport != TransportEnum.NormalArea) ?
ErpUploadStatusEnum.Unnecessary : ErpUploadStatusEnum.WaittingToUpload
});
await ElevatorPlcQueueService.DeleteById(allowPickUpInfo.ElevatorPlcQueue.Id);
LogHelper.Info($"入库任务已生成,提升机队列被删除,队列原始信息 {JsonConvert.SerializeObject(allowPickUpInfo.ElevatorPlcQueue)}");
}
}
}
}
@@ -0,0 +1,87 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Business.Common.IService;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Job.Job
{
/// <summary>
/// 中转区域调度作业
/// </summary>
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(TransferAreaJob)
, Group = nameof(TransferAreaJob) + "_Group"
, Description = "中转区域作业调度"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 2000
, RepeatCount = 0
, StartTimeStr = null)]
public class TransferAreaJob : IJob
{
private static readonly IERPTaskService ERPTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IAGVTaskService AgvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly ICommonService CommonService = GlobalServericeProvidor.GetService<ICommonService>();
private static readonly IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
public async Task Execute(IJobExecutionContext context)
{
//1.查询关联的erp任务类型
//2.如果是出库,且任务批次和当前物料批次一致(这种情况是出库没有可用的提升机库位,从而暂时运到中转区),则该任务需要去往提升机出库
//3.除了2以外的所有情况,均搬运到存储区
StorageRackDto storageRack = await StorageRackService.GetSingalByExpression(p =>
p.Transport == TransportEnum.TransitArea &&
!p.IsLock &&
!p.IsDisabled &&
!string.IsNullOrEmpty(p.BarCode));
if (storageRack == null)
{
return;
}
AGVTaskDto lastAgvTaskRelated = await AgvTaskService.GetSingalByExpression(
p => p.EndPositionCode == storageRack.StorageRackNo,
p => p.CreationTime,
SqlSugar.OrderByType.Desc
);
if (lastAgvTaskRelated==null)
{
LogHelper.Error($"中转区业务 未查询到物料 {storageRack.BarCode} 上一次agv任务,无法调度该物料,需要手动处理");
return;
}
ERPTaskDto erpTask = await ERPTaskService.GetSingalByExpression(p => p.Id == lastAgvTaskRelated.ErpTaskId);
if(erpTask==null)
{
LogHelper.Error($"中转区业务 未查询到物料 {storageRack.BarCode} 关联的erp任务,无法调度该物料,需要手动处理");
return;
}
bool bRet = await CommonService.CreateAgvTaskFromTransferArea(storageRack, lastAgvTaskRelated, erpTask);
if(!bRet)
{
_ = ERPTaskService.UpdateTaskMsg(erpTask.Id, "中转区作业调度中,存储区已满或者无可用提升机库位,调度失败");
LogHelper.Error("中转区作业调度中,存储区已满或者无可用提升机库位,调度失败");
}
else
{
_ = ERPTaskService.UpdateTaskMsg(erpTask.Id, "");
}
}
}
}
@@ -0,0 +1,344 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using JSMachine.WMS.RPC.ErpRPC.Dto.In;
using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult;
using JSMachine.WMS.RPC.ErpRPC.Dto.Out.XRBusinessResult;
using JSMachine.WMS.RPC.ErpRPC.IService;
using Quartz;
using SqlSugar;
namespace JSMachine.WMS.Job.Job
{
[DisallowConcurrentExecution]
[PersistJobDataAfterExecution]
[Job(Name = nameof(XinRongErpJob)
, Group = nameof(XinRongErpJob) + "_Group"
, Description = "新荣Erp数据上传作业"
, ScheduleType = ScheduleType.Simple
, IntervalMilliSeconds = 2000
, RepeatCount = 0
, StartTimeStr = null)]
public class XinRongErpJob : IJob
{
private static readonly IERPTaskService ErpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
private static readonly IAGVTaskService AGVTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
private static readonly IPaperStorageService ErpService = GlobalServericeProvidor.GetService<IPaperStorageService>();
protected static readonly int erpUploadBefore = Global.AppSettings.ErpConfig.ErpUploadBefore;
protected static readonly int retryNum = Global.AppSettings.ErpConfig.RetryNum;
public async Task Execute(IJobExecutionContext context)
{
List<AGVTaskDto> agvTaskList = await AGVTaskService.GetListByExpression(p =>
p.CreationTime >= DateTime.Now.AddDays(erpUploadBefore) &&
(p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TransferStockIn || p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut) &&
(p.ErpUploadStatus == ErpUploadStatusEnum.WaittingToUpload || p.ErpUploadStatus == ErpUploadStatusEnum.UploadFailed) &&
p.TaskStatus == TaskStatusEnum.Completed &&
p.RetryNum < retryNum);
if (agvTaskList.Count <= 0)
{
return;
}
agvTaskList = agvTaskList.OrderBy(p => p.CreationTime).ToList();
foreach (AGVTaskDto agvTask in agvTaskList)
{
ERPTaskDto erpTask = await ErpTaskService.GetSingalByExpression(p => p.Id == agvTask.ErpTaskId);
if (erpTask == null)
{
return;
}
//已上传给Erp的数量
var alreadyUploadNum = erpTask.AlreadyUploadNum;
switch (agvTask.TaskType)
{
case TaskTypeEnum.MachineHandIn:
break;
case TaskTypeEnum.PdaIn:
await UploadDataIn(agvTask, erpTask);
break;
case TaskTypeEnum.TransferStockIn:
await UploadDataIn(agvTask, erpTask);
break;
case TaskTypeEnum.SaleOut:
await UploadDataSaleOut(agvTask, erpTask, alreadyUploadNum);
break;
case TaskTypeEnum.ProductionOut:
await UploadDataProductionOut(agvTask, erpTask, alreadyUploadNum);
break;
default:
break;
}
}
}
/// <summary>
/// 生产退库数据上传
/// </summary>
/// <param name="agvTask"></param>
/// <param name="erpTask"></param>
/// <param name="alreadyUploadNum"></param>
/// <returns></returns>
private static async Task UploadDataProductionOut(AGVTaskDto agvTask, ERPTaskDto erpTask, int alreadyUploadNum)
{
//if (alreadyUploadNum < (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
//{
// if (alreadyUploadNum + agvTask.MateriaNum <= (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
// {
// var param = new ErpProduceOutParam
// {
// produceReturnCode = erpTask.ProduceReturnCode,
// barCode = erpTask.BarCode,
// materiaName = erpTask.MateriaName,
// materiaBatch = erpTask.MateriaBatch,
// materiaCode = erpTask.MateriaCode,
// num = agvTask.MateriaNum
// };
// XRResultDto erpReulst = await ErpService.AddProduceOut(param);
// await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
// }
// else
// {
// var param = new ErpProduceOutParam
// {
// produceReturnCode = erpTask.ProduceReturnCode,
// barCode = erpTask.BarCode,
// materiaName = erpTask.MateriaName,
// materiaBatch = erpTask.MateriaBatch,
// materiaCode = erpTask.MateriaCode,
// num = erpTask.ErpOrderNum - alreadyUploadNum
// };
// XRResultDto erpReulst = await ErpService.AddProduceOut(param);
// await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
// }
//}
//else
//{
// agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadSuccess;
// agvTask.LastModifiedTime = DateTime.Now;
// agvTask.ErpReturnMsg = "已上传数量等于订单数";
// agvTask.RetryNum = retryNum;
// await AGVTaskService.EditSingal(agvTask);
// erpTask.AlreadyUploadNum = erpTask.ErpOrderNum;
// erpTask.TaskStatus = TaskStatusEnum.Completed;
// await ErpTaskService.EditSingal(erpTask);
//}
if (alreadyUploadNum < (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
{
var param = new ErpProduceOutParam
{
produceReturnCode = erpTask.ProduceReturnCode,
barCode = agvTask.BarCode,
materiaName = agvTask.MateriaName,
materiaBatch = agvTask.MateriaBatch,
materiaCode = agvTask.MateriaCode,
num = agvTask.MateriaNum
};
XRResultDto erpReulst = await ErpService.AddProduceOut(param);
await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
}
else
{
if (alreadyUploadNum > erpTask.ErpOrderNum - erpTask.ErpCancelNum)
{
agvTask.ErpReturnMsg = "请注意:已上传数量大于订单数";
}
agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadSuccess;
agvTask.LastModifiedTime = DateTime.Now;
agvTask.RetryNum = retryNum;
await AGVTaskService.EditSingal(agvTask);
erpTask.AlreadyUploadNum = alreadyUploadNum;
erpTask.TaskStatus = TaskStatusEnum.Completed;
await ErpTaskService.EditSingal(erpTask);
}
}
/// <summary>
/// 销售出库数据上传
/// </summary>
/// <param name="agvTask"></param>
/// <param name="erpTask"></param>
/// <param name="alreadyUploadNum"></param>
/// <returns></returns>
private static async Task UploadDataSaleOut(AGVTaskDto agvTask, ERPTaskDto erpTask, int alreadyUploadNum)
{
//if (alreadyUploadNum < (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
//{
// if (alreadyUploadNum + agvTask.MateriaNum <= (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
// {
// var param = new ErpSaleOutParam
// {
// saleNo = erpTask.SaleNo,
// barCode = agvTask.BarCode,
// materiaCode = agvTask.MateriaCode,
// materiaBatch = agvTask.MateriaBatch,
// num = agvTask.MateriaNum
// };
// XRResultDto erpReulst = await ErpService.AddSaleOut(param);
// await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
// }
// else
// {
// var param = new ErpSaleOutParam
// {
// saleNo = erpTask.SaleNo,
// barCode = agvTask.BarCode,
// materiaCode = agvTask.MateriaCode,
// materiaBatch = agvTask.MateriaBatch,
// num = erpTask.ErpOrderNum - alreadyUploadNum
// };
// XRResultDto erpReulst = await ErpService.AddSaleOut(param);
// await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
// }
//}
//else
//{
// agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadSuccess;
// agvTask.LastModifiedTime = DateTime.Now;
// agvTask.ErpReturnMsg = "已上传数量等于订单数";
// agvTask.RetryNum = retryNum;
// await AGVTaskService.EditSingal(agvTask);
// erpTask.AlreadyUploadNum = erpTask.ErpOrderNum;
// erpTask.TaskStatus = TaskStatusEnum.Completed;
// await ErpTaskService.EditSingal(erpTask);
//}
if (alreadyUploadNum < (erpTask.ErpOrderNum - erpTask.ErpCancelNum))
{
var param = new StockOutParam
{
saleNo = erpTask.SaleNo,
barCode = agvTask.BarCode,
materiaCode = agvTask.MateriaCode,
materiaBatch = agvTask.MateriaBatch,
num = agvTask.MateriaNum
};
XRResultDto erpReulst = await ErpService.AddSaleOut(param);
await HandErpUploadResult(agvTask, erpTask, erpReulst, param.num);
}
else
{
if (alreadyUploadNum > erpTask.ErpOrderNum - erpTask.ErpCancelNum)
{
agvTask.ErpReturnMsg = "请注意:已上传数量大于订单数";
}
agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadSuccess;
agvTask.LastModifiedTime = DateTime.Now;
agvTask.RetryNum = retryNum;
await AGVTaskService.EditSingal(agvTask);
erpTask.AlreadyUploadNum = alreadyUploadNum;
erpTask.TaskStatus = TaskStatusEnum.Completed;
await ErpTaskService.EditSingal(erpTask);
}
}
/// <summary>
/// Pda入库数据上传
/// </summary>
/// <param name="agvTask"></param>
/// <returns></returns>
private static async Task UploadDataIn(AGVTaskDto agvTask, ERPTaskDto erpTask)
{
//入库:一个合格证号,只能成功上传一次Erp
//var agvTaskList = await AGVTaskService.GetListByExpression(p => p.BarCode == agvTask.BarCode && p.ErpUploadStatus == ErpUploadStatusEnum.UploadSuccess);
//if (!agvTaskList.IsNullOrEmpty())
//{
// LogHelper.Error($"合格证:{agvTask.BarCode},AgvTask任务Id:{agvTask.Id} 入库信息,已经上传到Erp,不能重复上传!");
//}
//else
{
var param = new ErpProduceInParam
{
barCode = agvTask.BarCode,
materiaName = agvTask.MateriaName,
materiaBatch = agvTask.MateriaBatch,
materiaCode = agvTask.MateriaCode,
materiaNum = agvTask.MateriaNum
};
XRResultDto erpReulst = await ErpService.AddProduceIn(param);
await HandErpUploadResult(agvTask, erpTask, erpReulst, param.materiaNum);
}
}
/// <summary>
/// 处理调用Erp接口,后Wms后续业务
/// </summary>
/// <param name="agvTask"></param>
/// <param name="erpTask"></param>
/// <param name="erpReulst"></param>
/// <param name="num"></param>
/// <returns></returns>
private static async Task HandErpUploadResult(AGVTaskDto agvTask, ERPTaskDto erpTask, XRResultDto erpReulst, int num)
{
if (erpReulst.success)
{
agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadSuccess;
agvTask.LastModifiedTime = DateTime.Now;
agvTask.ErpReturnMsg = erpReulst.message;
agvTask.RetryNum += 1;
bool bRet = await AGVTaskService.EditSingal(agvTask);
if (!bRet)
{
agvTask.RetryNum++;
LogHelper.Error($"更新ID为{agvTask.Id}的agvTask的Erp上传状态失败");
}
else
{
erpTask.AlreadyUploadNum += num;
erpTask.LastModifiedTime = DateTime.Now;
await ErpTaskService.EditSingalWithSpecificCols(erpTask, ["AlreadyUploadNum", "LastModifiedTime"]);
}
}
//处理网络不通或超时
else if (erpReulst == null)
{
agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadFailed;
agvTask.LastModifiedTime = DateTime.Now;
agvTask.ErpReturnMsg = "访问Erp连接超时";
agvTask.RetryNum = 0;
bool bRet = await AGVTaskService.EditSingal(agvTask);
if (!bRet)
{
LogHelper.Error($"更新ID为{agvTask.Id}的agvTask的Erp上传状态失败");
}
}
else
{
agvTask.ErpUploadStatus = ErpUploadStatusEnum.UploadFailed;
agvTask.LastModifiedTime = DateTime.Now;
agvTask.ErpReturnMsg = erpReulst.errors;
agvTask.RetryNum += 1;
bool bRet = await AGVTaskService.EditSingal(agvTask);
if (!bRet)
{
agvTask.RetryNum++;
LogHelper.Error($"更新ID为{agvTask.Id}的agvTask的Erp上传状态失败");
}
}
}
}
}
@@ -0,0 +1,86 @@
using JSMachine.WMS.Job.Enum;
namespace JSMachine.WMS.Job.JobAttributes
{
/// <summary>
/// Quartz 作业注册特性,描述作业名称、分组、触发方式和执行周期。
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class JobAttribute : Attribute
{
// 默认配置
/// <summary>创建使用默认调度参数的作业特性。</summary>
public JobAttribute()
{
ScheduleType = ScheduleType.Simple;
Name = "MyJob";
Group = "MyGroup";
Description = "NoJobAttributeAndDefault";
StartTimeStr = null;
RepeatCount = 0;
IntervalMilliSeconds = 5000;
}
/// <summary>
/// Job的特性
/// </summary>
/// <param name="modeEnum">Job的Trigger的创建模式</param>
/// <param name="name">JobKeyName</param>
/// <param name="group">JobKeyGroup</param>
/// <param name="decription">JobDecription</param>
/// <param name="dateTimeOffset">Trigger开始时间</param>
/// <param name="repeatCount">重复次数(null为Forever)</param>
/// <param name="intervaMilliSeconds">间隔时间(毫秒)</param>
/// <summary>使用指定调度参数创建作业特性。</summary>
public JobAttribute(ScheduleType scheduleType
, string name
, string group
, string decription
, string dateTimeOffset
, int repeatCount
, int intervaMilliSeconds)
{
ScheduleType = scheduleType;
Name = name;
Description = decription;
Group = group;
StartTimeStr = dateTimeOffset;
RepeatCount = repeatCount;
IntervalMilliSeconds = intervaMilliSeconds;
}
/// <summary>
/// Job的Trigger的创建模式
/// Simple、Corn、Calendar、XMLConfig、JsonConfig
/// </summary>
public ScheduleType ScheduleType { get; set; }
/// <summary>
/// JobKeyName
/// </summary>
public string Name { get; set; }
/// <summary>
/// JobKeyGroup
/// </summary>
public string Group { get; set; }
/// <summary>
/// JobDescription
/// </summary>
public string Description { get; set; }
/// <summary>
/// 开始时间
/// 可转为时间的字符串,标准格式为"yyyy-MM-dd HH:mm:ss.fff"
/// null或string.Empty为立即开始
/// </summary>
public string StartTimeStr { get; set; }
/// <summary>
/// 重复次数,0为总是重复
/// </summary>
public int RepeatCount { get; set; }
/// <summary>
/// 任务执行间隔
/// </summary>
public int IntervalMilliSeconds { get; set; }
}
}
@@ -0,0 +1,14 @@
using JSMachine.WMS.Infrastructure.Enums;
namespace JSMachine.WMS.Job.JobAttributes
{
[AttributeUsage(AttributeTargets.Class)]
public class ScannerSceneAttribute : Attribute
{
public ScannerScene ScannerScene { get; set; }
public ScannerSceneAttribute(ScannerScene scannerScene)
{
ScannerScene = scannerScene;
}
}
}
+95
View File
@@ -0,0 +1,95 @@
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure.Enums;
using JSMachine.WMS.Job.Enum;
using JSMachine.WMS.Job.JobAttributes;
using Microsoft.AspNetCore.Builder;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using System.Collections.Specialized;
using System.Reflection;
namespace JSMachine.WMS.Job
{
/// <summary>
/// Quartz 作业调度引擎,根据 JobAttribute 自动发现并注册作业。
/// </summary>
public static class JobEngine
{
/// <summary>
/// 启动 Quartz 调度器,并按作业属性创建周期触发器。
/// 作业执行由 Quartz 管理,调用方无需等待单次作业完成。
/// </summary>
public async static void UseAllJobs(this IApplicationBuilder app)
{
List<(Type, JobAttribute)> tuples = ResolveAllJobTypes();
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = await schedulerFactory.GetScheduler();
await scheduler.Start();
// 每个作业独立创建 JobDetail 和 Trigger,避免不同作业共享调度配置。
tuples?.ForEach(async tuple =>
{
IJobDetail jobDetail = JobBuilder
.Create(tuple.Item1)
.WithIdentity(tuple.Item2.Name, tuple.Item2.Group)
.Build();
TriggerBuilder triggerBuilder = TriggerBuilder
.Create()
.WithIdentity($"{tuple.Item2.Name}Trigger", tuple.Item2.Group);
if (tuple.Item2.ScheduleType == ScheduleType.Simple)
{
triggerBuilder.WithSimpleSchedule(p =>
{
p.WithInterval(TimeSpan.FromMilliseconds(tuple.Item2.IntervalMilliSeconds));
if (tuple.Item2.RepeatCount > 0)
{
p.WithRepeatCount(tuple.Item2.RepeatCount);
}
else
{
p.RepeatForever();
}
});
}
ITrigger trigger = triggerBuilder.Build();
await scheduler.ScheduleJob(jobDetail, trigger);
});
}
private static List<(Type, JobAttribute)> ResolveAllJobTypes()
{
Assembly assembly = typeof(JobEngine).Assembly;
//获取所有类
Type[] jobTypes = assembly
.GetTypes()
.Where(t => t.IsDefined(typeof(JobAttribute), false) && typeof(IJob).IsAssignableFrom(t))
.ToArray();
List<(Type, JobAttribute)> tuples = new();
//获取类定义的
foreach (Type jobType in jobTypes)
{
object[] attrs = jobType.GetCustomAttributes(typeof(JobAttribute), false);
if (attrs.Length > 0)
{
foreach (var attr in attrs)
{
if (attr is JobAttribute attribute)
{
tuples.Add((jobType, attribute));
}
}
}
}
return tuples;
}
}
}
+64
View File
@@ -0,0 +1,64 @@
using JSMachine.WMS.Infrastructure.Helper;
using Quartz;
namespace JSMachine.WMS.Job
{
/// <summary>
/// 作业监听器
/// </summary>
public class JobListener : IJobListener
{
/// <summary>
/// Quartz 作业监听器名称。
/// </summary>
public string Name => "JobDurationListener";
/// <summary>
/// 记录因禁止并发执行而被 Quartz 跳过的作业。
/// </summary>
/// <param name="context">Quartz 作业上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>已完成的监听任务。</returns>
public Task JobExecutionVetoed(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
return Task.Run(() =>
{
LogHelper.Info($"{context.JobDetail.Key.Name} actives the JobListener.JobExecutionVetoed method");
});
}
/// <summary>
/// 作业执行前
/// </summary>
/// <param name="context"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task JobToBeExecuted(IJobExecutionContext context, CancellationToken cancellationToken = default)
{
return Task.Run(() =>
{
//LogHelp.Info($"{context.JobDetail.Key.Name} is excuting ......");
});
}
/// <summary>
/// 作业执行后
/// </summary>
/// <param name="context"></param>
/// <param name="jobException"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
{
return Task.Run(() =>
{
if (jobException != null)
{
LogHelper.Error(@$"{context.JobDetail.Key.Name} occurs an error.
Error message is {jobException.InnerException?.InnerException?.Message} .
StackTrace is {jobException.InnerException?.InnerException?.StackTrace}");
}
});
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using JSMachine.WMS.RPC.PmsRPC.Dto.In;
namespace JSMachine.WMS.Job
{
/// <summary>
/// 进程内保存的机台备纸数据及 ACS 记录标识。
/// </summary>
public static class PlateList
{
/// <summary>
/// 各机台最近批次的备纸需求列表。
/// </summary>
public static List<List<PaperNeed>> Plates { get; set; }//记录所有机台下"前十"批的备纸数据
public static Guid SavedAcsId { get; set; }//记录第一个Id
public static List<Guid> AcsIdArray { get; set; }//记录第一个批订单下所有的Id
}
}