FatigueTestBackgroundService.cs 10.3 KB
using MassTransit.Mediator;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Application.Services;
using Rcs.Domain.Entities;
using Rcs.Domain.Repositories;
using TaskStatus = Rcs.Domain.Entities.TaskStatus;

namespace Rcs.Infrastructure.Services;

/// <summary>
/// 疲劳测试后台服务 - 自动随机生成搬运任务
/// 功能:根据配置的地图ID、机器人ID集合和库位ID集合,随机指定空闲车辆
/// 生成有货库位到空货位的搬运任务
/// @author zzy
/// </summary>
public class FatigueTestBackgroundService : BackgroundService
{
    private readonly ILogger<FatigueTestBackgroundService> _logger;
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(5);

    public FatigueTestBackgroundService(
        ILogger<FatigueTestBackgroundService> logger,
        IServiceScopeFactory scopeFactory)
    {
        _logger = logger;
        _scopeFactory = scopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("[FatigueTest] 疲劳测试后台服务已启动");

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await ProcessFatigueTestAsync(stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "[FatigueTest] 处理疲劳测试时发生异常");
            }

            await Task.Delay(_checkInterval, stoppingToken);
        }

        _logger.LogInformation("[FatigueTest] 疲劳测试后台服务已停止");
    }

    /// <summary>
    /// 处理疲劳测试任务生成
    /// </summary>
    private async Task ProcessFatigueTestAsync(CancellationToken cancellationToken)
    {
        using var scope = _scopeFactory.CreateScope();
        var configService = scope.ServiceProvider.GetRequiredService<IFatigueTestConfigService>();

        // 1. 检查是否处于运行状态
        var config = await configService.GetConfigAsync(cancellationToken);
        if (config?.IsRunning != true)
        {
            return;
        }

        // 2. 验证配置完整性
        if (!ValidateConfig(config))
        {
            return;
        }

        // 3. 获取必要的服务
        var robotRepository = scope.ServiceProvider.GetRequiredService<IRobotRepository>();
        var robotTaskRepository = scope.ServiceProvider.GetRequiredService<IRobotTaskRepository>();
        var storageLocationRepository = scope.ServiceProvider.GetRequiredService<IStorageLocationRepository>();
        var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();

        // 4. 获取配置的机器人中空闲的(没有执行中任务的)
        var availableRobots = await GetAvailableRobotsAsync(
            config.RobotIds, 
            robotRepository, 
            robotTaskRepository, 
            cancellationToken);

        if (!availableRobots.Any())
        {
            _logger.LogDebug("[FatigueTest] 没有可用的空闲机器人");
            return;
        }

        // 5. 获取配置的库位
        var locationIds = config.LocationIds
            .Where(id => Guid.TryParse(id, out _))
            .Select(Guid.Parse)
            .ToList();

        if (locationIds.Count < 2)
        {
            _logger.LogWarning("[FatigueTest] 库位数量不足,至少需要2个库位");
            return;
        }

        // 6. 获取库位详情
        var allLocations = new List<StorageLocation>();
        foreach (var locationId in locationIds)
        {
            var location = await storageLocationRepository.GetByIdAsync(locationId, cancellationToken);
            if (location != null && location.IsActive)
            {
                allLocations.Add(location);
            }
        }

        // 7. 分离有货库位和空货位
        var occupiedLocations = allLocations
            .Where(l => l.Status == StorageLocationStatus.Occupied)
            .ToList();
        var emptyLocations = allLocations
            .Where(l => l.Status == StorageLocationStatus.Empty)
            .ToList();

        if (!occupiedLocations.Any() || !emptyLocations.Any())
        {
            _logger.LogDebug("[FatigueTest] 没有足够的货源或空位,有货库位: {OccupiedCount}, 空货位: {EmptyCount}", 
                occupiedLocations.Count, emptyLocations.Count);
            return;
        }

        // 8. 随机匹配生成任务
        var random = new Random();
        var robot = availableRobots[random.Next(availableRobots.Count)];
        
        // 随机选择起始库位(有货)和目标库位(空货)
        var beginLocation = occupiedLocations[random.Next(occupiedLocations.Count)];
        var endLocation = emptyLocations[random.Next(emptyLocations.Count)];

        // 确保起始和目标不是同一个库位
        if (beginLocation.LocationId == endLocation.LocationId)
        {
            _logger.LogDebug("[FatigueTest] 起始库位和目标库位相同,跳过本次任务生成");
            return;
        }

        // 9. 检查库位是否有执行中的任务
        var hasActiveTask = await CheckLocationHasActiveTaskAsync(
            beginLocation.LocationId, 
            endLocation.LocationId, 
            robotTaskRepository, 
            cancellationToken);

        if (hasActiveTask)
        {
            _logger.LogDebug("[FatigueTest] 起始库位或目标库位已有执行中的任务");
            return;
        }

        // 10. 创建搬运任务
        await CreateTransportTaskAsync(
            robot,
            beginLocation,
            endLocation,
            mediator,
            cancellationToken);

        _logger.LogInformation("[FatigueTest] 已生成搬运任务: 机器人={RobotCode}, 起始={BeginLocation}, 目标={EndLocation}",
            robot.RobotCode,
            beginLocation.LocationCode,
            endLocation.LocationCode);

        // 11. 根据配置的间隔等待
        await Task.Delay(config.TaskIntervalMs, cancellationToken);
    }

    /// <summary>
    /// 验证配置是否完整有效
    /// </summary>
    private bool ValidateConfig(FatigueTestConfig config)
    {

        if (!config.RobotIds.Any())
        {
            _logger.LogWarning("[FatigueTest] 配置无效:RobotIds为空");
            return false;
        }

        if (!config.LocationIds.Any())
        {
            _logger.LogWarning("[FatigueTest] 配置无效:LocationIds为空");
            return false;
        }

        return true;
    }

    /// <summary>
    /// 获取可用的空闲机器人(没有执行中任务的)
    /// </summary>
    private async Task<List<Robot>> GetAvailableRobotsAsync(
        List<string> robotIds,
        IRobotRepository robotRepository,
        IRobotTaskRepository robotTaskRepository,
        CancellationToken cancellationToken)
    {
        var availableRobots = new List<Robot>();

        foreach (var robotIdStr in robotIds)
        {
            if (!Guid.TryParse(robotIdStr, out var robotId))
            {
                continue;
            }

            // 获取机器人详情
            var robot = await robotRepository.GetByIdAsync(robotId, cancellationToken);
            if (robot?.Active != true)
            {
                continue;
            }

            // 检查机器人是否有执行中的任务
            var inProgressTasks = await robotTaskRepository.GetnProgressByRobotIdAsync(robotId, cancellationToken);
            if (inProgressTasks.Any())
            {
                _logger.LogDebug("[FatigueTest] 机器人 {RobotCode} 有执行中的任务,跳过", robot.RobotCode);
                continue;
            }

            availableRobots.Add(robot);
        }

        return availableRobots;
    }

    /// <summary>
    /// 检查库位是否有执行中的任务
    /// 检查起始库位或目标库位是否已被其他任务占用
    /// </summary>
    private async Task<bool> CheckLocationHasActiveTaskAsync(
        Guid beginLocationId,
        Guid endLocationId,
        IRobotTaskRepository robotTaskRepository,
        CancellationToken cancellationToken)
    {
        // 获取所有非终态任务(除了Pending、Completed、Cancelled之外都是执行中)
        var allTasks = await robotTaskRepository.GetAllAsync(cancellationToken);
        var activeTasks = allTasks.Where(t => t.Status != TaskStatus.Cancelled && t.Status != TaskStatus.Completed).ToList();

        // 检查起始库位或目标库位是否已被占用
        foreach (var task in activeTasks)
        {
            // 检查起始库位
            if (task.BeginLocationId.HasValue && 
                (task.BeginLocationId.Value == beginLocationId || task.BeginLocationId.Value == endLocationId))
            {
                return true;
            }

            // 检查目标库位
            if (task.EndLocationId.HasValue && 
                (task.EndLocationId.Value == beginLocationId || task.EndLocationId.Value == endLocationId))
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// 创建搬运任务
    /// </summary>
    private async Task CreateTransportTaskAsync(
        Robot robot,
        StorageLocation beginLocation,
        StorageLocation endLocation,
        IMediator mediator,
        CancellationToken cancellationToken)
    {
        var command = new CreateOrUpdateRobotTaskCommand
        {
            TaskCode = $"FATIGUE_{DateTime.Now:yyMMddHHmmss}_{Guid.NewGuid().ToString("N")[..6].ToUpper()}",
            TaskName = $"疲劳测试任务_{DateTime.Now:HHmmss}",
            // RobotId = robot.RobotId.ToString(),
            BeginLocationId = beginLocation.LocationId.ToString(),
            EndLocationId = endLocation.LocationId.ToString(),
            Priority = 50, // 中等优先级
            Status = 1 // Pending
        };

        var client = mediator.CreateRequestClient<CreateOrUpdateRobotTaskCommand>();
        var response = await client.GetResponse<ApiResponse>(command, cancellationToken);

        if (!response.Message.Success)
        {
            throw new InvalidOperationException($"创建任务失败: {response.Message.Message}");
        }
    }
}