Files
BQP/srcs/RobotNet10/Commons/RobotNet10.ScriptEngine/Hubs/InstanceMissionHub.cs
2026-07-13 09:25:40 +07:00

167 lines
5.7 KiB
C#

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using RobotNet10.ScriptEngine.Data;
using RobotNet10.ScriptEngine.Shared;
using RobotNet10.Shared;
using System.Security.Claims;
using System.Text.Json;
namespace RobotNet10.ScriptEngine.Hubs;
/// <summary>
/// SignalR hub for InstanceMission management operations.
/// </summary>
[Authorize]
public class InstanceMissionHub : Hub
{
private readonly ScriptEngineDbContext _dbContext;
private readonly MissionManager _missionManager;
public InstanceMissionHub(ScriptEngineDbContext dbContext, MissionManager missionManager)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
_missionManager = missionManager ?? throw new ArgumentNullException(nameof(missionManager));
}
/// <summary>
/// Searches instance missions with pagination and text search.
/// Running missions are prioritized.
/// </summary>
public async Task<SearchResult<InstanceMissionDto>> SearchInstanceMissions(SearchRequest request)
{
var query = _dbContext.InstanceMissions.AsQueryable();
// Text search on MissionName
if (!string.IsNullOrWhiteSpace(request.TxtSearch))
{
var searchText = request.TxtSearch.Trim();
query = query.Where(m => m.MissionName.Contains(searchText));
}
// Get total count before pagination
var total = await query.CountAsync();
// Order by: running missions first (Running, Pausing, Resuming), then by CreatedAt descending
var orderedQuery = query
.OrderByDescending(m => m.State == ScriptMissionState.Running ||
m.State == ScriptMissionState.Pausing ||
m.State == ScriptMissionState.Resuming)
.ThenByDescending(m => m.CreatedAt);
// Apply pagination
var items = await orderedQuery
.Skip((request.Page - 1) * request.Size)
.Take(request.Size)
.Select(m => new InstanceMissionDto
{
Id = m.Id,
MissionName = m.MissionName,
Parameters = m.Parameters ?? "{}",
CreatedAt = m.CreatedAt,
State = m.State,
TotalScore = m.TotalScore,
Score = m.Score,
StoppedAt = m.StoppedAt,
Log = m.Log
})
.ToArrayAsync();
return new SearchResult<InstanceMissionDto>(total, request.Page, request.Size, items);
}
/// <summary>
/// Gets the log for a specific instance mission.
/// </summary>
public async Task<string?> GetInstanceMissionLog(Guid missionId)
{
var mission = await _dbContext.InstanceMissions.FindAsync([missionId]);
return mission?.Log;
}
/// <summary>
/// Cancels a running or paused mission.
/// </summary>
public Task<MessageResult> CancelMission(Guid missionId, string reason)
{
var mission = _missionManager.GetMission(missionId);
if (mission == null)
{
return Task.FromResult(new MessageResult(false, "Mission not found"));
}
try
{
if (mission.State != ScriptMissionState.Running &&
mission.State != ScriptMissionState.Paused &&
mission.State != ScriptMissionState.Pausing)
{
return Task.FromResult(new MessageResult(false, $"Mission is in {mission.State} state and cannot be canceled"));
}
mission.Cancel(reason);
return Task.FromResult(new MessageResult(true, $"Mission canceled{(string.IsNullOrWhiteSpace(reason) ? "" : $": {reason}")}"));
}
catch (Exception ex)
{
return Task.FromResult(new MessageResult(false, $"Failed to cancel mission: {ex.Message}"));
}
}
/// <summary>
/// Pauses a running mission.
/// </summary>
public Task<MessageResult> PauseMission(Guid missionId)
{
var mission = _missionManager.GetMission(missionId);
if (mission == null)
{
return Task.FromResult(new MessageResult(false, "Mission not found"));
}
try
{
if (mission.State != ScriptMissionState.Running)
{
return Task.FromResult(new MessageResult(false, $"Mission is in {mission.State} state and cannot be paused"));
}
mission.Pause();
return Task.FromResult(new MessageResult(true, "Mission paused"));
}
catch (Exception ex)
{
return Task.FromResult(new MessageResult(false, $"Failed to pause mission: {ex.Message}"));
}
}
/// <summary>
/// Resumes a paused mission.
/// </summary>
public Task<MessageResult> ResumeMission(Guid missionId)
{
var mission = _missionManager.GetMission(missionId);
if (mission == null)
{
return Task.FromResult(new MessageResult(false, "Mission not found"));
}
try
{
if (mission.State != ScriptMissionState.Paused)
{
return Task.FromResult(new MessageResult(false, $"Mission is in {mission.State} state and cannot be resumed"));
}
mission.Resume();
return Task.FromResult(new MessageResult(true, "Mission resumed"));
}
catch (Exception ex)
{
return Task.FromResult(new MessageResult(false, $"Failed to resume mission: {ex.Message}"));
}
}
}