From 8c83680b28fdd9906de29ff3d0514f493e98ff1a Mon Sep 17 00:00:00 2001 From: aestene Date: Wed, 6 Sep 2023 14:35:39 +0200 Subject: [PATCH] Format mission controller --- backend/api/Controllers/MissionController.cs | 1072 +++++++++--------- 1 file changed, 562 insertions(+), 510 deletions(-) diff --git a/backend/api/Controllers/MissionController.cs b/backend/api/Controllers/MissionController.cs index d3b2d1148..3d6ade26a 100644 --- a/backend/api/Controllers/MissionController.cs +++ b/backend/api/Controllers/MissionController.cs @@ -4,575 +4,627 @@ using Api.Database.Models; using Api.Services; using Api.Utilities; +using Azure; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; - -namespace Api.Controllers; - -[ApiController] -[Route("missions")] -public class MissionController : ControllerBase +namespace Api.Controllers { - private readonly IMissionDefinitionService _missionDefinitionService; - private readonly IMissionRunService _missionRunService; - private readonly IAreaService _areaService; - private readonly IRobotService _robotService; - private readonly IEchoService _echoService; - private readonly ICustomMissionService _customMissionService; - private readonly ILogger _logger; - private readonly IStidService _stidService; - private readonly IMapService _mapService; - private readonly ISourceService _sourceService; - - public MissionController( - IMissionDefinitionService missionDefinitionService, - IMissionRunService missionRunService, - IAreaService areaService, - IRobotService robotService, - IEchoService echoService, - ICustomMissionService customMissionService, - ILogger logger, - IMapService mapService, - IStidService stidService, - ISourceService sourceService - ) - { - _missionDefinitionService = missionDefinitionService; - _missionRunService = missionRunService; - _areaService = areaService; - _robotService = robotService; - _echoService = echoService; - _customMissionService = customMissionService; - _mapService = mapService; - _stidService = stidService; - _sourceService = sourceService; - _logger = logger; - } - - /// - /// List all mission runs in the Flotilla database - /// - /// - /// This query gets all mission runs - /// - [HttpGet("runs")] - [Authorize(Roles = Role.Any)] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetMissionRuns( - [FromQuery] MissionRunQueryStringParameters parameters - ) + [ApiController] + [Route("missions")] + public class MissionController : ControllerBase { - if (parameters.MaxDesiredStartTime < parameters.MinDesiredStartTime) + private readonly IAreaService _areaService; + private readonly ICustomMissionService _customMissionService; + private readonly IEchoService _echoService; + private readonly ILogger _logger; + private readonly IMapService _mapService; + private readonly IMissionDefinitionService _missionDefinitionService; + private readonly IMissionRunService _missionRunService; + private readonly IRobotService _robotService; + private readonly ISourceService _sourceService; + private readonly IStidService _stidService; + + public MissionController( + IMissionDefinitionService missionDefinitionService, + IMissionRunService missionRunService, + IAreaService areaService, + IRobotService robotService, + IEchoService echoService, + ICustomMissionService customMissionService, + ILogger logger, + IMapService mapService, + IStidService stidService, + ISourceService sourceService + ) { - return BadRequest("Max DesiredStartTime cannot be less than min DesiredStartTime"); + _missionDefinitionService = missionDefinitionService; + _missionRunService = missionRunService; + _areaService = areaService; + _robotService = robotService; + _echoService = echoService; + _customMissionService = customMissionService; + _mapService = mapService; + _stidService = stidService; + _sourceService = sourceService; + _logger = logger; } - if (parameters.MaxStartTime < parameters.MinStartTime) - { - return BadRequest("Max StartTime cannot be less than min StartTime"); - } - if (parameters.MaxEndTime < parameters.MinEndTime) + + /// + /// List all mission runs in the Flotilla database + /// + /// + /// This query gets all mission runs + /// + [HttpGet("runs")] + [Authorize(Roles = Role.Any)] + [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task>> GetMissionRuns( + [FromQuery] MissionRunQueryStringParameters parameters + ) { - return BadRequest("Max EndTime cannot be less than min EndTime"); + if (parameters.MaxDesiredStartTime < parameters.MinDesiredStartTime) + { + return BadRequest("Max DesiredStartTime cannot be less than min DesiredStartTime"); + } + if (parameters.MaxStartTime < parameters.MinStartTime) + { + return BadRequest("Max StartTime cannot be less than min StartTime"); + } + if (parameters.MaxEndTime < parameters.MinEndTime) + { + return BadRequest("Max EndTime cannot be less than min EndTime"); + } + + PagedList missionRuns; + try + { + missionRuns = await _missionRunService.ReadAll(parameters); + } + catch (InvalidDataException e) + { + _logger.LogError(e.Message); + return BadRequest(e.Message); + } + + var metadata = new + { + missionRuns.TotalCount, + missionRuns.PageSize, + missionRuns.CurrentPage, + missionRuns.TotalPages, + missionRuns.HasNext, + missionRuns.HasPrevious + }; + + Response.Headers.Add( + QueryStringParameters.PaginationHeader, + JsonSerializer.Serialize(metadata) + ); + + return Ok(missionRuns); } - PagedList missionRuns; - try + /// + /// List all mission definitions in the Flotilla database + /// + /// + /// This query gets all mission definitions + /// + [HttpGet("definitions")] + [Authorize(Roles = Role.Any)] + [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task>> GetMissionDefinitions( + [FromQuery] MissionDefinitionQueryStringParameters parameters + ) { - missionRuns = await _missionRunService.ReadAll(parameters); + PagedList missionDefinitions; + try + { + missionDefinitions = await _missionDefinitionService.ReadAll(parameters); + } + catch (InvalidDataException e) + { + _logger.LogError(e.Message); + return BadRequest(e.Message); + } + + var metadata = new + { + missionDefinitions.TotalCount, + missionDefinitions.PageSize, + missionDefinitions.CurrentPage, + missionDefinitions.TotalPages, + missionDefinitions.HasNext, + missionDefinitions.HasPrevious + }; + + Response.Headers.Add( + QueryStringParameters.PaginationHeader, + JsonSerializer.Serialize(metadata) + ); + + return Ok(missionDefinitions); } - catch (InvalidDataException e) + + /// + /// Lookup mission run by specified id. + /// + [HttpGet] + [Authorize(Roles = Role.Any)] + [Route("runs/{id}")] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetMissionRunById([FromRoute] string id) { - _logger.LogError(e.Message); - return BadRequest(e.Message); + var missioRun = await _missionRunService.ReadById(id); + if (missioRun == null) + { + return NotFound($"Could not find mission run with id {id}"); + } + return Ok(missioRun); } - var metadata = new + /// + /// Lookup mission definition by specified id. + /// + [HttpGet] + [Authorize(Roles = Role.Any)] + [Route("definitions/{id}")] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetMissionDefinitionById([FromRoute] string id) { - missionRuns.TotalCount, - missionRuns.PageSize, - missionRuns.CurrentPage, - missionRuns.TotalPages, - missionRuns.HasNext, - missionRuns.HasPrevious - }; - - Response.Headers.Add( - QueryStringParameters.PaginationHeader, - JsonSerializer.Serialize(metadata) - ); - - return Ok(missionRuns); - } + var missionDefinition = await _missionDefinitionService.ReadById(id); + if (missionDefinition == null) + { + return NotFound($"Could not find mission definition with id {id}"); + } + return Ok(missionDefinition); + } - /// - /// List all mission definitions in the Flotilla database - /// - /// - /// This query gets all mission definitions - /// - [HttpGet("definitions")] - [Authorize(Roles = Role.Any)] - [ProducesResponseType(typeof(IList), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task>> GetMissionDefinitions( - [FromQuery] MissionDefinitionQueryStringParameters parameters - ) - { - PagedList missionDefinitions; - try + /// + /// Lookup which mission run is scheduled next for the given mission definition + /// + [HttpGet] + [Authorize(Roles = Role.Any)] + [Route("definitions/{id}/next-run")] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> GetNextMissionRun([FromRoute] string id) { - missionDefinitions = await _missionDefinitionService.ReadAll(parameters); + var missionDefinition = await _missionDefinitionService.ReadById(id); + if (missionDefinition == null) + { + return NotFound($"Could not find mission definition with id {id}"); + } + var nextRun = await _missionRunService.ReadNextScheduledRunByMissionId(id); + return Ok(nextRun); } - catch (InvalidDataException e) + + /// + /// Get map for mission with specified id. + /// + [HttpGet] + [Authorize(Roles = Role.Any)] + [Route("{installationCode}/{mapName}/map")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + [ProducesResponseType(StatusCodes.Status502BadGateway)] + public async Task> GetMap([FromRoute] string installationCode, string mapName) { - _logger.LogError(e.Message); - return BadRequest(e.Message); + try + { + byte[] mapStream = await _mapService.FetchMapImage(mapName, installationCode); + return File(mapStream, "image/png"); + } + catch (RequestFailedException) + { + return NotFound("Could not find map for this area."); + } } - var metadata = new + /// + /// Schedule an existing mission definition + /// + /// + /// This query schedules an existing mission and adds it to the database + /// + [HttpPost("schedule")] + [Authorize(Roles = Role.User)] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Schedule( + [FromBody] ScheduleMissionQuery scheduledMissionQuery + ) { - missionDefinitions.TotalCount, - missionDefinitions.PageSize, - missionDefinitions.CurrentPage, - missionDefinitions.TotalPages, - missionDefinitions.HasNext, - missionDefinitions.HasPrevious - }; - - Response.Headers.Add( - QueryStringParameters.PaginationHeader, - JsonSerializer.Serialize(metadata) - ); - - return Ok(missionDefinitions); - } + var robot = await _robotService.ReadById(scheduledMissionQuery.RobotId); + if (robot is null) + { + return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}"); + } - /// - /// Lookup mission run by specified id. - /// - [HttpGet] - [Authorize(Roles = Role.Any)] - [Route("runs/{id}")] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> GetMissionRunById([FromRoute] string id) - { - var missioRun = await _missionRunService.ReadById(id); - if (missioRun == null) - return NotFound($"Could not find mission run with id {id}"); - return Ok(missioRun); - } + var missionDefinition = await _missionDefinitionService.ReadById(scheduledMissionQuery.MissionDefinitionId); + if (missionDefinition == null) + { + return NotFound("Mission definition not found"); + } - /// - /// Lookup mission definition by specified id. - /// - [HttpGet] - [Authorize(Roles = Role.Any)] - [Route("definitions/{id}")] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> GetMissionDefinitionById([FromRoute] string id) - { - var missionDefinition = await _missionDefinitionService.ReadById(id); - if (missionDefinition == null) - return NotFound($"Could not find mission definition with id {id}"); - return Ok(missionDefinition); - } + List? missionTasks; + missionTasks = missionDefinition.Source.Type switch + { + MissionSourceType.Echo => + // CultureInfo is not important here since we are not using decimal points + missionTasks = _echoService.GetMissionById( + int.Parse(missionDefinition.Source.SourceId, new CultureInfo("en-US")) + ).Result.Tags + .Select( + t => + { + var tagPosition = _stidService + .GetTagPosition(t.TagId, missionDefinition.InstallationCode) + .Result; + return new MissionTask(t, tagPosition); + } + ) + .ToList(), + MissionSourceType.Custom => + missionTasks = await _customMissionService.GetMissionTasksFromSourceId(missionDefinition.Source.SourceId), + _ => + throw new MissionSourceTypeException($"Mission type {missionDefinition.Source.Type} is not accounted for") + }; - /// - /// Lookup which mission run is scheduled next for the given mission definition - /// - [HttpGet] - [Authorize(Roles = Role.Any)] - [Route("definitions/{id}/next-run")] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> GetNextMissionRun([FromRoute] string id) - { - var missionDefinition = await _missionDefinitionService.ReadById(id); - if (missionDefinition == null) - return NotFound($"Could not find mission definition with id {id}"); - var nextRun = await _missionRunService.ReadNextScheduledRunByMissionId(id); - return Ok(nextRun); - } + if (missionTasks == null) + { + return NotFound("No mission tasks were found for the requested mission"); + } - /// - /// Get map for mission with specified id. - /// - [HttpGet] - [Authorize(Roles = Role.Any)] - [Route("{installationCode}/{mapName}/map")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - [ProducesResponseType(StatusCodes.Status502BadGateway)] - public async Task> GetMap([FromRoute] string installationCode, string mapName) - { - try - { - byte[] mapStream = await _mapService.FetchMapImage(mapName, installationCode); - return File(mapStream, "image/png"); + var missionRun = new MissionRun + { + Name = missionDefinition.Name, + Robot = robot, + MissionId = missionDefinition.Id, + Status = MissionStatus.Pending, + DesiredStartTime = scheduledMissionQuery.DesiredStartTime, + Tasks = missionTasks, + InstallationCode = missionDefinition.InstallationCode, + Area = missionDefinition.Area, + Map = new MapMetadata() + }; + + await _mapService.AssignMapToMission(missionRun); + + if (missionRun.Tasks.Any()) + { + missionRun.CalculateEstimatedDuration(); + } + + var newMissionRun = await _missionRunService.Create(missionRun); + + return CreatedAtAction(nameof(GetMissionRunById), new + { + id = newMissionRun.Id + }, newMissionRun); } - catch (Azure.RequestFailedException) + + /// + /// Schedule a new echo mission + /// + /// + /// This query schedules a new echo mission and adds it to the database + /// + [HttpPost] + [Authorize(Roles = Role.User)] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Create( + [FromBody] ScheduledMissionQuery scheduledMissionQuery + ) { - return NotFound("Could not find map for this area."); - } - } + var robot = await _robotService.ReadById(scheduledMissionQuery.RobotId); + if (robot is null) + { + return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}"); + } - /// - /// Schedule an existing mission definition - /// - /// - /// This query schedules an existing mission and adds it to the database - /// - [HttpPost("schedule")] - [Authorize(Roles = Role.User)] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> Schedule( - [FromBody] ScheduleMissionQuery scheduledMissionQuery - ) - { - var robot = await _robotService.ReadById(scheduledMissionQuery.RobotId); - if (robot is null) - return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}"); + EchoMission? echoMission; + try + { + echoMission = await _echoService.GetMissionById(scheduledMissionQuery.EchoMissionId); + } + catch (HttpRequestException e) + { + if (e.StatusCode.HasValue && (int)e.StatusCode.Value == 404) + { + _logger.LogWarning( + "Could not find echo mission with id={id}", + scheduledMissionQuery.EchoMissionId + ); + return NotFound("Echo mission not found"); + } - var missionDefinition = await _missionDefinitionService.ReadById(scheduledMissionQuery.MissionDefinitionId); - if (missionDefinition == null) - return NotFound("Mission definition not found"); + _logger.LogError(e, "Error getting mission from Echo"); + return StatusCode(StatusCodes.Status502BadGateway, $"{e.Message}"); + } + catch (JsonException e) + { + string message = "Error deserializing mission from Echo"; + _logger.LogError(e, "{message}", message); + return StatusCode(StatusCodes.Status500InternalServerError, message); + } + catch (InvalidDataException e) + { + string message = + "Can not schedule mission because EchoMission is invalid. One or more tasks does not contain a robot pose."; + _logger.LogError(e, message); + return StatusCode(StatusCodes.Status502BadGateway, message); + } - List? missionTasks; - missionTasks = missionDefinition.Source.Type switch - { - MissionSourceType.Echo => - // CultureInfo is not important here since we are not using decimal points - missionTasks = _echoService.GetMissionById( - int.Parse(missionDefinition.Source.SourceId, new CultureInfo("en-US")) - ).Result.Tags - .Select( - t => - { - var tagPosition = _stidService - .GetTagPosition(t.TagId, missionDefinition.InstallationCode) - .Result; - return new MissionTask(t, tagPosition); - } - ) - .ToList(), - MissionSourceType.Custom => - missionTasks = await _customMissionService.GetMissionTasksFromSourceId(missionDefinition.Source.SourceId), - _ => - throw new MissionSourceTypeException($"Mission type {missionDefinition.Source.Type} is not accounted for") - }; - - if (missionTasks == null) - return NotFound("No mission tasks were found for the requested mission"); - - var missionRun = new MissionRun - { - Name = missionDefinition.Name, - Robot = robot, - MissionId = missionDefinition.Id, - Status = MissionStatus.Pending, - DesiredStartTime = scheduledMissionQuery.DesiredStartTime, - Tasks = missionTasks, - InstallationCode = missionDefinition.InstallationCode, - Area = missionDefinition.Area, - Map = new MapMetadata() - }; + var missionTasks = echoMission.Tags + .Select( + t => + { + var tagPosition = _stidService + .GetTagPosition(t.TagId, scheduledMissionQuery.InstallationCode) + .Result; + return new MissionTask(t, tagPosition); + } + ) + .ToList(); + + Area? area = null; + if (scheduledMissionQuery.AreaName != null) + { + area = await _areaService.ReadByInstallationAndName(scheduledMissionQuery.InstallationCode, scheduledMissionQuery.AreaName); + if (area == null) + { + return NotFound($"Could not find area by installation '{scheduledMissionQuery.InstallationCode}' and name '{scheduledMissionQuery.AreaName}'"); + } + } - await _mapService.AssignMapToMission(missionRun); + var source = await _sourceService.CheckForExistingEchoSource(scheduledMissionQuery.EchoMissionId); + MissionDefinition? existingMissionDefinition = null; + if (source == null) + { + source = new Source + { + SourceId = $"{echoMission.Id}", + Type = MissionSourceType.Echo + }; + } + else + { + var missionDefinitions = await _missionDefinitionService.ReadBySourceId(source.SourceId); + if (missionDefinitions.Count > 0) + { + existingMissionDefinition = missionDefinitions.First(); + } + } - if (missionRun.Tasks.Any()) - missionRun.CalculateEstimatedDuration(); + var scheduledMissionDefinition = existingMissionDefinition ?? new MissionDefinition + { + Id = Guid.NewGuid().ToString(), + Source = source, + Name = echoMission.Name, + InspectionFrequency = scheduledMissionQuery.InspectionFrequency, + InstallationCode = scheduledMissionQuery.InstallationCode, + Area = area + }; - var newMissionRun = await _missionRunService.Create(missionRun); + var missionRun = new MissionRun + { + Name = echoMission.Name, + Robot = robot, + MissionId = scheduledMissionDefinition.Id, + Status = MissionStatus.Pending, + DesiredStartTime = scheduledMissionQuery.DesiredStartTime, + Tasks = missionTasks, + InstallationCode = scheduledMissionQuery.InstallationCode, + Area = area, + Map = new MapMetadata() + }; - return CreatedAtAction(nameof(GetMissionRunById), new { id = newMissionRun.Id }, newMissionRun); - } + await _mapService.AssignMapToMission(missionRun); - /// - /// Schedule a new echo mission - /// - /// - /// This query schedules a new echo mission and adds it to the database - /// - [HttpPost] - [Authorize(Roles = Role.User)] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> Create( - [FromBody] ScheduledMissionQuery scheduledMissionQuery - ) - { - var robot = await _robotService.ReadById(scheduledMissionQuery.RobotId); - if (robot is null) - return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}"); + if (missionRun.Tasks.Any()) + { + missionRun.CalculateEstimatedDuration(); + } - EchoMission? echoMission; - try - { - echoMission = await _echoService.GetMissionById(scheduledMissionQuery.EchoMissionId); - } - catch (HttpRequestException e) - { - if (e.StatusCode.HasValue && (int)e.StatusCode.Value == 404) + if (existingMissionDefinition == null) { - _logger.LogWarning( - "Could not find echo mission with id={id}", - scheduledMissionQuery.EchoMissionId - ); - return NotFound("Echo mission not found"); + await _missionDefinitionService.Create(scheduledMissionDefinition); } - _logger.LogError(e, "Error getting mission from Echo"); - return StatusCode(StatusCodes.Status502BadGateway, $"{e.Message}"); - } - catch (JsonException e) - { - string message = "Error deserializing mission from Echo"; - _logger.LogError(e, "{message}", message); - return StatusCode(StatusCodes.Status500InternalServerError, message); + var newMissionRun = await _missionRunService.Create(missionRun); + + return CreatedAtAction(nameof(GetMissionRunById), new + { + id = newMissionRun.Id + }, newMissionRun); } - catch (InvalidDataException e) + + /// + /// Schedule a custom mission + /// + /// + /// This query schedules a custom mission defined in the incoming json + /// + [HttpPost] + [Authorize(Roles = Role.User)] + [Route("custom")] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> Create( + [FromBody] CustomMissionQuery customMissionQuery + ) { - string message = - "Can not schedule mission because EchoMission is invalid. One or more tasks does not contain a robot pose."; - _logger.LogError(e, message); - return StatusCode(StatusCodes.Status502BadGateway, message); - } + var robot = await _robotService.ReadById(customMissionQuery.RobotId); + if (robot is null) + { + return NotFound($"Could not find robot with id {customMissionQuery.RobotId}"); + } + + var installationResults = await _echoService.GetEchoPlantInfos(); + if (installationResults == null) + { + return NotFound("Unable to retrieve plant information from Echo"); + } + + var installationResult = installationResults + .Where( + installation => installation.PlantCode.ToUpperInvariant() == customMissionQuery.InstallationCode.ToUpperInvariant() + ).FirstOrDefault(); + if (installationResult == null) + { + return NotFound($"Could not find installation with id {customMissionQuery.InstallationCode}"); + } - var missionTasks = echoMission.Tags - .Select( - t => + var missionTasks = customMissionQuery.Tasks.Select(task => new MissionTask(task)).ToList(); + + Area? area = null; + if (customMissionQuery.AreaName != null) + { + area = await _areaService.ReadByInstallationAndName(customMissionQuery.InstallationCode, customMissionQuery.AreaName); + } + + var source = await _sourceService.CheckForExistingCustomSource(missionTasks); + MissionDefinition? existingMissionDefinition = null; + if (source == null) + { + string sourceURL = _customMissionService.UploadSource(missionTasks); + source = new Source { - var tagPosition = _stidService - .GetTagPosition(t.TagId, scheduledMissionQuery.InstallationCode) - .Result; - return new MissionTask(t, tagPosition); + SourceId = sourceURL, + Type = MissionSourceType.Custom + }; + } + else + { + var missionDefinitions = await _missionDefinitionService.ReadBySourceId(source.SourceId); + if (missionDefinitions.Count > 0) + { + existingMissionDefinition = missionDefinitions.First(); } - ) - .ToList(); - - Area? area = null; - if (scheduledMissionQuery.AreaName != null) - { - area = await _areaService.ReadByInstallationAndName(scheduledMissionQuery.InstallationCode, scheduledMissionQuery.AreaName); - if (area == null) - return NotFound($"Could not find area by installation '{scheduledMissionQuery.InstallationCode}' and name '{scheduledMissionQuery.AreaName}'"); - } + } - var source = await _sourceService.CheckForExistingEchoSource(scheduledMissionQuery.EchoMissionId); - MissionDefinition? existingMissionDefinition = null; - if (source == null) - { - source = new Source + var customMissionDefinition = existingMissionDefinition ?? new MissionDefinition { - SourceId = $"{echoMission.Id}", - Type = MissionSourceType.Echo + Id = Guid.NewGuid().ToString(), + Source = source, + Name = customMissionQuery.Name, + InspectionFrequency = customMissionQuery.InspectionFrequency, + InstallationCode = customMissionQuery.InstallationCode, + Area = area }; - } - else - { - var missionDefinitions = await _missionDefinitionService.ReadBySourceId(source.SourceId); - if (missionDefinitions.Count > 0) - existingMissionDefinition = missionDefinitions.First(); - } - var scheduledMissionDefinition = existingMissionDefinition ?? new MissionDefinition - { - Id = Guid.NewGuid().ToString(), - Source = source, - Name = echoMission.Name, - InspectionFrequency = scheduledMissionQuery.InspectionFrequency, - InstallationCode = scheduledMissionQuery.InstallationCode, - Area = area - }; - - var missionRun = new MissionRun - { - Name = echoMission.Name, - Robot = robot, - MissionId = scheduledMissionDefinition.Id, - Status = MissionStatus.Pending, - DesiredStartTime = scheduledMissionQuery.DesiredStartTime, - Tasks = missionTasks, - InstallationCode = scheduledMissionQuery.InstallationCode, - Area = area, - Map = new MapMetadata() - }; - - await _mapService.AssignMapToMission(missionRun); + var scheduledMission = new MissionRun + { + Name = customMissionQuery.Name, + Description = customMissionQuery.Description, + MissionId = customMissionDefinition.Id, + Comment = customMissionQuery.Comment, + Robot = robot, + Status = MissionStatus.Pending, + DesiredStartTime = customMissionQuery.DesiredStartTime ?? DateTimeOffset.UtcNow, + Tasks = missionTasks, + InstallationCode = customMissionQuery.InstallationCode, + Area = area, + Map = new MapMetadata() + }; - if (missionRun.Tasks.Any()) - missionRun.CalculateEstimatedDuration(); + await _mapService.AssignMapToMission(scheduledMission); - if (existingMissionDefinition == null) - await _missionDefinitionService.Create(scheduledMissionDefinition); + if (scheduledMission.Tasks.Any()) + { + scheduledMission.CalculateEstimatedDuration(); + } - var newMissionRun = await _missionRunService.Create(missionRun); + if (existingMissionDefinition == null) + { + await _missionDefinitionService.Create(customMissionDefinition); + } - return CreatedAtAction(nameof(GetMissionRunById), new { id = newMissionRun.Id }, newMissionRun); - } + var newMissionRun = await _missionRunService.Create(scheduledMission); - /// - /// Schedule a custom mission - /// - /// - /// This query schedules a custom mission defined in the incoming json - /// - [HttpPost] - [Authorize(Roles = Role.User)] - [Route("custom")] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> Create( - [FromBody] CustomMissionQuery customMissionQuery - ) - { - var robot = await _robotService.ReadById(customMissionQuery.RobotId); - if (robot is null) - return NotFound($"Could not find robot with id {customMissionQuery.RobotId}"); - - var installationResults = await _echoService.GetEchoPlantInfos(); - if (installationResults == null) - return NotFound("Unable to retrieve plant information from Echo"); - - var installationResult = installationResults - .Where( - installation => installation.PlantCode.ToUpperInvariant() == customMissionQuery.InstallationCode.ToUpperInvariant() - ).FirstOrDefault(); - if (installationResult == null) - return NotFound($"Could not find installation with id {customMissionQuery.InstallationCode}"); - - var missionTasks = customMissionQuery.Tasks.Select(task => new MissionTask(task)).ToList(); - - Area? area = null; - if (customMissionQuery.AreaName != null) - area = await _areaService.ReadByInstallationAndName(customMissionQuery.InstallationCode, customMissionQuery.AreaName); - - var source = await _sourceService.CheckForExistingCustomSource(missionTasks); - MissionDefinition? existingMissionDefinition = null; - if (source == null) - { - string sourceURL = _customMissionService.UploadSource(missionTasks); - source = new Source + return CreatedAtAction(nameof(GetMissionRunById), new { - SourceId = sourceURL.ToString(), - Type = MissionSourceType.Custom - }; + id = newMissionRun.Id + }, newMissionRun); } - else + + /// + /// Deletes the mission definition with the specified id from the database. + /// + [HttpDelete] + [Authorize(Roles = Role.Admin)] + [Route("definitions/{id}")] + [ProducesResponseType(typeof(MissionDefinition), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> DeleteMissionDefinition([FromRoute] string id) { - var missionDefinitions = await _missionDefinitionService.ReadBySourceId(source.SourceId); - if (missionDefinitions.Count > 0) - existingMissionDefinition = missionDefinitions.First(); + var missionDefinition = await _missionDefinitionService.Delete(id); + if (missionDefinition is null) + { + return NotFound($"Mission definition with id {id} not found"); + } + return Ok(missionDefinition); } - var customMissionDefinition = existingMissionDefinition ?? new MissionDefinition + /// + /// Deletes the mission run with the specified id from the database. + /// + [HttpDelete] + [Authorize(Roles = Role.Admin)] + [Route("runs/{id}")] + [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> DeleteMissionRun([FromRoute] string id) { - Id = Guid.NewGuid().ToString(), - Source = source, - Name = customMissionQuery.Name, - InspectionFrequency = customMissionQuery.InspectionFrequency, - InstallationCode = customMissionQuery.InstallationCode, - Area = area - }; - - var scheduledMission = new MissionRun - { - Name = customMissionQuery.Name, - Description = customMissionQuery.Description, - MissionId = customMissionDefinition.Id, - Comment = customMissionQuery.Comment, - Robot = robot, - Status = MissionStatus.Pending, - DesiredStartTime = customMissionQuery.DesiredStartTime ?? DateTimeOffset.UtcNow, - Tasks = missionTasks, - InstallationCode = customMissionQuery.InstallationCode, - Area = area, - Map = new MapMetadata() - }; - - await _mapService.AssignMapToMission(scheduledMission); - - if (scheduledMission.Tasks.Any()) - scheduledMission.CalculateEstimatedDuration(); - - if (existingMissionDefinition == null) - await _missionDefinitionService.Create(customMissionDefinition); - - var newMissionRun = await _missionRunService.Create(scheduledMission); - - return CreatedAtAction(nameof(GetMissionRunById), new { id = newMissionRun.Id }, newMissionRun); - } - - /// - /// Deletes the mission definition with the specified id from the database. - /// - [HttpDelete] - [Authorize(Roles = Role.Admin)] - [Route("definitions/{id}")] - [ProducesResponseType(typeof(MissionDefinition), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> DeleteMissionDefinition([FromRoute] string id) - { - var missionDefinition = await _missionDefinitionService.Delete(id); - if (missionDefinition is null) - return NotFound($"Mission definition with id {id} not found"); - return Ok(missionDefinition); - } - - /// - /// Deletes the mission run with the specified id from the database. - /// - [HttpDelete] - [Authorize(Roles = Role.Admin)] - [Route("runs/{id}")] - [ProducesResponseType(typeof(MissionRun), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> DeleteMissionRun([FromRoute] string id) - { - var missionRun = await _missionRunService.Delete(id); - if (missionRun is null) - return NotFound($"Mission run with id {id} not found"); - return Ok(missionRun); + var missionRun = await _missionRunService.Delete(id); + if (missionRun is null) + { + return NotFound($"Mission run with id {id} not found"); + } + return Ok(missionRun); + } } }