Skip to content
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
6 changes: 4 additions & 2 deletions backend/api.test/Database/DatabaseUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,11 @@ public async Task<MissionRun> NewMissionRun(
MissionRunType missionRunType = MissionRunType.Normal,
MissionStatus missionStatus = MissionStatus.Pending,
string isarMissionId = "",
Api.Database.Models.TaskStatus taskStatus = Api.Database.Models.TaskStatus.Successful
MissionTask[] tasks = null!
)
{
tasks ??= [];

if (string.IsNullOrEmpty(isarMissionId))
isarMissionId = Guid.NewGuid().ToString();
var missionRun = new MissionRun
Expand All @@ -110,7 +112,7 @@ public async Task<MissionRun> NewMissionRun(
Status = missionStatus,
DesiredStartTime = DateTime.UtcNow,
InspectionArea = inspectionArea,
Tasks = [],
Tasks = tasks,
InstallationCode = installationCode,
};

Expand Down
161 changes: 161 additions & 0 deletions backend/api.test/Services/MissionSchedulingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Api.Controllers.Models;
using Api.Database.Models;
using Api.Services;
using Api.Services.Models;
using Api.Test.Database;
using Microsoft.Extensions.DependencyInjection;
using Testcontainers.PostgreSql;
Expand Down Expand Up @@ -75,5 +76,165 @@ await DatabaseUtilities.NewMissionRun(
// We expect two new missions since a return home mission will also be scheduled
Assert.Equal(nReportsBefore + 1, nReportsAfter);
}

[Fact]
public async Task CheckThatReturnHomeIsNotDeletedWhenPreviousMissionWasOutsideInspectionArea()
{
// Arrange
var installation = await DatabaseUtilities.NewInstallation();
var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode);
var inspectionArea1 = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode,
inspectionAreaName: "Inspection area 1",
new InspectionAreaPolygon
{
ZMin = 0,
ZMax = 10,
Positions =
[
new XYPosition(x: 0, y: 0),
new XYPosition(x: 0, y: 10),
new XYPosition(x: 10, y: 10),
new XYPosition(x: 10, y: 0),
],
}
);
var inspectionArea2 = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode,
inspectionAreaName: "Inspection area 2",
new InspectionAreaPolygon
{
ZMin = 0,
ZMax = 10,
Positions =
[
new XYPosition(x: 0, y: 0),
new XYPosition(x: 0, y: -10),
new XYPosition(x: -10, y: -10),
new XYPosition(x: -10, y: 0),
],
}
);

var taskOutsideInspectionArea = new MissionTask(
new Pose(-5, -5, 0, 0, 0, 0, 0),
MissionTaskType.Inspection
);
var returnHomeTask = new MissionTask(
new Pose(0, 0, 0, 0, 0, 0, 0),
MissionTaskType.Inspection
);

var robot = await DatabaseUtilities.NewRobot(
RobotStatus.Available,
installation,
inspectionArea2
);
await DatabaseUtilities.NewMissionRun(
installation.InstallationCode,
robot,
inspectionArea1,
writeToDatabase: true,
missionRunType: MissionRunType.ReturnHome,
tasks: [returnHomeTask]
);
await DatabaseUtilities.NewMissionRun(
installation.InstallationCode,
robot,
inspectionArea1,
writeToDatabase: true,
tasks: [taskOutsideInspectionArea]
);

// Act
await MissionSchedulingService.StartNextMissionRunIfSystemIsAvailable(robot);

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

// TODO: the mission still exists, it is however aborted. Need to check why no return home mission scheduled

Assert.Equal(2, reportsAfter.Count);
Assert.False(reportsAfter[0].IsReturnHomeMission());
Assert.Equal(MissionStatus.Aborted, reportsAfter[0].Status);
Assert.True(reportsAfter[1].IsReturnHomeMission());
Assert.Equal(MissionStatus.Ongoing, reportsAfter[1].Status);
}

[Fact]
public async Task CheckThatReturnHomeIsNotScheduledWhenFailingToStartFirstMission()
{
// Arrange
var installation = await DatabaseUtilities.NewInstallation();
var plant = await DatabaseUtilities.NewPlant(installation.InstallationCode);
var inspectionArea1 = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode,
inspectionAreaName: "Inspection area 1",
new InspectionAreaPolygon
{
ZMin = 0,
ZMax = 10,
Positions =
[
new XYPosition(x: 0, y: 0),
new XYPosition(x: 0, y: 10),
new XYPosition(x: 10, y: 10),
new XYPosition(x: 10, y: 0),
],
}
);
var inspectionArea2 = await DatabaseUtilities.NewInspectionArea(
installation.InstallationCode,
plant.PlantCode,
inspectionAreaName: "Inspection area 2",
new InspectionAreaPolygon
{
ZMin = 0,
ZMax = 10,
Positions =
[
new XYPosition(x: 0, y: 0),
new XYPosition(x: 0, y: -10),
new XYPosition(x: -10, y: -10),
new XYPosition(x: -10, y: 0),
],
}
);

var taskOutsideInspectionArea = new MissionTask(
new Pose(-5, -5, 0, 0, 0, 0, 0),
MissionTaskType.Inspection
);

var robot = await DatabaseUtilities.NewRobot(
RobotStatus.Available,
installation,
inspectionArea2
);
await DatabaseUtilities.NewMissionRun(
installation.InstallationCode,
robot,
inspectionArea1,
writeToDatabase: true,
tasks: [taskOutsideInspectionArea]
);

// Act
await MissionSchedulingService.StartNextMissionRunIfSystemIsAvailable(robot);

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

Assert.Single(reportsAfter);
Assert.False(reportsAfter[0].IsReturnHomeMission());
Assert.Equal(MissionStatus.Aborted, reportsAfter[0].Status);
}
}
}
28 changes: 22 additions & 6 deletions backend/api/Services/MissionSchedulingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,28 +123,30 @@ public async Task StartNextMissionRunIfSystemIsAvailable(Robot robot)
)
{
logger.LogError(
"Robot {RobotNAme} with Id {RobotId} is not on the same inspection area as the mission run with Id {MissionRunId}. Aborting all mission runs",
"Robot {RobotNAme} with Id {RobotId} is not on the same inspection area as the mission run with Id {MissionRunId}. Aborting this mission run",
robot.Name,
robot.Id,
missionRun.Id
);
try
{
await AbortAllScheduledNormalMissions(
robot.Id,
$"All missions aborted: Robot {robot.Name} is on inspection area {robot.CurrentInspectionArea?.Name} "
+ $"and mission run was on inspection area {missionRun.InspectionArea?.Name}"
await AbortMissionRun(
missionRun,
$"Mission run {missionRun.Id} aborted: Robot {robot.Name} is on inspection area {robot.CurrentInspectionArea?.Name} "
+ $"and mission run is on inspection area {missionRun.InspectionArea?.Name}"
);
}
catch (RobotNotFoundException)
{
logger.LogError(
"Failed to abort all scheduled missions for robot {RobotName} with Id {RobotId}",
"Failed to abort scheduled mission {missionRun.Id} for robot {RobotName} with Id {RobotId}",
missionRun.Id,
robot.Name,
robot.Id
);
}

await StartNextMissionRunIfSystemIsAvailable(robot);
return;
}

Expand Down Expand Up @@ -341,6 +343,20 @@ public async Task StopCurrentMissionRun(string robotId)
catch (RobotNotFoundException) { }
}

public async Task AbortMissionRun(MissionRun missionRun, string abortReason)
{
await missionRunService.UpdateMissionRunProperty(
missionRun.Id,
"Status",
MissionStatus.Aborted
);
await missionRunService.UpdateMissionRunProperty(
missionRun.Id,
"StatusReason",
abortReason
);
}

public async Task AbortAllScheduledNormalMissions(string robotId, string? abortReason)
{
var robot = await robotService.ReadById(robotId, readOnly: true);
Expand Down
8 changes: 8 additions & 0 deletions backend/api/Services/Models/InspectionAreaPolygon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ public class InspectionAreaPolygon

public class XYPosition
{
public XYPosition() { }

public XYPosition(float x = 0, float y = 0)
{
X = x;
Y = y;
}

[JsonPropertyName("x")]
public double X { get; set; }

Expand Down