Initial commit
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
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}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user