diff --git a/backend/api.test/EventHandlers/TestMissionEventHandler.cs b/backend/api.test/EventHandlers/TestMissionEventHandler.cs index ec0a72116..05b9c9307 100644 --- a/backend/api.test/EventHandlers/TestMissionEventHandler.cs +++ b/backend/api.test/EventHandlers/TestMissionEventHandler.cs @@ -335,16 +335,14 @@ public async void QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameT var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, null); var missionRun1 = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true); var missionRun2 = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true); - Thread.Sleep(100); var missionRunCreatedEventArgs = new MissionRunCreatedEventArgs(missionRun1.Id); _missionRunService.RaiseEvent(nameof(MissionRunService.MissionRunCreated), missionRunCreatedEventArgs); - Thread.Sleep(100); // Act - var mqttEventArgs = new MqttReceivedArgs( + var mqttIsarMissionEventArgs = new MqttReceivedArgs( new IsarMissionMessage { RobotName = robot.Name, @@ -353,12 +351,62 @@ public async void QueuedMissionsAreNotAbortedWhenRobotAvailableHappensAtTheSameT Status = "successful", Timestamp = DateTime.UtcNow }); + var robotAvailableEventArgs = new RobotAvailableEventArgs(robot.Id); - _mqttService.RaiseEvent(nameof(MqttService.MqttIsarMissionReceived), mqttEventArgs); + _mqttService.RaiseEvent(nameof(MqttService.MqttIsarMissionReceived), mqttIsarMissionEventArgs); _missionSchedulingService.RaiseEvent(nameof(MissionSchedulingService.RobotAvailable), robotAvailableEventArgs); Thread.Sleep(500); + // Assert + var postTestMissionRun1 = await _missionRunService.ReadById(missionRun1.Id); + Assert.Equal(MissionRunType.Localization, postTestMissionRun1!.MissionRunType); + Assert.Equal(MissionStatus.Successful, postTestMissionRun1!.Status); + var postTestMissionRun2 = await _missionRunService.ReadById(missionRun2.Id); + Assert.Equal(MissionStatus.Pending, postTestMissionRun2!.Status); + } + + [Fact] + public async void QueuedContinuesWhenOnIsarStatusHappensAtTheSameTimeAsOnIsarMissionCompleted() + { + // Arrange + var installation = await _databaseUtilities.NewInstallation(); + var plant = await _databaseUtilities.NewPlant(installation.InstallationCode); + var deck = await _databaseUtilities.NewDeck(installation.InstallationCode, plant.PlantCode); + var area = await _databaseUtilities.NewArea(installation.InstallationCode, plant.PlantCode, deck.Name); + var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, null); + var missionRun1 = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true); + var missionRun2 = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true); + Thread.Sleep(100); + + var missionRunCreatedEventArgs = new MissionRunCreatedEventArgs(missionRun1.Id); + _missionRunService.RaiseEvent(nameof(MissionRunService.MissionRunCreated), missionRunCreatedEventArgs); + Thread.Sleep(100); + + // Act + var mqttIsarMissionEventArgs = new MqttReceivedArgs( + new IsarMissionMessage + { + RobotName = robot.Name, + IsarId = robot.IsarId, + MissionId = missionRun1.IsarMissionId, + Status = "successful", + Timestamp = DateTime.UtcNow + }); + + var mqttIsarStatusEventArgs = new MqttReceivedArgs( + new IsarStatusMessage + { + RobotName = robot.Name, + IsarId = robot.IsarId, + Status = RobotStatus.Available, + Timestamp = DateTime.UtcNow + }); + + _mqttService.RaiseEvent(nameof(MqttService.MqttIsarMissionReceived), mqttIsarMissionEventArgs); + _mqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttIsarStatusEventArgs); + Thread.Sleep(500); + // Assert var postTestMissionRun1 = await _missionRunService.ReadById(missionRun1.Id); Assert.Equal(MissionRunType.Localization, postTestMissionRun1!.MissionRunType); diff --git a/backend/api/EventHandlers/MissionEventHandler.cs b/backend/api/EventHandlers/MissionEventHandler.cs index 105a5e22b..d2146c7d4 100644 --- a/backend/api/EventHandlers/MissionEventHandler.cs +++ b/backend/api/EventHandlers/MissionEventHandler.cs @@ -42,7 +42,7 @@ public override void Subscribe() { MissionRunService.MissionRunCreated += OnMissionRunCreated; MissionSchedulingService.RobotAvailable += OnRobotAvailable; - MissionSchedulingService.MissionCompleted += OnMissionCompleted; + MissionSchedulingService.LocalizationMissionSuccessful += OnLocalizationMissionSuccessful; EmergencyActionService.EmergencyButtonPressedForRobot += OnEmergencyButtonPressedForRobot; EmergencyActionService.EmergencyButtonDepressedForRobot += OnEmergencyButtonDepressedForRobot; } @@ -51,7 +51,7 @@ public override void Unsubscribe() { MissionRunService.MissionRunCreated -= OnMissionRunCreated; MissionSchedulingService.RobotAvailable -= OnRobotAvailable; - MissionSchedulingService.MissionCompleted -= OnMissionCompleted; + MissionSchedulingService.LocalizationMissionSuccessful -= OnLocalizationMissionSuccessful; EmergencyActionService.EmergencyButtonPressedForRobot -= OnEmergencyButtonPressedForRobot; EmergencyActionService.EmergencyButtonDepressedForRobot -= OnEmergencyButtonDepressedForRobot; } @@ -126,9 +126,9 @@ private async void OnRobotAvailable(object? sender, RobotAvailableEventArgs e) finally { _startMissionSemaphore.Release(); } } - private async void OnMissionCompleted(object? sender, MissionCompletedEventArgs e) + private async void OnLocalizationMissionSuccessful(object? sender, LocalizationMissionSuccessfulEventArgs e) { - _logger.LogInformation("Triggered MissionCompleted event for robot ID: {RobotId}", e.RobotId); + _logger.LogInformation("Triggered LocalizationMissionSuccessful event for robot ID: {RobotId}", e.RobotId); var robot = await RobotService.ReadById(e.RobotId); if (robot == null) { diff --git a/backend/api/EventHandlers/MqttEventHandler.cs b/backend/api/EventHandlers/MqttEventHandler.cs index a7bccf7a1..953649c68 100644 --- a/backend/api/EventHandlers/MqttEventHandler.cs +++ b/backend/api/EventHandlers/MqttEventHandler.cs @@ -271,6 +271,9 @@ private async void OnIsarMissionUpdate(object? sender, MqttReceivedArgs mqttArgs try { await robotService.UpdateCurrentArea(flotillaMissionRun.Robot.Id, null); + _logger.LogError("Localization mission run {MissionRunId} was unsuccessful on {RobotId}, scheduled missions will be aborted", flotillaMissionRun.Id, flotillaMissionRun.Robot.Id); + try { await missionSchedulingService.AbortAllScheduledMissions(flotillaMissionRun.Robot.Id, "Aborted: Robot was not localized"); } + catch (RobotNotFoundException) { _logger.LogError("Failed to abort scheduled missions for robot {RobotId}", flotillaMissionRun.Robot.Id); } } catch (RobotNotFoundException) { @@ -322,7 +325,11 @@ private async void OnIsarMissionUpdate(object? sender, MqttReceivedArgs mqttArgs } _logger.LogInformation("Robot '{Id}' ('{Name}') - completed mission run {MissionRunId}", robot.IsarId, robot.Name, updatedFlotillaMissionRun.Id); - missionSchedulingService.TriggerMissionCompleted(new MissionCompletedEventArgs(robot.Id)); + + if (updatedFlotillaMissionRun.IsLocalizationMission() && (updatedFlotillaMissionRun.Status == MissionStatus.Successful || updatedFlotillaMissionRun.Status == MissionStatus.PartiallySuccessful)) + { + missionSchedulingService.TriggerLocalizationMissionSuccessful(new LocalizationMissionSuccessfulEventArgs(robot.Id)); + } if (updatedFlotillaMissionRun.MissionId == null) { diff --git a/backend/api/Services/Events/MissionEventArgs.cs b/backend/api/Services/Events/MissionEventArgs.cs index 6d39a7737..9b75e6b9f 100644 --- a/backend/api/Services/Events/MissionEventArgs.cs +++ b/backend/api/Services/Events/MissionEventArgs.cs @@ -9,7 +9,7 @@ public class RobotAvailableEventArgs(string robotId) : EventArgs { public string RobotId { get; } = robotId; } - public class MissionCompletedEventArgs(string robotId) : EventArgs + public class LocalizationMissionSuccessfulEventArgs(string robotId) : EventArgs { public string RobotId { get; } = robotId; } diff --git a/backend/api/Services/MissionSchedulingService.cs b/backend/api/Services/MissionSchedulingService.cs index 57fb4aaa2..1701e0a90 100644 --- a/backend/api/Services/MissionSchedulingService.cs +++ b/backend/api/Services/MissionSchedulingService.cs @@ -26,7 +26,7 @@ public interface IMissionSchedulingService public void TriggerRobotAvailable(RobotAvailableEventArgs e); - public void TriggerMissionCompleted(MissionCompletedEventArgs e); + public void TriggerLocalizationMissionSuccessful(LocalizationMissionSuccessfulEventArgs e); } @@ -312,9 +312,9 @@ public void TriggerRobotAvailable(RobotAvailableEventArgs e) OnRobotAvailable(e); } - public void TriggerMissionCompleted(MissionCompletedEventArgs e) + public void TriggerLocalizationMissionSuccessful(LocalizationMissionSuccessfulEventArgs e) { - OnMissionCompleted(e); + OnLocalizationMissionSuccessful(e); } private async Task SelectNextMissionRun(string robotId) @@ -542,7 +542,7 @@ private static float CalculateDistance(Pose pose1, Pose pose2) protected virtual void OnRobotAvailable(RobotAvailableEventArgs e) { RobotAvailable?.Invoke(this, e); } public static event EventHandler? RobotAvailable; - protected virtual void OnMissionCompleted(MissionCompletedEventArgs e) { MissionCompleted?.Invoke(this, e); } - public static event EventHandler? MissionCompleted; + protected virtual void OnLocalizationMissionSuccessful(LocalizationMissionSuccessfulEventArgs e) { LocalizationMissionSuccessful?.Invoke(this, e); } + public static event EventHandler? LocalizationMissionSuccessful; } }