FatigueTestController.cs 10.4 KB
using Microsoft.AspNetCore.Mvc;
using Rcs.Application.Common;
using Rcs.Application.Services;
using StatusCodes = Microsoft.AspNetCore.Http.StatusCodes;

namespace Rcs.Api.Controllers;

/// <summary>
/// Fatigue test controller.
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class FatigueTestController : ControllerBase
{
    private readonly ILogger<FatigueTestController> _logger;
    private readonly IFatigueTestConfigService _configService;

    public FatigueTestController(
        ILogger<FatigueTestController> logger,
        IFatigueTestConfigService configService)
    {
        _logger = logger;
        _configService = configService;
    }

    /// <summary>
    /// Get all fatigue test configs.
    /// </summary>
    [HttpGet("config")]
    public async Task<IActionResult> GetConfig(CancellationToken cancellationToken = default)
    {
        try
        {
            var configs = await _configService.GetConfigsAsync(cancellationToken);
            var dtoList = configs.Select(MapToConfigDto).ToList();
            return Ok(ApiResponse.Successful(dtoList));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to get config list");
            return Ok(ApiResponse.Failed($"Failed to get configs: {ex.Message}"));
        }
    }

    /// <summary>
    /// Create or update one fatigue test config.
    /// </summary>
    [HttpPost("config")]
    public async Task<IActionResult> SaveConfig([FromBody] SaveFatigueTestConfigDto dto, CancellationToken cancellationToken = default)
    {
        try
        {
            if (!string.IsNullOrWhiteSpace(dto.ConfigId) && !Guid.TryParse(dto.ConfigId, out _))
            {
                return Ok(ApiResponse.Failed("Invalid configId", StatusCodes.Status400BadRequest));
            }

            if (dto.RobotIds == null || !dto.RobotIds.Any())
            {
                return Ok(ApiResponse.Failed("RobotIds cannot be empty", StatusCodes.Status400BadRequest));
            }

            if (dto.LocationIds == null || dto.LocationIds.Count < 2)
            {
                return Ok(ApiResponse.Failed("At least 2 location ids are required", StatusCodes.Status400BadRequest));
            }

            var validRobotIds = dto.RobotIds
                .Where(id => !string.IsNullOrWhiteSpace(id) && Guid.TryParse(id, out _))
                .Distinct()
                .ToList();

            var validLocationIds = dto.LocationIds
                .Where(id => !string.IsNullOrWhiteSpace(id) && Guid.TryParse(id, out _))
                .Distinct()
                .ToList();

            if (!validRobotIds.Any())
            {
                return Ok(ApiResponse.Failed("No valid robot ids", StatusCodes.Status400BadRequest));
            }

            if (validLocationIds.Count < 2)
            {
                return Ok(ApiResponse.Failed("No enough valid location ids", StatusCodes.Status400BadRequest));
            }

            var config = new FatigueTestConfig
            {
                ConfigId = dto.ConfigId?.Trim() ?? string.Empty,
                MapId = dto.MapId?.Trim() ?? string.Empty,
                RobotIds = validRobotIds,
                LocationIds = validLocationIds,
                TaskIntervalMs = Math.Max(100, Math.Min(60000, dto.TaskIntervalMs))
            };

            var saved = await _configService.UpsertConfigAsync(config, cancellationToken);
            if (saved == null)
            {
                return Ok(ApiResponse.Failed("Failed to save config", StatusCodes.Status500InternalServerError));
            }

            _logger.LogInformation(
                "[FatigueTest] Config saved: ConfigId={ConfigId}, Robots={RobotCount}, Locations={LocationCount}",
                saved.ConfigId,
                saved.RobotIds.Count,
                saved.LocationIds.Count);

            return Ok(ApiResponse.Successful(MapToConfigDto(saved)));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to save config");
            return Ok(ApiResponse.Failed($"Failed to save config: {ex.Message}"));
        }
    }

    /// <summary>
    /// Delete one fatigue test config.
    /// </summary>
    [HttpDelete("config/{configId}")]
    public async Task<IActionResult> DeleteConfig(string configId, CancellationToken cancellationToken = default)
    {
        try
        {
            if (!Guid.TryParse(configId, out _))
            {
                return Ok(ApiResponse.Failed("Invalid configId", StatusCodes.Status400BadRequest));
            }

            var result = await _configService.DeleteConfigAsync(configId, cancellationToken);
            if (!result)
            {
                return Ok(ApiResponse.Failed("Delete failed or config not found", StatusCodes.Status404NotFound));
            }

            _logger.LogInformation("[FatigueTest] Config deleted: ConfigId={ConfigId}", configId);
            return Ok(ApiResponse.Successful("Config deleted"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to delete config");
            return Ok(ApiResponse.Failed($"Failed to delete config: {ex.Message}"));
        }
    }

    /// <summary>
    /// Start one fatigue test config.
    /// </summary>
    [HttpPost("start/{configId}")]
    public async Task<IActionResult> Start(string configId, CancellationToken cancellationToken = default)
    {
        try
        {
            if (!Guid.TryParse(configId, out _))
            {
                return Ok(ApiResponse.Failed("Invalid configId", StatusCodes.Status400BadRequest));
            }

            var result = await _configService.StartAsync(configId, cancellationToken);
            if (!result)
            {
                return Ok(ApiResponse.Failed("Start failed or config not found", StatusCodes.Status404NotFound));
            }

            _logger.LogInformation("[FatigueTest] Started config: ConfigId={ConfigId}", configId);
            return Ok(ApiResponse.Successful("Fatigue test started"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to start config {ConfigId}", configId);
            return Ok(ApiResponse.Failed($"Failed to start: {ex.Message}"));
        }
    }

    /// <summary>
    /// Stop one fatigue test config.
    /// </summary>
    [HttpPost("stop/{configId}")]
    public async Task<IActionResult> Stop(string configId, CancellationToken cancellationToken = default)
    {
        try
        {
            if (!Guid.TryParse(configId, out _))
            {
                return Ok(ApiResponse.Failed("Invalid configId", StatusCodes.Status400BadRequest));
            }

            var result = await _configService.StopAsync(configId, cancellationToken);
            if (!result)
            {
                return Ok(ApiResponse.Failed("Stop failed or config not found", StatusCodes.Status404NotFound));
            }

            _logger.LogInformation("[FatigueTest] Stopped config: ConfigId={ConfigId}", configId);
            return Ok(ApiResponse.Successful("Fatigue test stopped"));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to stop config {ConfigId}", configId);
            return Ok(ApiResponse.Failed($"Failed to stop: {ex.Message}"));
        }
    }

    /// <summary>
    /// Get fatigue test statuses.
    /// </summary>
    [HttpGet("status")]
    public async Task<IActionResult> GetStatus(CancellationToken cancellationToken = default)
    {
        try
        {
            var statuses = await _configService.GetStatusesAsync(cancellationToken);
            var configStatuses = statuses.Select(s => new FatigueTestStatusDto
            {
                ConfigId = s.ConfigId,
                IsRunning = s.IsRunning,
                MapId = s.MapId,
                RobotCount = s.RobotCount,
                LocationCount = s.LocationCount,
                TaskIntervalMs = s.TaskIntervalMs
            }).ToList();

            var dto = new FatigueTestStatusListDto
            {
                Configs = configStatuses,
                TotalCount = configStatuses.Count,
                RunningCount = configStatuses.Count(s => s.IsRunning)
            };

            return Ok(ApiResponse.Successful(dto));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[FatigueTest] Failed to get status");
            return Ok(ApiResponse.Failed($"Failed to get status: {ex.Message}"));
        }
    }

    private static FatigueTestConfigDto MapToConfigDto(FatigueTestConfig config)
    {
        return new FatigueTestConfigDto
        {
            ConfigId = config.ConfigId,
            MapId = config.MapId,
            RobotIds = config.RobotIds,
            LocationIds = config.LocationIds,
            IsRunning = config.IsRunning,
            TaskIntervalMs = config.TaskIntervalMs,
            CreatedAt = config.CreatedAt,
            UpdatedAt = config.UpdatedAt
        };
    }
}

/// <summary>
/// Fatigue test config DTO.
/// </summary>
public class FatigueTestConfigDto
{
    public string ConfigId { get; set; } = string.Empty;

    public string MapId { get; set; } = string.Empty;

    public List<string> RobotIds { get; set; } = new();

    public List<string> LocationIds { get; set; } = new();

    public bool IsRunning { get; set; }

    public int TaskIntervalMs { get; set; } = 1000;

    public DateTime CreatedAt { get; set; }

    public DateTime UpdatedAt { get; set; }
}

/// <summary>
/// Save fatigue test config DTO.
/// </summary>
public class SaveFatigueTestConfigDto
{
    public string? ConfigId { get; set; }

    public string? MapId { get; set; }

    public List<string> RobotIds { get; set; } = new();

    public List<string> LocationIds { get; set; } = new();

    public int TaskIntervalMs { get; set; } = 1000;
}

/// <summary>
/// Fatigue test status DTO.
/// </summary>
public class FatigueTestStatusDto
{
    public string ConfigId { get; set; } = string.Empty;

    public bool IsRunning { get; set; }

    public string? MapId { get; set; }

    public int RobotCount { get; set; }

    public int LocationCount { get; set; }

    public int TaskIntervalMs { get; set; }
}

/// <summary>
/// Fatigue test status list DTO.
/// </summary>
public class FatigueTestStatusListDto
{
    public int TotalCount { get; set; }

    public int RunningCount { get; set; }

    public List<FatigueTestStatusDto> Configs { get; set; } = new();
}