You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
6.8 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
public class TaskExecuteStrategy : ITaskExecuteStrategy
{
private readonly ILiveTaskQueueService _taskQueueService;
private readonly ILiveTaskDetailService _taskDetailService;
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,
IHikRoBotService hikRobotService,
IHoistApiService hoistService)
{
_taskQueueService = taskQueueService;
_taskDetailService = taskDetailService;
_hikRobotService = hikRobotService;
_hoistService = hoistService;
}
public Task ExecuteAsync(CancellationToken cancellationToken = default)
{
return Task.WhenAll(
ProcessCategory(1, cancellationToken), // 包材
ProcessCategory(2, cancellationToken), // 成品
ProcessCategory(3, cancellationToken) // 托盘
);
}
/// <summary>
/// 按任务类别轮询待执行任务,同一类别内多个任务并行处理
/// </summary>
private async Task ProcessCategory(int taskCategory, CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
try
{
var pendingTasks = _taskQueueService.Query(x =>
x.taskStatus == 1 && x.taskCategory == taskCategory);
if (pendingTasks.Count > 0)
{
await Parallel.ForEachAsync(pendingTasks, ct, async (task, innerCt) =>
{
await ProcessTask(task, innerCt);
});
}
}
catch (Exception ex)
{
Console.WriteLine($"任务类别 {taskCategory} 调度异常: {ex.Message}");
}
await Task.Delay(1000, ct);
}
}
/// <summary>
/// 处理单个任务:按顺序遍历明细步骤,依次下发到对应设备
/// </summary>
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),
_ => Task.FromResult(false) // ConveyorLine 暂不处理
};
}
/// <summary>
/// 下发 AGV 任务,通过 HikRoBotApiAGV 多车可并发)
/// </summary>
private async Task<bool> DispatchToAgv(LiveTaskDetail detail)
{
await _agvSemaphore.WaitAsync();
try
{
var dto = new GenAgvSchedulingTaskDto
{
reqCode = detail.taskCode,
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", 2 => "1", _ => "0" },
wbCode = detail.startPoint,
ctnrCode = detail.palletBarcode,
materialLot = detail.materialBarcode,
positionCodePath = new List<Position>
{
new() { positionCode = detail.startPoint, type = "00" },
new() { positionCode = detail.endPoint, type = "00" }
}
};
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>
/// 下发提升机任务,通过 HoistApi每台提升机串行
/// </summary>
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 = hoistCode,
taskCode = detail.taskCode,
startPoint = int.TryParse(detail.startPoint, out var s) ? s : 0,
endPoint = int.TryParse(detail.endPoint, out var e) ? e : 0
};
var result = _hoistService.HoistTaskExecutor(dto);
if (result.code.Equals(HoistStatusEnum.))
{
detail.taskStatus = 2;
_taskDetailService.Update(detail);
return true;
}
Console.WriteLine($"提升机 {hoistCode} 任务 {detail.taskCode} 下发失败: [{result.code}] {result.msg}");
return false;
}
finally
{
semaphore.Release();
}
}
}