change - 调度策略逻辑实现

pull/1/head
WenJY 4 weeks ago
parent 305409e1d1
commit ffa7e93dbe

@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"Bash(dotnet build *)"
]
}
}

@ -1,13 +1,13 @@
namespace Sln.Wcs.Strategy;
/// <summary>
/// 任务执行策略接口 - 从任务队列中取出待执行任务,根据设备类型下发到对应 SDK
/// 任务执行调度策略 - 按任务类别(包材/托盘/成品)并行调度,
/// AGV 阶段并发下发(多车),提升机阶段按设备串行下发
/// </summary>
public interface ITaskExecuteStrategy
{
/// <summary>
/// 执行一轮任务调度:查询待执行任务,按设备类型下发
/// 启动任务调度,持续运行直到 cancellationToken 被取消
/// </summary>
/// <returns>本次下发的任务数量</returns>
Task<int> ExecuteAsync();
Task ExecuteAsync(CancellationToken cancellationToken = default);
}

@ -9,8 +9,8 @@
<ItemGroup>
<ProjectReference Include="..\Sln.Wcs.Model\Sln.Wcs.Model.csproj" />
<ProjectReference Include="..\Sln.Wcs.Repository\Sln.Wcs.Repository.csproj" />
<ProjectReference Include="..\Sln.Wcs.HikRoBotSdk\Sln.Wcs.HikRoBotSdk.csproj" />
<ProjectReference Include="..\Sln.Wcs.HoistSdk\Sln.Wcs.HoistSdk.csproj" />
<ProjectReference Include="..\Sln.Wcs.HikRoBotApi\Sln.Wcs.HikRoBotApi.csproj" />
<ProjectReference Include="..\Sln.Wcs.HoistApi\Sln.Wcs.HoistApi.csproj" />
</ItemGroup>
</Project>

@ -1,111 +1,134 @@
using Sln.Wcs.HikRoBotSdk;
using Sln.Wcs.HikRoBotSdk.Dto.GenAgvSchedulingTask;
using Sln.Wcs.HoistSdk;
using Sln.Wcs.HoistSdk.Dto.HoistTaskExecutor;
using System.Collections.Concurrent;
using Sln.Wcs.HikRoBotApi.Domain.Dto.GenAgvSchedulingTask;
using Sln.Wcs.HikRoBotApi.Enum;
using Sln.Wcs.HikRoBotApi.Service;
using Sln.Wcs.HoistApi.Domain.Dto.HoistTaskExecutor;
using Sln.Wcs.HoistApi.Domain.Enum;
using Sln.Wcs.HoistApi.Service;
using Sln.Wcs.Model.Domain;
using Sln.Wcs.Repository.service;
namespace Sln.Wcs.Strategy;
/// <summary>
/// 任务执行策略 - 轮询数据库中待执行的任务,根据设备类型调用对应 SDK 下发
/// </summary>
public class TaskExecuteStrategy : ITaskExecuteStrategy
{
private readonly ILiveTaskQueueService _taskQueueService;
private readonly ILiveTaskDetailService _taskDetailService;
private readonly IHIKRoBotSdk _hikRobotSdk;
private readonly IHoistSdk _hoistSdk;
private readonly IHikRoBotService _hikRobotService;
private readonly IHoistApiService _hoistService;
// AGV 多车并发,默认最大 10 个并发
private readonly SemaphoreSlim _agvSemaphore = new(10, 10);
// 提升机按设备串行,每台提升机一次只能执行一个任务
private readonly ConcurrentDictionary<string, SemaphoreSlim> _hoistSemaphores = new();
public TaskExecuteStrategy(
ILiveTaskQueueService taskQueueService,
ILiveTaskDetailService taskDetailService,
IHIKRoBotSdk hikRobotSdk,
IHoistSdk hoistSdk)
IHikRoBotService hikRobotService,
IHoistApiService hoistService)
{
_taskQueueService = taskQueueService;
_taskDetailService = taskDetailService;
_hikRobotSdk = hikRobotSdk;
_hoistSdk = hoistSdk;
_hikRobotService = hikRobotService;
_hoistService = hoistService;
}
public Task ExecuteAsync(CancellationToken cancellationToken = default)
{
return Task.WhenAll(
ProcessCategory(1, cancellationToken), // 包材
ProcessCategory(2, cancellationToken), // 成品
ProcessCategory(3, cancellationToken) // 托盘
);
}
/// <summary>
/// 执行一轮任务调度:查询所有待执行任务,按设备类型下发
/// 按任务类别轮询待执行任务,同一类别内多个任务并行处理
/// </summary>
public Task<int> ExecuteAsync()
private async Task ProcessCategory(int taskCategory, CancellationToken ct)
{
return Task.Run(() =>
{
// 1. 查询所有待执行的任务队列task_status = 1
var pendingTasks = _taskQueueService.Query(x => x.taskStatus == 1);
if (pendingTasks.Count == 0)
return 0;
int dispatchedCount = 0;
foreach (var task in pendingTasks)
while (!ct.IsCancellationRequested)
{
try
{
// 2. 查询任务明细
var details = _taskDetailService.Query(x => x.taskCode == task.taskCode);
var pendingTasks = _taskQueueService.Query(x =>
x.taskStatus == 1 && x.taskCategory == taskCategory);
if (details.Count == 0)
continue;
// 3. 取第一个未执行的明细步骤
var currentDetail = details
.Where(x => x.taskStatus == 1)
.OrderBy(x => x.objId)
.FirstOrDefault();
if (currentDetail == null)
continue;
// 4. 根据设备类型下发任务
var success = DispatchByDeviceType(currentDetail);
if (success)
if (pendingTasks.Count > 0)
{
// 5. 更新明细状态为执行中
currentDetail.taskStatus = 2;
_taskDetailService.Update(currentDetail);
// 6. 更新队列状态为执行中
task.taskStatus = 2;
_taskQueueService.Update(task);
dispatchedCount++;
await Parallel.ForEachAsync(pendingTasks, ct, async (task, innerCt) =>
{
await ProcessTask(task, innerCt);
});
}
}
catch (Exception ex)
{
Console.WriteLine($"任务 {task.taskCode} 下发失败: {ex.Message}");
}
Console.WriteLine($"任务类别 {taskCategory} 调度异常: {ex.Message}");
}
return dispatchedCount;
});
await Task.Delay(1000, ct);
}
}
/// <summary>
/// 根据设备类型分发到对应 SDK
/// 处理单个任务:按顺序遍历明细步骤,依次下发到对应设备
/// </summary>
private bool DispatchByDeviceType(LiveTaskDetail detail)
private async Task ProcessTask(LiveTaskQueue task, CancellationToken ct)
{
try
{
var details = _taskDetailService.Query(x =>
x.taskCode == task.taskCode && x.taskStatus == 1)
.OrderBy(x => x.objId)
.ToList();
foreach (var detail in details)
{
if (ct.IsCancellationRequested) return;
var success = await DispatchDetail(detail);
if (!success) break;
}
// 检查是否还有待执行的明细,若无则更新队列状态为执行中
var remainingPending = _taskDetailService.Query(x =>
x.taskCode == task.taskCode && x.taskStatus == 1);
if (remainingPending.Count == 0)
{
task.taskStatus = 2;
_taskQueueService.Update(task);
}
}
catch (Exception ex)
{
Console.WriteLine($"任务 {task.taskCode} 处理异常: {ex.Message}");
}
}
/// <summary>
/// 根据设备类型分发明细到对应设备 API
/// </summary>
private Task<bool> DispatchDetail(LiveTaskDetail detail)
{
return detail.deviceType switch
{
1 => DispatchToAgv(detail),
2 => DispatchToHoist(detail),
_ => throw new NotSupportedException($"不支持的设备类型: {detail.deviceType}")
_ => Task.FromResult(false) // ConveyorLine 暂不处理
};
}
/// <summary>
/// 下发 AGV 任务(海康机器人 SDK
/// 下发 AGV 任务,通过 HikRoBotApiAGV 多车可并发
/// </summary>
private bool DispatchToAgv(LiveTaskDetail detail)
private async Task<bool> DispatchToAgv(LiveTaskDetail detail)
{
await _agvSemaphore.WaitAsync();
try
{
var dto = new GenAgvSchedulingTaskDto
{
@ -113,18 +136,8 @@ public class TaskExecuteStrategy : ITaskExecuteStrategy
reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
clientCode = "WCS",
taskCode = detail.taskCode,
taskTyp = detail.taskType switch
{
1 => "1", // 入库
2 => "2", // 出库
_ => "0"
},
taskMode = detail.taskType switch
{
1 => "2", // 入库 move
2 => "1", // 出库 move
_ => "0"
},
taskTyp = detail.taskType switch { 1 => "1", 2 => "2", _ => "0" },
taskMode = detail.taskType switch { 1 => "2", 2 => "1", _ => "0" },
wbCode = detail.startPoint,
ctnrCode = detail.palletBarcode,
materialLot = detail.materialBarcode,
@ -135,35 +148,56 @@ public class TaskExecuteStrategy : ITaskExecuteStrategy
}
};
var result = _hikRobotSdk.GenAgvSchedulingTask(dto);
return result.code == "0";
var result = _hikRobotService.GetGenAgvSchedulingTask(dto);
if (result.code.Equals(HikRoBotStatusEnum.))
{
detail.taskStatus = 2;
_taskDetailService.Update(detail);
return true;
}
Console.WriteLine($"AGV 任务 {detail.taskCode} 下发失败: [{result.code}] {result.msg}");
return false;
}
finally
{
_agvSemaphore.Release();
}
}
/// <summary>
/// 下发提升机任务Hoist SDK
/// 下发提升机任务,通过 HoistApi每台提升机串行
/// </summary>
private bool DispatchToHoist(LiveTaskDetail detail)
private async Task<bool> DispatchToHoist(LiveTaskDetail detail)
{
var hoistCode = detail.startPoint ?? "default";
var semaphore = _hoistSemaphores.GetOrAdd(hoistCode, _ => new SemaphoreSlim(1, 1));
await semaphore.WaitAsync();
try
{
var dto = new HoistTaskExeDto
{
hoistCode = detail.startPoint,
hoistCode = hoistCode,
taskCode = detail.taskCode,
startPoint = ParseIntPoint(detail.startPoint),
endPoint = ParseIntPoint(detail.endPoint)
startPoint = int.TryParse(detail.startPoint, out var s) ? s : 0,
endPoint = int.TryParse(detail.endPoint, out var e) ? e : 0
};
var result = _hoistSdk.HoistTaskExecutor(dto);
return result.code == "0";
var result = _hoistService.HoistTaskExecutor(dto);
if (result.code.Equals(HoistStatusEnum.))
{
detail.taskStatus = 2;
_taskDetailService.Update(detail);
return true;
}
/// <summary>
/// 将位置字符串转为整数楼层/层号
/// </summary>
private static int ParseIntPoint(string? point)
Console.WriteLine($"提升机 {hoistCode} 任务 {detail.taskCode} 下发失败: [{result.code}] {result.msg}");
return false;
}
finally
{
if (string.IsNullOrEmpty(point))
return 0;
return int.TryParse(point, out var val) ? val : 0;
semaphore.Release();
}
}
}

Loading…
Cancel
Save