167 lines
5.7 KiB
C#
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}"));
|
|
}
|
|
}
|
|
}
|
|
|