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

Implement localization as part of a mission #1071

Closed
wants to merge 18 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
9 changes: 6 additions & 3 deletions backend/api.test/Client/AreaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public async Task AreaTest()
}

[Fact]
public async Task GetMissionsInAreaTest()
public async Task MissionIsCreatedInArea()
{
// Arrange
// Robot
Expand All @@ -156,15 +156,15 @@ public async Task GetMissionsInAreaTest()
Assert.True(installationResponse.IsSuccessStatusCode);
var installations = await installationResponse.Content.ReadFromJsonAsync<List<Installation>>(_serializerOptions);
Assert.True(installations != null);
var installation = installations[0];
var installation = installations.Where(installation => installation.InstallationCode == robot.CurrentInstallation?.InstallationCode).First();

// Area
string areaUrl = "/areas";
var areaResponse = await _client.GetAsync(areaUrl);
Assert.True(areaResponse.IsSuccessStatusCode);
var areas = await areaResponse.Content.ReadFromJsonAsync<List<AreaResponse>>(_serializerOptions);
Assert.True(areas != null);
var area = areas[0];
var area = areas.Where(area => area.InstallationCode == installation.InstallationCode).First();
string areaId = area.Id;

string testMissionName = "testMissionInAreaTest";
Expand Down Expand Up @@ -272,6 +272,9 @@ public async Task SafePositionTest()
// Assert
Assert.True(missionResponse.IsSuccessStatusCode);

// The endpoint posted to above triggers an event and returns a successful response.
// The test finishes and disposes of objects, but the operations of that event handler are still running, leading to a crash.
await Task.Delay(5000);
}

[Fact]
Expand Down
248 changes: 220 additions & 28 deletions backend/api.test/Client/MissionTests.cs

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions backend/api.test/Database/DatabaseUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public DatabaseUtilities(FlotillaDbContext context)
_areaService = new AreaService(context, _installationService, _plantService, _deckService, defaultLocalizationPoseService, _accessRoleService);
_missionRunService = new MissionRunService(context, new MockSignalRService(), new Mock<ILogger<MissionRunService>>().Object, _accessRoleService);
_robotModelService = new RobotModelService(context);
_robotService = new RobotService(context, new Mock<ILogger<RobotService>>().Object, _robotModelService, new MockSignalRService(), _accessRoleService, _installationService);
_robotService = new RobotService(context, new Mock<ILogger<RobotService>>().Object, _robotModelService, new MockSignalRService(), _accessRoleService, _installationService, _areaService);
}

public void Dispose()
Expand Down Expand Up @@ -116,7 +116,7 @@ public async Task<Area> NewArea(string installationCode, string plantCode, strin
return await _areaService.Create(createAreaQuery);
}

public async Task<Robot> NewRobot(RobotStatus status, Installation installation)
public async Task<Robot> NewRobot(RobotStatus status, Installation installation, Area? area = null)
{
var createRobotQuery = new CreateRobotQuery
{
Expand All @@ -125,6 +125,7 @@ public async Task<Robot> NewRobot(RobotStatus status, Installation installation)
RobotType = RobotType.Robot,
SerialNumber = "0001",
CurrentInstallationCode = installation.InstallationCode,
CurrentAreaName = area?.Name,
VideoStreams = new List<CreateVideoStreamQuery>(),
Host = "localhost",
Port = 3000,
Expand All @@ -133,7 +134,7 @@ public async Task<Robot> NewRobot(RobotStatus status, Installation installation)
};

var robotModel = await _robotModelService.ReadByRobotType(createRobotQuery.RobotType);
var robot = new Robot(createRobotQuery, installation)
var robot = new Robot(createRobotQuery, installation, area)
{
Model = robotModel!
};
Expand Down
22 changes: 12 additions & 10 deletions backend/api.test/EventHandlers/TestMissionEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,14 @@ public class TestMissionEventHandler : IDisposable
private readonly MissionEventHandler _missionEventHandler;
private readonly MissionRunService _missionRunService;
private readonly IMissionSchedulingService _missionSchedulingService;

#pragma warning disable IDE0052
private readonly MqttEventHandler _mqttEventHandler;
#pragma warning restore IDE0052

private readonly MqttService _mqttService;
private readonly PlantService _plantService;
private readonly RobotControllerMock _robotControllerMock;
private readonly RobotModelService _robotModelService;
private readonly RobotService _robotService;
private readonly ISignalRService _signalRService;
private readonly LocalizationService _localizationService;
private readonly DatabaseUtilities _databaseUtilities;
private readonly AccessRoleService _accessRoleService;

Expand All @@ -57,6 +54,7 @@ public TestMissionEventHandler(DatabaseFixture fixture)
var missionLogger = new Mock<ILogger<MissionRunService>>().Object;
var missionSchedulingServiceLogger = new Mock<ILogger<MissionSchedulingService>>().Object;
var robotServiceLogger = new Mock<ILogger<RobotService>>().Object;
var localizationServiceLogger = new Mock<ILogger<LocalizationService>>().Object;

var configuration = WebApplication.CreateBuilder().Configuration;

Expand All @@ -75,9 +73,10 @@ public TestMissionEventHandler(DatabaseFixture fixture)
_plantService = new PlantService(_context, _installationService, _accessRoleService);
_deckService = new DeckService(_context, _defaultLocalisationPoseService, _installationService, _plantService, _accessRoleService);
_areaService = new AreaService(_context, _installationService, _plantService, _deckService, _defaultLocalisationPoseService, _accessRoleService);
_robotService = new RobotService(_context, robotServiceLogger, _robotModelService, _signalRService, _accessRoleService, _installationService);
_robotService = new RobotService(_context, robotServiceLogger, _robotModelService, _signalRService, _accessRoleService, _installationService, _areaService);
_missionSchedulingService = new MissionSchedulingService(missionSchedulingServiceLogger, _missionRunService, _robotService, _robotControllerMock.Mock.Object, _areaService,
_isarServiceMock);
_localizationService = new LocalizationService(localizationServiceLogger, _robotService, _missionRunService, _installationService, _areaService);

_databaseUtilities = new DatabaseUtilities(_context);

Expand All @@ -99,6 +98,9 @@ public TestMissionEventHandler(DatabaseFixture fixture)
mockServiceProvider
.Setup(p => p.GetService(typeof(FlotillaDbContext)))
.Returns(_context);
mockServiceProvider
.Setup(p => p.GetService(typeof(ILocalizationService)))
.Returns(_localizationService);

// Mock service injector
var mockScope = new Mock<IServiceScope>();
Expand All @@ -125,7 +127,7 @@ public async void ScheduledMissionStartedWhenSystemIsAvailable()
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);
var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, area);
var missionRun = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, false);

SetupMocksForRobotController(robot, missionRun);
Expand All @@ -146,7 +148,7 @@ public async void SecondScheduledMissionQueuedIfRobotIsBusy()
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);
var robot = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, area);
var missionRunOne = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, false);
var missionRunTwo = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, false);

Expand All @@ -171,7 +173,7 @@ public async void NewMissionIsStartedWhenRobotBecomesAvailable()
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);
var robot = await _databaseUtilities.NewRobot(RobotStatus.Busy, installation, area);
var missionRun = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robot, area, false);

SetupMocksForRobotController(robot, missionRun);
Expand Down Expand Up @@ -248,8 +250,8 @@ public async void MissionRunIsStartedForOtherAvailableRobotIfOneRobotHasAnOngoin
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 robotOne = await _databaseUtilities.NewRobot(RobotStatus.Available, installation);
var robotTwo = await _databaseUtilities.NewRobot(RobotStatus.Available, installation);
var robotOne = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, area);
var robotTwo = await _databaseUtilities.NewRobot(RobotStatus.Available, installation, area);
var missionRunOne = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robotOne, area, false);
var missionRunTwo = await _databaseUtilities.NewMissionRun(installation.InstallationCode, robotTwo, area, false);

Expand Down
18 changes: 11 additions & 7 deletions backend/api.test/Mocks/CustomMissionServiceMock.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Api.Controllers.Models;
using Api.Database.Models;

namespace Api.Services
{
public class MockCustomMissionService : ICustomMissionService
Expand Down Expand Up @@ -36,16 +37,19 @@ public Task<string> UploadSource(List<MissionTask> tasks)

public string CalculateHashFromTasks(IList<MissionTask> tasks)
{
List<MissionTask> genericTasks = [];
foreach (var task in tasks)
{
var taskCopy = new MissionTask(task);
genericTasks.Add(taskCopy);
}
IList<MissionTask> genericTasks = tasks.Select(task => new MissionTask(task)).ToList();

string json = JsonSerializer.Serialize(genericTasks);
byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
return BitConverter.ToString(hash).Replace("-", "", StringComparison.CurrentCulture).ToUpperInvariant();
}

#pragma warning disable IDE0060, CA1822 // Remove unused parameter
public async Task<MissionRun> QueueCustomMissionRun(CustomMissionQuery customMissionQuery, MissionDefinition customMissionDefinition, Robot robot, IList<MissionTask> missionTasks)
{
await Task.CompletedTask;
return new MissionRun();
}
#pragma warning restore IDE0060, CA1822 // Remove unused parameter
}
}
16 changes: 12 additions & 4 deletions backend/api.test/Services/RobotService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public class RobotServiceTest : IDisposable
private readonly ISignalRService _signalRService;
private readonly IAccessRoleService _accessRoleService;
private readonly IInstallationService _installationService;
private readonly IPlantService _plantService;
private readonly IDefaultLocalizationPoseService _defaultLocalizationPoseService;
private readonly IDeckService _deckService;
private readonly IAreaService _areaService;

public RobotServiceTest(DatabaseFixture fixture)
{
Expand All @@ -30,6 +34,10 @@ public RobotServiceTest(DatabaseFixture fixture)
_signalRService = new MockSignalRService();
_accessRoleService = new AccessRoleService(_context, new HttpContextAccessor());
_installationService = new InstallationService(_context, _accessRoleService);
_plantService = new PlantService(_context, _installationService, _accessRoleService);
_defaultLocalizationPoseService = new DefaultLocalizationPoseService(_context);
_deckService = new DeckService(_context, _defaultLocalizationPoseService, _installationService, _plantService, _accessRoleService);
_areaService = new AreaService(_context, _installationService, _plantService, _deckService, _defaultLocalizationPoseService, _accessRoleService);
}

public void Dispose()
Expand All @@ -41,7 +49,7 @@ public void Dispose()
[Fact]
public async Task ReadAll()
{
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService);
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService, _areaService);
var robots = await robotService.ReadAll();

Assert.True(robots.Any());
Expand All @@ -50,7 +58,7 @@ public async Task ReadAll()
[Fact]
public async Task Read()
{
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService);
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService, _areaService);
var robots = await robotService.ReadAll();
var firstRobot = robots.First();
var robotById = await robotService.ReadById(firstRobot.Id);
Expand All @@ -61,15 +69,15 @@ public async Task Read()
[Fact]
public async Task ReadIdDoesNotExist()
{
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService);
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService, _areaService);
var robot = await robotService.ReadById("some_id_that_does_not_exist");
Assert.Null(robot);
}

[Fact]
public async Task Create()
{
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService);
var robotService = new RobotService(_context, _logger, _robotModelService, _signalRService, _accessRoleService, _installationService, _areaService);
var installationService = new InstallationService(_context, _accessRoleService);

var installation = await installationService.Create(new CreateInstallationQuery
Expand Down
33 changes: 16 additions & 17 deletions backend/api/Controllers/MissionSchedulingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ namespace Api.Controllers
[Route("missions")]
public class MissionSchedulingController(
IMissionDefinitionService missionDefinitionService,
ICustomMissionSchedulingService customMissionSchedulingService,
IMissionRunService missionRunService,
IInstallationService installationService,
IRobotService robotService,
IEchoService echoService,
ILogger<MissionSchedulingController> logger,
IMapService mapService,
IStidService stidService,
ISourceService sourceService,
ICustomMissionSchedulingService customMissionSchedulingService
ILocalizationService localizationService,
ISourceService sourceService
) : ControllerBase
{

/// <summary>
/// Schedule an existing mission definition
/// </summary>
Expand All @@ -42,24 +44,17 @@ [FromBody] ScheduleMissionQuery scheduledMissionQuery
)
{
var robot = await robotService.ReadById(scheduledMissionQuery.RobotId);
if (robot is null)
{
return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}");
}
if (robot is null) { return NotFound($"Could not find robot with id {scheduledMissionQuery.RobotId}"); }

var missionDefinition = await missionDefinitionService.ReadById(scheduledMissionQuery.MissionDefinitionId);
if (missionDefinition == null)
{
return NotFound("Mission definition not found");
}
if (missionDefinition == null) { return NotFound("Mission definition not found"); }

List<MissionTask>? missionTasks;
missionTasks = await missionDefinitionService.GetTasksFromSource(missionDefinition.Source, missionDefinition.InstallationCode);
try { await localizationService.EnsureRobotIsOnSameInstallationAsMission(robot, missionDefinition); }
catch (InstallationNotFoundException e) { return NotFound(e.Message); }
catch (RobotNotInSameInstallationAsMissionException e) { return Conflict(e.Message); }

if (missionTasks == null)
{
return NotFound("No mission tasks were found for the requested mission");
}
var missionTasks = await missionDefinitionService.GetTasksFromSource(missionDefinition.Source, missionDefinition.InstallationCode);
if (missionTasks == null) return NotFound("No mission tasks were found for the requested mission");

var missionRun = new MissionRun
{
Expand All @@ -72,7 +67,7 @@ [FromBody] ScheduleMissionQuery scheduledMissionQuery
Tasks = missionTasks,
InstallationCode = missionDefinition.InstallationCode,
Area = missionDefinition.Area,
Map = new MapMetadata()
Map = missionDefinition.Area?.MapMetadata ?? new MapMetadata()
};

await mapService.AssignMapToMission(missionRun);
Expand Down Expand Up @@ -270,6 +265,10 @@ [FromBody] CustomMissionQuery customMissionQuery
try { customMissionDefinition = await customMissionSchedulingService.FindExistingOrCreateCustomMissionDefinition(customMissionQuery, missionTasks); }
catch (SourceException e) { return StatusCode(StatusCodes.Status502BadGateway, e.Message); }

try { await localizationService.EnsureRobotIsOnSameInstallationAsMission(robot, customMissionDefinition); }
catch (InstallationNotFoundException e) { return NotFound(e.Message); }
catch (RobotNotInSameInstallationAsMissionException e) { return Conflict(e.Message); }

MissionRun? newMissionRun;
try { newMissionRun = await customMissionSchedulingService.QueueCustomMissionRun(customMissionQuery, customMissionDefinition.Id, robot.Id, missionTasks); }
catch (Exception e) when (e is RobotNotFoundException or MissionNotFoundException) { return NotFound(e.Message); }
Expand Down
2 changes: 2 additions & 0 deletions backend/api/Controllers/Models/CreateRobotQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public struct CreateRobotQuery

public string CurrentInstallationCode { get; set; }

public string? CurrentAreaName { get; set; }

public IList<CreateVideoStreamQuery> VideoStreams { get; set; }

public string Host { get; set; }
Expand Down
Loading
Loading