Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete localisation missions #1613

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions backend/api.test/Database/DatabaseUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ public async Task<MissionRun> NewMissionRun(
Map = new MapMetadata(),
InstallationCode = installationCode
};
if (missionRunType == MissionRunType.Localization)
{
missionRun.Tasks = new List<MissionTask>
{
new(new Pose(), MissionTaskType.Localization)
};
}
if (missionRunType == MissionRunType.ReturnHome)
{
missionRun.Tasks = new List<MissionTask>
Expand Down
62 changes: 1 addition & 61 deletions backend/api.test/EventHandlers/TestMissionEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public TestMissionEventHandler(DatabaseFixture fixture)
var areaService = new AreaService(context, installationService, plantService, deckService, defaultLocalizationPoseService, accessRoleService);
var mapServiceMock = new MockMapService();
_robotService = new RobotService(context, robotServiceLogger, robotModelService, signalRService, accessRoleService, installationService, areaService, _missionRunService);
_localizationService = new LocalizationService(localizationServiceLogger, _robotService, _missionRunService, installationService, areaService, mapServiceMock);
_localizationService = new LocalizationService(localizationServiceLogger, _robotService, installationService, areaService);

var returnToHomeService = new ReturnToHomeService(returnToHomeServiceLogger, _robotService, _missionRunService, mapServiceMock);
_missionSchedulingService = new MissionSchedulingService(missionSchedulingServiceLogger, _missionRunService, _robotService, areaService,
Expand Down Expand Up @@ -291,66 +291,6 @@ public async void MissionRunIsStartedForOtherAvailableRobotIfOneRobotHasAnOngoin
Assert.Equal(MissionStatus.Ongoing, postStartMissionRunTwo.Status);
}

[Fact]
public async void QueuedMissionsAreAbortedWhenLocalizationFails()
{
// 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, area);
var localizationMissionRun = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true, MissionRunType.Localization, MissionStatus.Ongoing, Guid.NewGuid().ToString());
var missionRun1 = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true);

Thread.Sleep(100);

var mqttEventArgs = new MqttReceivedArgs(
new IsarMissionMessage
{
RobotName = robot.Name,
IsarId = robot.IsarId,
MissionId = localizationMissionRun.IsarMissionId,
Status = "failed",
Timestamp = DateTime.UtcNow
});

// Act
_mqttService.RaiseEvent(nameof(MqttService.MqttIsarMissionReceived), mqttEventArgs);
Thread.Sleep(500);

// Assert
var postTestMissionRun = await _missionRunService.ReadById(missionRun1.Id);
Assert.Equal(MissionStatus.Aborted, postTestMissionRun!.Status);
}

[Fact]
public async void LocalizationMissionCompletesAfterPressingSendToSafeZoneButton()
{
// 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.Busy, installation, area);
await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, true, MissionRunType.Localization, MissionStatus.Ongoing, Guid.NewGuid().ToString());

Thread.Sleep(100);

// Act
var eventArgs = new EmergencyButtonPressedForRobotEventArgs(robot.Id);
_emergencyActionService.RaiseEvent(nameof(EmergencyActionService.EmergencyButtonPressedForRobot), eventArgs);

Thread.Sleep(1000);

// Assert
var updatedRobot = await _robotService.ReadById(robot.Id);
Assert.True(updatedRobot?.MissionQueueFrozen);

bool isRobotLocalized = await _localizationService.RobotIsLocalized(robot.Id);
Assert.True(isRobotLocalized);
}

[Fact]
public async void ReturnHomeMissionNotScheduledIfRobotIsNotLocalized()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ public MissionRunQueryStringParameters()
/// </summary>
public MissionRunType? MissionRunType { get; set; }

/// <summary>
/// Filter for whether the result should exclude localization missions. The default is false
/// </summary>
///
public bool ExcludeLocalization { get; set; }

/// <summary>
/// Filter for whether the result should exclude return to home missions. The default is false
/// </summary>
Expand Down
5 changes: 1 addition & 4 deletions backend/api/Database/Models/MissionRun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,6 @@ var inspection in task.Inspections.Where(inspection => !inspection.IsCompleted)
}
}

public bool IsLocalizationMission() { return Tasks is [{ Type: MissionTaskType.Localization }]; }

public bool IsReturnHomeMission() { return Tasks is [{ Type: MissionTaskType.ReturnHome }]; }
}

Expand All @@ -211,7 +209,6 @@ public enum MissionRunType
{
Normal,
ReturnHome,
Emergency,
Localization
Emergency
}
}
72 changes: 3 additions & 69 deletions backend/api/EventHandlers/MissionEventHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Api.Controllers.Models;
using Api.Database.Models;
using Api.Database.Models;
using Api.Services;
using Api.Services.Events;
using Api.Utilities;
Expand All @@ -12,7 +11,6 @@ public class MissionEventHandler : EventHandlerBase

// The mutex is used to ensure multiple missions aren't attempted scheduled simultaneously whenever multiple mission runs are created
private readonly Semaphore _startMissionSemaphore = new(1, 1);
private readonly Semaphore _scheduleLocalizationSemaphore = new(1, 1);

private readonly IServiceScopeFactory _scopeFactory;

Expand Down Expand Up @@ -72,58 +70,12 @@ private async void OnMissionRunCreated(object? sender, MissionRunCreatedEventArg
return;
}


if (!await LocalizationService.RobotIsLocalized(missionRun.Robot.Id))
{
if (missionRun.Robot.RobotCapabilities != null && !missionRun.Robot.RobotCapabilities.Contains(RobotCapabilitiesEnum.localize))
{
await RobotService.UpdateCurrentArea(missionRun.Robot.Id, missionRun.Area);
}
else
{
_scheduleLocalizationSemaphore.WaitOne();
if (await MissionService.PendingLocalizationMissionRunExists(missionRun.Robot.Id)
|| await MissionService.OngoingLocalizationMissionRunExists(missionRun.Robot.Id))
{
_scheduleLocalizationSemaphore.Release();
return;
}

try
{
var localizationMissionRun = await LocalizationService.CreateLocalizationMissionInArea(missionRun.Robot.Id, missionRun.Area.Id);
_logger.LogInformation("{Message}", $"Created localization mission run with ID {localizationMissionRun.Id}");
}
catch (RobotNotAvailableException)
{
_logger.LogError("Mission run {MissionRunId} will be aborted as robot {RobotId} was not available", missionRun.Id, missionRun.Robot.Id);
missionRun.Status = MissionStatus.Aborted;
missionRun.StatusReason = "Aborted: Robot was not available";
await MissionService.Update(missionRun);
return;
}
catch (Exception ex) when (
ex is AreaNotFoundException
or DeckNotFoundException
or RobotNotFoundException
or IsarCommunicationException
)
{
_logger.LogError("Mission run {MissionRunId} will be aborted as robot {RobotId} was not correctly localized", missionRun.Id, missionRun.Robot.Id);
missionRun.Status = MissionStatus.Aborted;
missionRun.StatusReason = "Aborted: Robot was not correctly localized";
await MissionService.Update(missionRun);
return;
}
finally { _scheduleLocalizationSemaphore.Release(); }
}
}

await CancelReturnToHomeOnNewMissionSchedule(missionRun);

_startMissionSemaphore.WaitOne();
try { await MissionScheduling.StartNextMissionRunIfSystemIsAvailable(missionRun.Robot.Id); }
catch (MissionRunNotFoundException) { return; }
catch (RobotCurrentAreaMissingException) { return; }
finally { _startMissionSemaphore.Release(); }
}

Expand Down Expand Up @@ -222,7 +174,6 @@ private async void OnEmergencyButtonPressedForRobot(object? sender, EmergencyBut
SignalRService.ReportSafeZoneFailureToSignalR(robot, $"Failed to send {robot.Name} to a safe zone");
}

if (await MissionService.PendingOrOngoingLocalizationMissionRunExists(e.RobotId)) { return; }
try { await MissionScheduling.StopCurrentMissionRun(e.RobotId); }
catch (RobotNotFoundException) { return; }
catch (MissionRunNotFoundException)
Expand Down Expand Up @@ -283,24 +234,7 @@ private async void OnEmergencyButtonDepressedForRobot(object? sender, EmergencyB

if (!await LocalizationService.RobotIsLocalized(robotId))
{

if (await MissionService.PendingOrOngoingLocalizationMissionRunExists(robotId))
{
var missionRuns = await MissionService.ReadAll(
new MissionRunQueryStringParameters
{
Statuses = [MissionStatus.Ongoing, MissionStatus.Pending],
RobotId = robot.Id,
OrderBy = "DesiredStartTime",
PageSize = 100
});

var localizationMission = missionRuns.Find(missionRun => missionRun.IsLocalizationMission());

return localizationMission?.Area ?? null;
}

_logger.LogError("Robot {RobotName} is not localized and no localization mission is ongoing.", robot.Name);
_logger.LogError("Robot {RobotName} is not localized.", robot.Name);
SignalRService.ReportSafeZoneFailureToSignalR(robot, $"Robot {robot.Name} has not been localised.");
return null;
}
Expand Down
44 changes: 19 additions & 25 deletions backend/api/EventHandlers/MqttEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ private async void OnIsarMissionUpdate(object? sender, MqttReceivedArgs mqttArgs
var taskDurationService = provider.GetRequiredService<ITaskDurationService>();
var lastMissionRunService = provider.GetRequiredService<ILastMissionRunService>();
var missionSchedulingService = provider.GetRequiredService<IMissionSchedulingService>();
var signalRService = provider.GetRequiredService<ISignalRService>();
_ = provider.GetRequiredService<ISignalRService>();

var isarMission = (IsarMissionMessage)mqttArgs.Message;

Expand Down Expand Up @@ -259,34 +259,28 @@ private async void OnIsarMissionUpdate(object? sender, MqttReceivedArgs mqttArgs
return;
}

if (flotillaMissionRun.IsLocalizationMission())
if (flotillaMissionRun.Status != MissionStatus.Successful)
{
if (flotillaMissionRun.Status != MissionStatus.Successful)
try
{
try
{
await robotService.UpdateCurrentArea(robot.Id, null);
}
catch (RobotNotFoundException)
{
_logger.LogError("Could not find robot '{RobotName}' with ID '{Id}'", robot.Name, robot.Id);
return;
}

signalRService.ReportGeneralFailToSignalR(robot, "Failed Localization Mission", $"Failed localization mission for robot {robot.Name}.");
_logger.LogError("Localization mission for robot '{RobotName}' failed.", isarMission.RobotName);
await robotService.UpdateCurrentArea(robot.Id, null); // TODO: don't ever set area to null, and if it is null, then just set it when you receive a mission
}
else
catch (RobotNotFoundException)
{
try
{
await robotService.UpdateCurrentArea(robot.Id, flotillaMissionRun.Area);
}
catch (RobotNotFoundException)
{
_logger.LogError("Could not find robot '{RobotName}' with ID '{Id}'", robot.Name, robot.Id);
return;
}
_logger.LogError("Could not find robot '{RobotName}' with ID '{Id}'", robot.Name, robot.Id);
return;
}
}
else
{
try
{
await robotService.UpdateCurrentArea(robot.Id, flotillaMissionRun.Area);
}
catch (RobotNotFoundException)
{
_logger.LogError("Could not find robot '{RobotName}' with ID '{Id}'", robot.Name, robot.Id);
return;
}
}

Expand Down
70 changes: 2 additions & 68 deletions backend/api/Services/LocalizationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ namespace Api.Services
{
public interface ILocalizationService
{
public Task<MissionRun> CreateLocalizationMissionInArea(string robotId, string areaId);
public Task EnsureRobotIsOnSameInstallationAsMission(Robot robot, MissionDefinition missionDefinition);
public Task<bool> RobotIsLocalized(string robotId);
public Task<bool> RobotIsOnSameDeckAsMission(string robotId, string areaId);
}

public class LocalizationService(ILogger<LocalizationService> logger, IRobotService robotService, IMissionRunService missionRunService, IInstallationService installationService, IAreaService areaService, IMapService mapService) : ILocalizationService
public class LocalizationService(ILogger<LocalizationService> logger, IRobotService robotService, IInstallationService installationService, IAreaService areaService) : ILocalizationService
{

public async Task EnsureRobotIsOnSameInstallationAsMission(Robot robot, MissionDefinition missionDefinition)
Expand Down Expand Up @@ -45,72 +44,6 @@ public async Task<bool> RobotIsLocalized(string robotId)
return robot.CurrentArea is not null;
}

public async Task<MissionRun> CreateLocalizationMissionInArea(string robotId, string areaId)
{
var robot = await robotService.ReadById(robotId);
if (robot is null)
{
string errorMessage = $"The robot with ID {robotId} was not found";
logger.LogError("{Message}", errorMessage);
throw new RobotNotFoundException(errorMessage);
}

var area = await areaService.ReadById(areaId);
if (area is null)
{
string errorMessage = $"The area with ID {areaId} was not found";
logger.LogError("{Message}", errorMessage);
throw new AreaNotFoundException(errorMessage);
}

if (area.Deck?.DefaultLocalizationPose?.Pose is null)
{
const string ErrorMessage = "The mission area is not associated with any deck or that deck does not have a localization pose";
logger.LogError("{Message}", ErrorMessage);
throw new DeckNotFoundException(ErrorMessage);
}
if (robot.Status is not RobotStatus.Available)
{
string errorMessage = $"Robot '{robot.Id}' is not available as the status is {robot.Status}";
logger.LogWarning("{Message}", errorMessage);
throw new RobotNotAvailableException(errorMessage);
}
if (robot.Deprecated)
{
string errorMessage = $"Robot '{robot.Id}' is deprecated and cannot localize";
logger.LogWarning("{Message}", errorMessage);
throw new RobotNotAvailableException(errorMessage);
}

var localizationMissionRun = new MissionRun
{
Name = "Localization mission",
Robot = robot,
MissionRunType = MissionRunType.Localization,
InstallationCode = area.Installation.InstallationCode,
Area = area,
Status = MissionStatus.Pending,
DesiredStartTime = DateTime.UtcNow,
Tasks = new List<MissionTask>
{
new(new Pose(area.Deck.DefaultLocalizationPose.Pose), MissionTaskType.Localization)
},
Map = new MapMetadata()
};
await mapService.AssignMapToMission(localizationMissionRun);

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;
}

public async Task<bool> RobotIsOnSameDeckAsMission(string robotId, string areaId)
{
var robot = await robotService.ReadById(robotId);
Expand All @@ -123,6 +56,7 @@ public async Task<bool> RobotIsOnSameDeckAsMission(string robotId, string areaId

if (robot.CurrentArea is null)
{
// TODO: if area is null then just set it. Use another variable for failed localisation or whatever. We can't detect the robot being moved anyways
const string ErrorMessage = "The robot is not associated with an area and a mission may not be started";
logger.LogError("{Message}", ErrorMessage);
throw new RobotCurrentAreaMissingException(ErrorMessage);
Expand Down
Loading
Loading