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

Improve mission starting stability #1889

Merged
merged 4 commits into from
Dec 17, 2024
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
2 changes: 1 addition & 1 deletion backend/api/Controllers/Models/UpdateRobotQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{
public class UpdateRobotQuery
{
public string? AreaId { get; set; }
public string? InspectionAreaId { get; set; }

public Pose? Pose { get; set; }

Expand Down
42 changes: 24 additions & 18 deletions backend/api/Controllers/RobotController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class RobotController(
IIsarService isarService,
IMissionSchedulingService missionSchedulingService,
IRobotModelService robotModelService,
IAreaService areaService,
IDeckService deckService,
IErrorHandlingService errorHandlingService
) : ControllerBase
{
Expand Down Expand Up @@ -163,8 +163,8 @@ [FromBody] Robot robot

try
{
var updatedRobot = await robotService.Update(robot);
var robotResponse = new RobotResponse(updatedRobot);
await robotService.Update(robot);
var robotResponse = new RobotResponse(robot);
logger.LogInformation("Successful PUT of robot to database");

return Ok(robotResponse);
Expand Down Expand Up @@ -215,31 +215,36 @@ [FromBody] UpdateRobotQuery query
return NotFound(errorMessage);
}

Robot updatedRobot;
switch (fieldName)
{
case "currentInspectionAreaId":
if (query.AreaId == null)
updatedRobot = await robotService.UpdateCurrentInspectionArea(id, null);
if (query.InspectionAreaId == null)
{
await robotService.UpdateCurrentInspectionArea(id, null);
robot.CurrentInspectionArea = null;
}
else
{
var area = await areaService.ReadById(query.AreaId, readOnly: true);
if (area == null) return NotFound($"No area with ID {query.AreaId} was found");
updatedRobot = await robotService.UpdateCurrentInspectionArea(id, area.Id);
var inspectionArea = await deckService.ReadById(query.InspectionAreaId, readOnly: true);
if (inspectionArea == null) return NotFound($"No inspection area with ID {query.InspectionAreaId} was found");
await robotService.UpdateCurrentInspectionArea(id, inspectionArea.Id);
robot.CurrentInspectionArea = inspectionArea;
}
break;
case "pose":
if (query.Pose == null) return BadRequest("Cannot set robot pose to null");
updatedRobot = await robotService.UpdateRobotPose(id, query.Pose);
await robotService.UpdateRobotPose(id, query.Pose);
robot.Pose = query.Pose;
break;
case "missionId":
updatedRobot = await robotService.UpdateCurrentMissionId(id, query.MissionId);
await robotService.UpdateCurrentMissionId(id, query.MissionId);
robot.CurrentMissionId = query.MissionId;
break;
default:
return NotFound($"Could not find any field with name {fieldName}");
}

var robotResponse = new RobotResponse(updatedRobot);
var robotResponse = new RobotResponse(robot);
logger.LogInformation("Successful PUT of robot to database");

return Ok(robotResponse);
Expand Down Expand Up @@ -284,10 +289,10 @@ [FromRoute] bool deprecated
return NotFound(errorMessage);
}

Robot updatedRobot;
updatedRobot = await robotService.UpdateDeprecated(id, deprecated);
await robotService.UpdateDeprecated(id, deprecated);
robot.Deprecated = deprecated;

var robotResponse = new RobotResponse(updatedRobot);
var robotResponse = new RobotResponse(robot);
logger.LogInformation("Successful updated deprecated on robot to database");

return Ok(robotResponse);
Expand Down Expand Up @@ -357,10 +362,11 @@ [FromBody] RobotStatus robotStatus

try
{
var updatedRobot = await robotService.UpdateRobotStatus(id, robotStatus);
logger.LogInformation("Successfully updated robot {RobotId}", updatedRobot.Id);
await robotService.UpdateRobotStatus(id, robotStatus);
robot.Status = robotStatus;
logger.LogInformation("Successfully updated robot {RobotId}", robot.Id);

var robotResponse = new RobotResponse(updatedRobot);
var robotResponse = new RobotResponse(robot);

if (robotStatus == RobotStatus.Available) missionSchedulingService.TriggerRobotAvailable(new RobotAvailableEventArgs(robot.Id));

Expand Down
11 changes: 6 additions & 5 deletions backend/api/EventHandlers/MqttEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,16 @@ private async void OnIsarStatus(object? sender, MqttReceivedArgs mqttArgs)
_updateRobotSemaphore.WaitOne();
_logger.LogDebug("Semaphore acquired for updating robot status");

var updatedRobot = await RobotService.UpdateRobotStatus(robot.Id, isarStatus.Status);
await RobotService.UpdateRobotStatus(robot.Id, isarStatus.Status);
robot.Status = isarStatus.Status;
oysand marked this conversation as resolved.
Show resolved Hide resolved
oysand marked this conversation as resolved.
Show resolved Hide resolved

_updateRobotSemaphore.Release();
_logger.LogDebug("Semaphore released after updating robot status");

_logger.LogInformation("Updated status for robot {Name} to {Status}", updatedRobot.Name, updatedRobot.Status);
_logger.LogInformation("Updated status for robot {Name} to {Status}", robot.Name, robot.Status);


_logger.LogInformation("OnIsarStatus: Robot {robotName} has status {robotStatus} and current inspection area {areaName}", updatedRobot.Name, updatedRobot.Status, updatedRobot.CurrentInspectionArea?.Name);
_logger.LogInformation("OnIsarStatus: Robot {robotName} has status {robotStatus} and current inspection area {areaName}", robot.Name, robot.Status, robot.CurrentInspectionArea?.Name);

if (isarStatus.Status == RobotStatus.Available)
{
Expand Down Expand Up @@ -205,7 +206,7 @@ private async void OnIsarRobotInfo(object? sender, MqttReceivedArgs mqttArgs)
if (isarRobotInfo.Capabilities is not null) UpdateRobotCapabilitiesIfChanged(isarRobotInfo.Capabilities, ref robot, ref updatedFields);
if (updatedFields.Count < 1) return;

robot = await RobotService.Update(robot);
await RobotService.Update(robot);
_logger.LogInformation("Updated robot '{Id}' ('{RobotName}') in database: {Updates}", robot.Id, robot.Name, updatedFields);
}
finally
Expand Down Expand Up @@ -353,7 +354,7 @@ private async void OnIsarBatteryUpdate(object? sender, MqttReceivedArgs mqttArgs
var robot = await BatteryTimeseriesService.AddBatteryEntry(batteryStatus.BatteryLevel, batteryStatus.IsarId);
if (robot != null && robot.BatteryState != batteryStatus.BatteryState)
{
robot = await RobotService.UpdateRobotBatteryState(robot.Id, batteryStatus.BatteryState);
await RobotService.UpdateRobotBatteryState(robot.Id, batteryStatus.BatteryState);
}

_updateRobotSemaphore.Release();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class BatteryTimeseriesService(ILogger<BatteryTimeseriesService> logger,
{
if (Math.Abs(batteryLevel - robot.BatteryLevel) > Tolerance)
{
robot = await robotService.UpdateRobotBatteryLevel(robot.Id, batteryLevel);
await robotService.UpdateRobotBatteryLevel(robot.Id, batteryLevel);
robot.BatteryLevel = batteryLevel;
}
}
catch (Exception e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public class PressureTimeseriesService(ILogger<PressureTimeseriesService> logger
{
if (robot.PressureLevel is null || Math.Abs(pressureLevel - (float)robot.PressureLevel) > Tolerance)
{
robot = await robotService.UpdateRobotPressureLevel(robot.Id, pressureLevel);
await robotService.UpdateRobotPressureLevel(robot.Id, pressureLevel);
robot.PressureLevel = pressureLevel;
}
}
catch (Exception e)
Expand Down
3 changes: 3 additions & 0 deletions backend/api/Services/IsarService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public async Task<IsarMission> StartMission(Robot robot, MissionRun missionRun)

if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == HttpStatusCode.Conflict)
throw new RobotBusyException("Robot was not available when starting mission");

(string message, int statusCode) = GetErrorDescriptionFoFailedIsarRequest(response);
string errorResponse = await response.Content.ReadAsStringAsync();
logger.LogError("{Message}: {ErrorResponse}", message, errorResponse);
Expand Down
74 changes: 11 additions & 63 deletions backend/api/Services/MissionSchedulingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ public async Task StartNextMissionRunIfSystemIsAvailable(Robot robot)
logger.LogInformation("Robot {robotName} has status {robotStatus} and current area {areaName}", robot.Name, robot.Status, robot.CurrentInspectionArea?.Name);

MissionRun? missionRun;
try { missionRun = await SelectNextMissionRun(robot.Id); }
try { missionRun = await SelectNextMissionRun(robot); }
catch (RobotNotFoundException)
{
logger.LogError("Robot with ID: {RobotId} was not found in the database", robot.Id);
return;
}

if (robot.MissionQueueFrozen && missionRun != null && !missionRun.IsEmergencyMission())
if (robot.MissionQueueFrozen && missionRun != null && !(missionRun.IsEmergencyMission() || missionRun.IsReturnHomeMission()))
{
logger.LogInformation("Robot {robotName} was ready to start a mission but its mission queue was frozen", robot.Name);
return;
Expand All @@ -68,7 +68,7 @@ public async Task StartNextMissionRunIfSystemIsAvailable(Robot robot)

if (missionRun == null) { return; }

if (!await TheSystemIsAvailableToRunAMission(robot.Id, missionRun.Id))
if (!TheSystemIsAvailableToRunAMission(robot, missionRun))
oysand marked this conversation as resolved.
Show resolved Hide resolved
{
logger.LogInformation("Mission {MissionRunId} was put on the queue as the system may not start a mission now", missionRun.Id);
return;
Expand Down Expand Up @@ -98,7 +98,7 @@ public async Task StartNextMissionRunIfSystemIsAvailable(Robot robot)
if (missionRun == null) { return; }
}

try { await StartMissionRun(missionRun); }
try { await StartMissionRun(missionRun, robot); }
catch (Exception ex) when (
ex is MissionException
or RobotNotFoundException
Expand All @@ -114,6 +114,9 @@ or MissionRunNotFoundException
);
await missionRunService.SetMissionRunToFailed(missionRun.Id, $"Mission run '{missionRun.Id}' was not started successfully. '{ex.Message}'");
}
catch (RobotBusyException)
andchiind marked this conversation as resolved.
Show resolved Hide resolved
{
}
}

public async Task<MissionRun?> HandleBatteryAndPressureLevel(Robot robot)
Expand Down Expand Up @@ -308,16 +311,8 @@ public void TriggerRobotAvailable(RobotAvailableEventArgs e)
OnRobotAvailable(e);
}

private async Task<MissionRun?> SelectNextMissionRun(string robotId)
private async Task<MissionRun?> SelectNextMissionRun(Robot robot)
{
var robot = await robotService.ReadById(robotId, readOnly: true);
if (robot == null)
{
string errorMessage = $"Could not find robot with id {robotId}";
logger.LogError("{Message}", errorMessage);
throw new RobotNotFoundException(errorMessage);
}

var missionRun = await missionRunService.ReadNextScheduledEmergencyMissionRun(robot.Id, readOnly: true);
if (robot.MissionQueueFrozen == false && missionRun == null) { missionRun = await missionRunService.ReadNextScheduledMissionRun(robot.Id, readOnly: true); }
return missionRun;
Expand Down Expand Up @@ -371,33 +366,10 @@ private async Task MoveInterruptedMissionsToQueue(IEnumerable<string> interrupte
}
}

private async Task StartMissionRun(MissionRun queuedMissionRun)
private async Task StartMissionRun(MissionRun queuedMissionRun, Robot robot)
{
string robotId = queuedMissionRun.Robot.Id;
string missionRunId = queuedMissionRun.Id;

var robot = await robotService.ReadById(robotId, readOnly: true);
if (robot == null)
{
string errorMessage = $"Could not find robot with id {robotId}";
logger.LogError("{Message}", errorMessage);
throw new RobotNotFoundException(errorMessage);
}

if (robot.Status is not RobotStatus.Available)
{
string errorMessage = $"Robot {robotId} has status {robot.Status} and is not available";
logger.LogError("{Message}", errorMessage);
throw new RobotNotAvailableException(errorMessage);
}

if (robot.Deprecated)
{
string errorMessage = $"Robot {robotId} is deprecated and cannot start mission";
logger.LogError("{Message}", errorMessage);
throw new RobotNotAvailableException(errorMessage);
}

var missionRun = await missionRunService.ReadById(missionRunId, readOnly: true);
if (missionRun == null)
{
Expand Down Expand Up @@ -452,33 +424,9 @@ private async Task StartMissionRun(MissionRun queuedMissionRun)
return ongoingMissions;
}

private async Task<bool> TheSystemIsAvailableToRunAMission(string robotId, string missionRunId)
private bool TheSystemIsAvailableToRunAMission(Robot robot, MissionRun missionRun)
{
bool ongoingMission = await OngoingMission(robotId);
oysand marked this conversation as resolved.
Show resolved Hide resolved
oysand marked this conversation as resolved.
Show resolved Hide resolved

if (ongoingMission)
{
logger.LogInformation("Mission run {MissionRunId} was not started as there is already an ongoing mission", missionRunId);
return false;
}

var robot = await robotService.ReadById(robotId, readOnly: true);
if (robot is null)
{
string errorMessage = $"Robot with ID: {robotId} was not found in the database";
logger.LogError("{Message}", errorMessage);
throw new RobotNotFoundException(errorMessage);
}

var missionRun = await missionRunService.ReadById(missionRunId, readOnly: true);
if (missionRun is null)
{
string errorMessage = $"Mission run with Id {missionRunId} was not found in the database";
logger.LogError("{Message}", errorMessage);
throw new MissionRunNotFoundException(errorMessage);
}

if (robot.MissionQueueFrozen && missionRun.MissionRunType != MissionRunType.Emergency)
if (robot.MissionQueueFrozen && !(missionRun.IsEmergencyMission() || missionRun.IsReturnHomeMission()))
oysand marked this conversation as resolved.
Show resolved Hide resolved
{
logger.LogInformation("Mission run {MissionRunId} was not started as the mission run queue for robot {RobotName} is frozen", missionRun.Id, robot.Name);
return false;
Expand Down
Loading
Loading