From 2136cdf17947db20e8a8b91b38c3f0c420c94a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Chirico=20Indreb=C3=B8?= Date: Tue, 9 Apr 2024 10:12:45 +0200 Subject: [PATCH] Return error when using unsupported sensors --- .../MissionSchedulingController.cs | 33 ++++++++++++++++--- backend/api/Database/Models/Inspection.cs | 13 ++++++++ backend/api/Services/LocalizationService.cs | 11 +++++-- backend/api/Services/MissionRunService.cs | 18 ++++++++++ .../api/Services/MissionSchedulingService.cs | 18 ++++++++-- backend/api/Services/ReturnToHomeService.cs | 3 +- backend/api/Utilities/Exceptions.cs | 4 +++ 7 files changed, 91 insertions(+), 9 deletions(-) diff --git a/backend/api/Controllers/MissionSchedulingController.cs b/backend/api/Controllers/MissionSchedulingController.cs index d6a09ccd8..07e7d8e1c 100644 --- a/backend/api/Controllers/MissionSchedulingController.cs +++ b/backend/api/Controllers/MissionSchedulingController.cs @@ -94,7 +94,15 @@ [FromBody] ScheduleMissionQuery scheduledMissionQuery // Compare with GetTasksFromSource - newMissionRun = await missionRunService.Create(newMissionRun); + try + { + newMissionRun = await missionRunService.Create(newMissionRun); + } + catch (UnsupportedRobotCapabilityException) + { + return BadRequest($"The robot {robot.Name} does not have the necessary sensors to run the mission."); + } + return CreatedAtAction(nameof(Rerun), new { @@ -171,7 +179,15 @@ [FromBody] ScheduleMissionQuery scheduledMissionQuery missionRun.CalculateEstimatedDuration(); } - var newMissionRun = await missionRunService.Create(missionRun); + MissionRun newMissionRun; + try + { + newMissionRun = await missionRunService.Create(missionRun); + } + catch (UnsupportedRobotCapabilityException) + { + return BadRequest($"The robot {robot.Name} does not have the necessary sensors to run the mission."); + } return CreatedAtAction(nameof(Schedule), new { @@ -337,7 +353,15 @@ [FromBody] ScheduledMissionQuery scheduledMissionQuery await missionDefinitionService.Create(scheduledMissionDefinition); } - var newMissionRun = await missionRunService.Create(missionRun); + MissionRun newMissionRun; + try + { + newMissionRun = await missionRunService.Create(missionRun); + } + catch (UnsupportedRobotCapabilityException) + { + return BadRequest($"The robot {robot.Name} does not have the necessary sensors to run the mission."); + } return CreatedAtAction(nameof(Create), new { @@ -393,8 +417,9 @@ [FromBody] CustomMissionQuery customMissionQuery MissionRun? newMissionRun; try { newMissionRun = await customMissionSchedulingService.QueueCustomMissionRun(customMissionQuery, customMissionDefinition.Id, robot.Id, missionTasks); } - catch (Exception e) when (e is RobotPressureTooLowException or RobotBatteryLevelTooLowException) { return BadRequest(e.Message); } + catch (Exception e) when (e is RobotPressureTooLowException or RobotBatteryLevelTooLowException or UnsupportedRobotCapabilityException) { return BadRequest(e.Message); } catch (Exception e) when (e is RobotNotFoundException or MissionNotFoundException) { return NotFound(e.Message); } + catch (Exception e) when (e is UnsupportedRobotCapabilityException) { return BadRequest($"The robot {robot.Name} does not have the necessary sensors to run the mission."); } return CreatedAtAction(nameof(Create), new { diff --git a/backend/api/Database/Models/Inspection.cs b/backend/api/Database/Models/Inspection.cs index 3ce6edace..52ec9a3fc 100644 --- a/backend/api/Database/Models/Inspection.cs +++ b/backend/api/Database/Models/Inspection.cs @@ -131,6 +131,19 @@ public void UpdateStatus(IsarStepStatus isarStatus) ) }; } + + public bool IsSupportedInspectionType(IList capabilities) + { + return InspectionType switch + { + InspectionType.Image => capabilities.Contains(RobotCapabilitiesEnum.take_image), + InspectionType.ThermalImage => capabilities.Contains(RobotCapabilitiesEnum.take_thermal_image), + InspectionType.Video => capabilities.Contains(RobotCapabilitiesEnum.take_video), + InspectionType.ThermalVideo => capabilities.Contains(RobotCapabilitiesEnum.take_thermal_video), + InspectionType.Audio => capabilities.Contains(RobotCapabilitiesEnum.record_audio), + _ => false, + }; + } } public enum InspectionStatus diff --git a/backend/api/Services/LocalizationService.cs b/backend/api/Services/LocalizationService.cs index 6e25c66b1..a4ef08204 100644 --- a/backend/api/Services/LocalizationService.cs +++ b/backend/api/Services/LocalizationService.cs @@ -99,8 +99,15 @@ public async Task CreateLocalizationMissionInArea(string robotId, st }; await mapService.AssignMapToMission(localizationMissionRun); - logger.LogWarning("Starting localization mission"); - await missionRunService.Create(localizationMissionRun, triggerCreatedMissionRunEvent: false); + try + { + logger.LogWarning("Starting localization mission"); + await missionRunService.Create(localizationMissionRun, triggerCreatedMissionRunEvent: false); + } + catch (UnsupportedRobotCapabilityException) + { + logger.LogError($"Unsupported robot capability detected when starting localisation mission for robot {localizationMissionRun.Robot.Name}. This should not happen."); + } return localizationMissionRun; } diff --git a/backend/api/Services/MissionRunService.cs b/backend/api/Services/MissionRunService.cs index 2da077b6e..5181fd006 100644 --- a/backend/api/Services/MissionRunService.cs +++ b/backend/api/Services/MissionRunService.cs @@ -40,6 +40,8 @@ public interface IMissionRunService public Task PendingOrOngoingReturnToHomeMissionRunExists(string robotId); + public bool IncludesUnsupportedInspectionType(MissionRun missionRun); + public Task Update(MissionRun mission); public Task UpdateMissionRunStatusByIsarMissionId( @@ -74,6 +76,11 @@ public async Task Create(MissionRun missionRun, bool triggerCreatedM // Making sure database does not try to create new robot context.Entry(missionRun.Robot).State = EntityState.Unchanged; + if (IncludesUnsupportedInspectionType(missionRun)) + { + throw new UnsupportedRobotCapabilityException($"Mission {missionRun.Name} contains inspection types not supported by robot: {missionRun.Robot.Name}."); + } + if (missionRun.Area is not null) { context.Entry(missionRun.Area).State = EntityState.Unchanged; } await context.MissionRuns.AddAsync(missionRun); await ApplyDatabaseUpdate(missionRun.Area?.Installation); @@ -224,6 +231,17 @@ public async Task PendingOrOngoingReturnToHomeMissionRunExists(string robo } + public bool IncludesUnsupportedInspectionType(MissionRun missionRun) + { + if (missionRun.Robot.RobotCapabilities == null) return false; + + foreach (var task in missionRun.Tasks) + foreach (var inspection in task.Inspections) + if (!inspection.IsSupportedInspectionType(missionRun.Robot.RobotCapabilities)) + return true; + return false; + } + public async Task Update(MissionRun missionRun) { context.Entry(missionRun.Robot).State = EntityState.Unchanged; diff --git a/backend/api/Services/MissionSchedulingService.cs b/backend/api/Services/MissionSchedulingService.cs index ae0878cf1..4406e36ac 100644 --- a/backend/api/Services/MissionSchedulingService.cs +++ b/backend/api/Services/MissionSchedulingService.cs @@ -252,7 +252,14 @@ public async Task ScheduleMissionToDriveToSafePosition(string robotId, string ar Map = new MapMetadata() }; - await missionRunService.Create(missionRun); + try + { + await missionRunService.Create(missionRun); + } + catch (UnsupportedRobotCapabilityException) + { + logger.LogError($"Unsupported robot capability detected when driving to safe position for robot {missionRun.Robot.Name}. This should not happen."); + } } public bool MissionRunQueueIsEmpty(IList missionRunQueue) @@ -315,7 +322,14 @@ private async Task MoveInterruptedMissionsToQueue(IEnumerable interrupte Map = new MapMetadata() }; - await missionRunService.Create(newMissionRun); + try + { + await missionRunService.Create(missionRun); + } + catch (UnsupportedRobotCapabilityException) + { + logger.LogError($"Unsupported robot capability detected when restarting interrupted missions for robot {missionRun.Robot.Name}. This should not happen."); + } } } diff --git a/backend/api/Services/ReturnToHomeService.cs b/backend/api/Services/ReturnToHomeService.cs index 7cd62697e..cbb091b04 100644 --- a/backend/api/Services/ReturnToHomeService.cs +++ b/backend/api/Services/ReturnToHomeService.cs @@ -29,8 +29,9 @@ public class ReturnToHomeService(ILogger logger, IRobotServ MissionRun missionRun; try { missionRun = await ScheduleReturnToHomeMissionRun(robotId); } - catch (Exception ex) when (ex is RobotNotFoundException or AreaNotFoundException or DeckNotFoundException or PoseNotFoundException) + catch (Exception ex) when (ex is RobotNotFoundException or AreaNotFoundException or DeckNotFoundException or PoseNotFoundException or UnsupportedRobotCapabilityException) { + // TODO: if we make ISAR aware of return to home missions, we can avoid scheduling them when the robot does not need them throw new ReturnToHomeMissionFailedToScheduleException(ex.Message); } diff --git a/backend/api/Utilities/Exceptions.cs b/backend/api/Utilities/Exceptions.cs index 15f641eb3..cae12f250 100644 --- a/backend/api/Utilities/Exceptions.cs +++ b/backend/api/Utilities/Exceptions.cs @@ -120,4 +120,8 @@ public class ReturnToHomeMissionFailedToScheduleException(string message) : Exce public class RobotCurrentAreaMissingException(string message) : Exception(message) { } + + public class UnsupportedRobotCapabilityException(string message) : Exception(message) + { + } }