diff --git a/src/isar/models/mission/__init__.py b/src/isar/models/mission/__init__.py new file mode 100644 index 00000000..94f5c906 --- /dev/null +++ b/src/isar/models/mission/__init__.py @@ -0,0 +1,2 @@ +from .mission import Mission, Task +from .status import MissionStatus, TaskStatus diff --git a/src/isar/models/mission.py b/src/isar/models/mission/mission.py similarity index 86% rename from src/isar/models/mission.py rename to src/isar/models/mission/mission.py index d58ae31f..592678fc 100644 --- a/src/isar/models/mission.py +++ b/src/isar/models/mission/mission.py @@ -13,11 +13,13 @@ StepStatus, ) +from .status import MissionStatus, TaskStatus + @dataclass class Task: steps: List[STEPS] - status: StepStatus = field(default=StepStatus.NotStarted, init=False) + status: TaskStatus = field(default=TaskStatus.NotStarted, init=False) id: UUID = field(default_factory=uuid4, init=False) _iterator: Iterator = None @@ -29,7 +31,7 @@ def is_finished(self) -> bool: if step.status is StepStatus.Failed and isinstance(step, MotionStep): # One motion step has failed meaning the task as a whole should be # aborted - self.status = StepStatus.Failed + self.status = TaskStatus.Failed return True elif (step.status is StepStatus.Failed) and isinstance( @@ -38,10 +40,10 @@ def is_finished(self) -> bool: # It should be possible to perform several inspections per task. If # one out of many inspections fail the task is considered as # partially successful. - self.status = StepStatus.PartiallySuccessful + self.status = TaskStatus.PartiallySuccessful continue - elif step.status is StepStatus.Completed: + elif step.status is StepStatus.Successful: # The task is complete once all steps are completed continue else: @@ -50,13 +52,13 @@ def is_finished(self) -> bool: # Check if the task has been marked as partially successful by having one or # more inspection steps fail - if self.status is not StepStatus.PartiallySuccessful: + if self.status is not TaskStatus.PartiallySuccessful: # All steps have been completed - self.status = StepStatus.Completed + self.status = TaskStatus.Successful # Set the task to failed if all inspection steps failed elif self._all_inspection_steps_failed(): - self.status = StepStatus.Failed + self.status = TaskStatus.Failed return True @@ -78,7 +80,7 @@ def __post_init__(self) -> None: class Mission: tasks: List[Task] id: Union[UUID, int, str, None] = None - status: StepStatus = StepStatus.NotStarted + status: MissionStatus = MissionStatus.NotStarted metadata: MissionMetadata = None _iterator: Iterator = None diff --git a/src/isar/models/mission/status.py b/src/isar/models/mission/status.py new file mode 100644 index 00000000..a1a077e2 --- /dev/null +++ b/src/isar/models/mission/status.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class MissionStatus(str, Enum): + NotStarted: str = "not_started" + Started: str = "started" + InProgress: str = "in_progress" + Failed: str = "failed" + Cancelled: str = "cancelled" + Completed: str = "completed" + + +class TaskStatus(str, Enum): + NotStarted: str = "not_started" + InProgress: str = "in_progress" + PartiallySuccessful: str = "partially_successful" + Failed: str = "failed" + Cancelled: str = "cancelled" + Successful: str = "successful" diff --git a/src/isar/state_machine/state_machine.py b/src/isar/state_machine/state_machine.py index d2492565..90fdc632 100644 --- a/src/isar/state_machine/state_machine.py +++ b/src/isar/state_machine/state_machine.py @@ -18,6 +18,7 @@ ) from isar.models.communication.queues.queues import Queues from isar.models.mission import Mission, Task +from isar.models.mission.status import MissionStatus, TaskStatus from isar.services.service_connections.mqtt.mqtt_client import MqttClientInterface from isar.services.utilities.json_service import EnhancedJSONEncoder from isar.state_machine.states import Finalize, Idle, InitiateStep, Monitor, Off @@ -120,7 +121,7 @@ def update_current_task(self): self.publish_task_status() try: self.current_task = self.current_mission.next_task() - self.current_task.status = StepStatus.InProgress + self.current_task.status = TaskStatus.InProgress self.publish_task_status() except StopIteration: # Indicates that all tasks are finished @@ -216,10 +217,10 @@ def start_mission(self, mission: Mission): """Starts a scheduled mission.""" self.mission_in_progress = True self.current_mission = mission - self.current_mission.status = StepStatus.InProgress + self.current_mission.status = MissionStatus.InProgress self.publish_mission_status() self.current_task = mission.next_task() - self.current_task.status = StepStatus.InProgress + self.current_task.status = TaskStatus.InProgress self.publish_task_status() self.queues.start_mission.output.put(deepcopy(StartMissionMessages.success())) @@ -272,13 +273,14 @@ def stop_mission(self): self.queues.stop_mission.output.put(deepcopy(message)) self.logger.info(message) if not failure: + self.current_mission.status = MissionStatus.Cancelled self.mission_in_progress = False for task in self.current_mission.tasks: for step in task.steps: - if step.status == StepStatus.NotStarted: + if step.status in [StepStatus.NotStarted, StepStatus.InProgress]: step.status = StepStatus.Cancelled - if task.status == StepStatus.NotStarted: - task.status = StepStatus.Cancelled + if task.status in [TaskStatus.NotStarted, TaskStatus.InProgress]: + task.status = TaskStatus.Cancelled def publish_mission_status(self) -> None: payload: str = json.dumps( @@ -298,7 +300,7 @@ def publish_mission_status(self) -> None: ) def publish_task_status(self) -> None: - """Publishes the current step status to the MQTT Broker""" + """Publishes the current task status to the MQTT Broker""" payload: str = json.dumps( { "robot_id": settings.ROBOT_ID, diff --git a/src/isar/state_machine/states/finalize.py b/src/isar/state_machine/states/finalize.py index d60255d5..f6277bdd 100644 --- a/src/isar/state_machine/states/finalize.py +++ b/src/isar/state_machine/states/finalize.py @@ -19,7 +19,6 @@ def __init__(self, state_machine: "StateMachine"): def start(self): self.state_machine.update_state() - self.state_machine.current_mission.status = StepStatus.Completed self.state_machine.publish_mission_status() self.state_machine.log_step_overview(mission=self.state_machine.current_mission) next_state = self.state_machine.reset_state_machine() @@ -39,11 +38,3 @@ def _log_state_transitions(self): ) self.logger.info(f"State transitions:\n {state_transitions}") - - def _update_tasks(self): - for task in self.state_machine.current_mission.tasks: - for step in task.steps: - if step.status == StepStatus.NotStarted: - step.status = StepStatus.Cancelled - if task.status == StepStatus.NotStarted: - task.status = StepStatus.Cancelled diff --git a/src/isar/state_machine/states/initiate_step.py b/src/isar/state_machine/states/initiate_step.py index fd339c53..438ac55d 100644 --- a/src/isar/state_machine/states/initiate_step.py +++ b/src/isar/state_machine/states/initiate_step.py @@ -5,6 +5,7 @@ from transitions import State from isar.config.settings import settings +from isar.models.mission.status import MissionStatus from isar.services.utilities.threaded_request import ( ThreadedRequest, ThreadedRequestNotFinishedError, @@ -82,7 +83,6 @@ def _run(self): except ThreadedRequestNotFinishedError: time.sleep(self.state_machine.sleep_time) continue - except RobotInfeasibleStepException: self.state_machine.current_step.status = StepStatus.Failed self.logger.warning( @@ -114,6 +114,8 @@ def _run(self): self.initiate_step_failure_counter >= self.initiate_step_failure_counter_limit ): + self.state_machine.current_step.status = StepStatus.Failed + self.state_machine.current_mission.status = MissionStatus.Failed self.logger.error( f"Failed to initiate step after " f"{self.initiate_step_failure_counter_limit} attempts. " diff --git a/src/isar/state_machine/states/monitor.py b/src/isar/state_machine/states/monitor.py index 240e2c8e..18834fcb 100644 --- a/src/isar/state_machine/states/monitor.py +++ b/src/isar/state_machine/states/monitor.py @@ -116,7 +116,7 @@ def _step_finished(self, step: Step) -> bool: if step.status == StepStatus.Failed: self.logger.warning(f"Step: {str(step.id)[:8]} failed") finished = True - elif step.status == StepStatus.Completed: + elif step.status == StepStatus.Successful: self.logger.info( f"{type(step).__name__} step: {str(step.id)[:8]} completed" ) @@ -124,7 +124,7 @@ def _step_finished(self, step: Step) -> bool: return finished def _process_finished_step(self, step: Step) -> State: - if step.status == StepStatus.Completed and isinstance(step, InspectionStep): + if step.status == StepStatus.Successful and isinstance(step, InspectionStep): self._queue_inspections_for_upload(current_step=step) return States.InitiateStep diff --git a/src/robot_interface/models/mission/status.py b/src/robot_interface/models/mission/status.py index bbb9ad9d..ef43cf56 100644 --- a/src/robot_interface/models/mission/status.py +++ b/src/robot_interface/models/mission/status.py @@ -4,8 +4,7 @@ class StepStatus(str, Enum): NotStarted: str = "not_started" - Completed: str = "completed" - PartiallySuccessful: str = "partially_successful" + Successful: str = "successful" InProgress: str = "in_progress" Failed: str = "failed" Cancelled: str = "cancelled" diff --git a/tests/isar/state_machine/states/test_monitor.py b/tests/isar/state_machine/states/test_monitor.py index 1218bc11..2fea1334 100644 --- a/tests/isar/state_machine/states/test_monitor.py +++ b/tests/isar/state_machine/states/test_monitor.py @@ -9,8 +9,8 @@ @pytest.mark.parametrize( "mock_status, expected_output", [ - (StepStatus.Completed, True), - (StepStatus.Completed, True), + (StepStatus.Successful, True), + (StepStatus.Successful, True), (StepStatus.Failed, True), ], ) @@ -27,7 +27,7 @@ def test_step_finished(monitor: Monitor, mock_status, expected_output): @pytest.mark.parametrize( "mock_status, should_queue_upload", [ - (StepStatus.Completed, True), + (StepStatus.Successful, True), (StepStatus.Failed, False), ], ) diff --git a/tests/mocks/robot_interface.py b/tests/mocks/robot_interface.py index 47e5543d..7f647868 100644 --- a/tests/mocks/robot_interface.py +++ b/tests/mocks/robot_interface.py @@ -17,7 +17,7 @@ class MockRobot(RobotInterface): def __init__( self, initiate_step: bool = True, - step_status: StepStatus = StepStatus.Completed, + step_status: StepStatus = StepStatus.Successful, stop: bool = True, pose: Pose = Pose( position=Position(x=0, y=0, z=0, frame=Frame("robot")),