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

Move create return home to starting mission #1963

Merged
merged 1 commit into from
Jan 17, 2025
Merged
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
43 changes: 0 additions & 43 deletions backend/api.test/EventHandlers/TestMissionEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,49 +192,6 @@ public async Task NewMissionIsStartedWhenRobotBecomesAvailable()
Assert.Equal(MissionStatus.Ongoing, postTestMissionRun!.Status);
}

[Fact]
public async Task ReturnToHomeMissionIsStartedIfQueueIsEmptyWhenRobotBecomesAvailable()
{
// Arrange
var installation = await DatabaseUtilities.NewInstallation();
var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode);
var inspectionArea = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode
);
var robot = await DatabaseUtilities.NewRobot(
RobotStatus.Busy,
installation,
inspectionArea
);

var mqttEventArgs = new MqttReceivedArgs(
new IsarStatusMessage
{
RobotName = robot.Name,
IsarId = robot.IsarId,
Status = RobotStatus.Available,
Timestamp = DateTime.UtcNow,
}
);

// Act
MqttService.RaiseEvent(nameof(MqttService.MqttIsarStatusReceived), mqttEventArgs);

// Assert
Thread.Sleep(1000);
var ongoingMission = await MissionRunService.ReadAll(
new MissionRunQueryStringParameters
{
Statuses = [MissionStatus.Ongoing],
OrderBy = "DesiredStartTime",
PageSize = 100,
},
readOnly: true
);
Assert.True(ongoingMission.Any());
}

[Fact]
public async Task ReturnToHomeMissionIsNotStartedIfReturnToHomeIsNotSupported()
{
Expand Down
36 changes: 1 addition & 35 deletions backend/api.test/Services/MissionRunService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Api.Controllers.Models;
using Api.Database.Models;
Expand Down Expand Up @@ -37,40 +38,5 @@ public async Task CheckThatReadByIdWithUnknownIdFails()
var missionRun = await MissionRunService.ReadById("IDoNotExist", readOnly: true);
Assert.Null(missionRun);
}

[Fact]
public async Task CheckThatNumberOfMissionRunReportsIncreaseByOneWhenNewMissionRunIsCreated()
{
// Arrange
var installation = await DatabaseUtilities.NewInstallation();
var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode);
var inspectionArea = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode
);
var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation);
var missionRun = await DatabaseUtilities.NewMissionRun(
installation.InstallationCode,
robot,
inspectionArea
);

var reportsBefore = await MissionRunService.ReadAll(
new MissionRunQueryStringParameters(),
readOnly: true
);
int nReportsBefore = reportsBefore.Count;

// Act
await MissionRunService.Create(missionRun);

// Assert
var reportsAfter = await MissionRunService.ReadAll(
new MissionRunQueryStringParameters()
);
int nReportsAfter = reportsAfter.Count;

Assert.Equal(nReportsBefore + 1, nReportsAfter);
}
}
}
76 changes: 76 additions & 0 deletions backend/api.test/Services/MissionSchedulingService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Api.Controllers.Models;
using Api.Database.Models;
using Api.Services;
using Api.Test.Database;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace Api.Test.Services
{
public class MissionSchedulingServiceTest : IAsyncLifetime
{
public required DatabaseUtilities DatabaseUtilities;
public required IMissionSchedulingService MissionSchedulingService;

public required IMissionRunService MissionRunService;

public async Task InitializeAsync()
{
string databaseName = Guid.NewGuid().ToString();
(string connectionString, var connection) = await TestSetupHelpers.ConfigureDatabase(
databaseName
);
var factory = TestSetupHelpers.ConfigureWebApplicationFactory(databaseName);
var serviceProvider = TestSetupHelpers.ConfigureServiceProvider(factory);

DatabaseUtilities = new DatabaseUtilities(
TestSetupHelpers.ConfigureFlotillaDbContext(connectionString)
);
MissionSchedulingService =
serviceProvider.GetRequiredService<IMissionSchedulingService>();
MissionRunService = serviceProvider.GetRequiredService<IMissionRunService>();
}

public Task DisposeAsync() => Task.CompletedTask;

[Fact]
public async Task CheckThatReturnHomeIsCreatedWhenRunningMission()
{
// Arrange
var installation = await DatabaseUtilities.NewInstallation();
var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode);
var inspectionArea = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode
);
var robot = await DatabaseUtilities.NewRobot(RobotStatus.Available, installation);
await DatabaseUtilities.NewMissionRun(
installation.InstallationCode,
robot,
inspectionArea,
writeToDatabase: true
);

var reportsBefore = await MissionRunService.ReadAll(
new MissionRunQueryStringParameters(),
readOnly: true
);
int nReportsBefore = reportsBefore.Count;

// Act
await MissionSchedulingService.StartNextMissionRunIfSystemIsAvailable(robot);

// Assert
var reportsAfter = await MissionRunService.ReadAll(
new MissionRunQueryStringParameters()
);
int nReportsAfter = reportsAfter.Count;

// We expect two new missions since a return home mission will also be scheduled
Assert.Equal(nReportsBefore + 1, nReportsAfter);
}
}
}
4 changes: 2 additions & 2 deletions backend/api/Controllers/ReturnToHomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ [FromRoute] string robotId
}

var returnToHomeMission =
await returnToHomeService.ScheduleReturnToHomeMissionRunIfNotAlreadyScheduledOrRobotIsHome(
robot.Id
await returnToHomeService.ScheduleReturnToHomeMissionRunIfNotAlreadyScheduled(
robot
);
if (returnToHomeMission is null)
{
Expand Down
2 changes: 1 addition & 1 deletion backend/api/Controllers/RobotController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ public async Task<ActionResult> ResetRobot([FromRoute] string robotId)

try
{
await missionSchedulingService.AbortAllScheduledMissions(
await missionSchedulingService.AbortAllScheduledNormalMissions(
robot.Id,
"Aborted: Robot was reset"
);
Expand Down
18 changes: 14 additions & 4 deletions backend/api/EventHandlers/IsarConnectionEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,20 @@ private async void OnTimeoutEvent(IsarRobotHeartbeatMessage robotHeartbeatMessag
missionRun.Id,
missionRun.Name
);
await MissionRunService.SetMissionRunToFailed(
missionRun.Id,
"Lost connection to ISAR during mission"
);
try
{
await MissionRunService.SetMissionRunToFailed(
missionRun.Id,
"Lost connection to ISAR during mission"
);
}
catch (MissionRunNotFoundException)
{
_logger.LogError(
"Mission '{MissionId}' could not be set to failed as it no longer exists",
missionRun.Id
);
}
}
}

Expand Down
11 changes: 1 addition & 10 deletions backend/api/EventHandlers/MissionEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ IServiceScopeFactory scopeFactory
private ISignalRService SignalRService =>
_scopeFactory.CreateScope().ServiceProvider.GetRequiredService<ISignalRService>();

private IReturnToHomeService ReturnToHomeService =>
_scopeFactory.CreateScope().ServiceProvider.GetRequiredService<IReturnToHomeService>();

public override void Subscribe()
{
MissionRunService.MissionRunCreated += OnMissionRunCreated;
Expand Down Expand Up @@ -71,13 +68,7 @@ private async void OnMissionRunCreated(object? sender, MissionRunCreatedEventArg

_startMissionSemaphore.WaitOne();

if (
missionRun.MissionRunType != MissionRunType.ReturnHome
&& await ReturnToHomeService.GetActiveReturnToHomeMissionRun(
missionRun.Robot.Id,
readOnly: true
) != null
)
if (missionRun.MissionRunType != MissionRunType.ReturnHome)
{
await MissionScheduling.AbortActiveReturnToHomeMission(missionRun.Robot.Id);
}
Expand Down
59 changes: 34 additions & 25 deletions backend/api/Services/MissionRunService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,20 @@ public Task<PagedList<MissionRun>> ReadAll(

public Task<MissionRun?> ReadByIsarMissionId(string isarMissionId, bool readOnly = true);

public Task<IList<MissionRun>> ReadMissionRunQueue(string robotId, bool readOnly = true);
public Task<IList<MissionRun>> ReadMissionRunQueue(
string robotId,
MissionRunType type = MissionRunType.Normal,
bool readOnly = true
);

public Task<MissionRun?> ReadNextScheduledRunByMissionId(
string missionId,
bool readOnly = true
);

public Task<MissionRun?> ReadNextScheduledMissionRun(string robotId, bool readOnly = true);

public Task<MissionRun?> ReadNextScheduledEmergencyMissionRun(
public Task<MissionRun?> ReadNextScheduledMissionRun(
string robotId,
MissionRunType type = MissionRunType.Normal,
bool readOnly = true
);

Expand Down Expand Up @@ -188,40 +191,32 @@ public async Task<PagedList<MissionRun>> ReadAll(

public async Task<IList<MissionRun>> ReadMissionRunQueue(
string robotId,
MissionRunType type = MissionRunType.Normal,
bool readOnly = true
)
{
return await GetMissionRunsWithSubModels(readOnly: readOnly)
.Where(missionRun =>
missionRun.Robot.Id == robotId && missionRun.Status == MissionStatus.Pending
missionRun.Robot.Id == robotId
&& missionRun.Status == MissionStatus.Pending
&& missionRun.MissionRunType == type
)
.OrderBy(missionRun => missionRun.DesiredStartTime)
.ToListAsync();
}

public async Task<MissionRun?> ReadNextScheduledMissionRun(
string robotId,
bool readOnly = true
)
{
return await GetMissionRunsWithSubModels(readOnly: readOnly)
.OrderBy(missionRun => missionRun.DesiredStartTime)
.FirstOrDefaultAsync(missionRun =>
missionRun.Robot.Id == robotId && missionRun.Status == MissionStatus.Pending
);
}

public async Task<MissionRun?> ReadNextScheduledEmergencyMissionRun(
string robotId,
MissionRunType type = MissionRunType.Normal,
bool readOnly = true
)
{
return await GetMissionRunsWithSubModels(readOnly: readOnly)
.OrderBy(missionRun => missionRun.DesiredStartTime)
.FirstOrDefaultAsync(missionRun =>
missionRun.Robot.Id == robotId
&& missionRun.MissionRunType == MissionRunType.Emergency
&& missionRun.Status == MissionStatus.Pending
&& missionRun.MissionRunType == type
);
}

Expand Down Expand Up @@ -273,8 +268,12 @@ public async Task<IList<MissionRun>> ReadMissionRuns(

public async Task<bool> PendingOrOngoingReturnToHomeMissionRunExists(string robotId)
{
var pendingMissionRuns = await ReadMissionRunQueue(robotId, readOnly: true);
if (pendingMissionRuns.Any((m) => m.IsReturnHomeMission()))
var pendingMissionRuns = await ReadNextScheduledMissionRun(
robotId,
type: MissionRunType.ReturnHome,
readOnly: true
);
if (pendingMissionRuns != null)
return true;

var ongoingMissionRuns = await GetMissionRunsWithSubModels(readOnly: true)
Expand Down Expand Up @@ -728,13 +727,23 @@ await robotService.ReadById(robotId, readOnly: true)
);
if (robot.CurrentMissionId != null)
{
var missionRun = await SetMissionRunToFailed(
robot.CurrentMissionId,
"Lost connection to ISAR during mission"
);
try
{
await SetMissionRunToFailed(
robot.CurrentMissionId,
"Lost connection to ISAR during mission"
);
}
catch (MissionRunNotFoundException)
{
logger.LogError(
"Mission '{MissionId}' could not be set to failed as it no longer exists",
robot.CurrentMissionId
);
}
logger.LogWarning(
"Mission '{Id}' failed because ISAR could not be reached",
missionRun.Id
robot.CurrentMissionId
);
}
}
Expand Down
Loading
Loading