ExecuteRobotSubTaskCommandHandler.cs 5.25 KB
using MassTransit;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.Services;
using Rcs.Application.Services.Protocol;
using Rcs.Application.MessageBus.Commands;
using Rcs.Domain.Repositories;
using Rcs.Infrastructure.PathFinding.Services;
using TaskStatus = Rcs.Domain.Entities.TaskStatus;

namespace Rcs.Infrastructure.MessageBus.Handlers.Commands;

/// <summary>
/// 执行子任务命令处理器
/// 处理逻辑与 SubTaskCompletedDomainEventHandler 中前置清理保持一致:
/// 清理 VDA 路径缓存、清空机器人缓存 Path、释放交通控制锁
/// </summary>
public class ExecuteRobotSubTaskCommandHandler : IConsumer<ExecuteRobotSubTaskCommand>
{
    private readonly ILogger<ExecuteRobotSubTaskCommandHandler> _logger;
    private readonly IRobotSubTaskRepository _robotSubTaskRepository;
    private readonly IRobotTaskRepository _robotTaskRepository;
    private readonly IRobotRepository _robotRepository;
    private readonly IRobotCacheService _robotCacheService;
    private readonly AgvPathService _agvPathService;
    private readonly IProtocolServiceFactory _protocolServiceFactory;

    public ExecuteRobotSubTaskCommandHandler(
        ILogger<ExecuteRobotSubTaskCommandHandler> logger,
        IRobotSubTaskRepository robotSubTaskRepository,
        IRobotTaskRepository robotTaskRepository,
        IRobotRepository robotRepository,
        IRobotCacheService robotCacheService,
        AgvPathService agvPathService,
        IProtocolServiceFactory protocolServiceFactory)
    {
        _logger = logger;
        _robotSubTaskRepository = robotSubTaskRepository;
        _robotTaskRepository = robotTaskRepository;
        _robotRepository = robotRepository;
        _robotCacheService = robotCacheService;
        _agvPathService = agvPathService;
        _protocolServiceFactory = protocolServiceFactory;
    }

    public async Task Consume(ConsumeContext<ExecuteRobotSubTaskCommand> context)
    {
        var command = context.Message;
        try
        {
            var subTask = await _robotSubTaskRepository.GetByIdWithDetailsAsync(command.SubTaskId, context.CancellationToken);
            if (subTask == null)
            {
                await context.RespondAsync(ApiResponse.Failed($"未找到子任务ID为 {command.SubTaskId} 的任务"));
                return;
            }

            var robotId = subTask.Task?.RobotId ?? subTask.RobotId ?? Guid.Empty;

            try
            {
                if (robotId != Guid.Empty)
                {
                    var robot = await _robotRepository.GetByIdAsync(robotId, context.CancellationToken);
                    if (robot != null)
                    {
                        _logger.LogInformation("[子任务重新执行] {subTask}清除缓存释放", subTask.Task.TaskCode + "-" + subTask.Sequence);
                        var protocolService = _protocolServiceFactory.GetService(robot);
                        await protocolService.CancelRobotTasksAsync(robot);
                    }
                    else
                    {
                        _logger.LogWarning("[子任务重新执行] 清理VDA路径缓存失败,机器人不存在: RobotId={RobotId}, SubTaskId={SubTaskId}",
                            robotId, command.SubTaskId);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "[子任务重新执行] 清理VDA路径缓存异常: RobotId={RobotId}, SubTaskId={SubTaskId}",
                    robotId, command.SubTaskId);
            }

            // 删除旧子任务,以新SubTaskId重建,确保VDA5050 OrderId唯一性
            // EF Core不允许修改被跟踪实体的主键,因此采用删旧建新策略
            // @author zzy
            var newSubTaskId = Guid.NewGuid();
            var oldSubTaskId = subTask.SubTaskId;

            await _robotSubTaskRepository.DeleteAsync(subTask, context.CancellationToken);
            // await _robotSubTaskRepository.SaveChangesAsync(context.CancellationToken);

            var newSubTask = new Domain.Entities.RobotSubTask
            {
                SubTaskId = newSubTaskId,
                TaskId = subTask.TaskId,
                RobotId = subTask.RobotId,
                BeginNodeId = subTask.BeginNodeId,
                EndNodeId = subTask.EndNodeId,
                Sequence = subTask.Sequence,
                Status = TaskStatus.Pending,
                ExecutionCount = 0,
                CreatedAt = subTask.CreatedAt,
                UpdatedAt = DateTime.Now
            };

            await _robotSubTaskRepository.AddAsync(newSubTask, context.CancellationToken);
            await _robotSubTaskRepository.SaveChangesAsync(context.CancellationToken);

            _logger.LogInformation(
                "[子任务重新执行] 子任务ID已重建: OldSubTaskId={OldSubTaskId}, NewSubTaskId={NewSubTaskId}, TaskId={TaskId}",
                oldSubTaskId, newSubTaskId, subTask.TaskId);

            await context.RespondAsync(ApiResponse.Successful("执行成功"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "执行子任务失败: SubTaskId={SubTaskId}", command.SubTaskId);
            await context.RespondAsync(ApiResponse.Failed(ex.Message));
        }
    }
}