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; /// /// SignalR hub for InstanceMission management operations. /// [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)); } /// /// Searches instance missions with pagination and text search. /// Running missions are prioritized. /// public async Task> 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(total, request.Page, request.Size, items); } /// /// Gets the log for a specific instance mission. /// public async Task GetInstanceMissionLog(Guid missionId) { var mission = await _dbContext.InstanceMissions.FindAsync([missionId]); return mission?.Log; } /// /// Cancels a running or paused mission. /// public Task 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}")); } } /// /// Pauses a running mission. /// public Task 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}")); } } /// /// Resumes a paused mission. /// public Task 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}")); } } }