Skip to content

Commit

Permalink
Use seprate status types for step, task and mission
Browse files Browse the repository at this point in the history
  • Loading branch information
vetlek committed May 19, 2022
1 parent 2db3130 commit ce0f4e0
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 33 deletions.
2 changes: 2 additions & 0 deletions src/isar/models/mission/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .mission import Mission, Task
from .status import MissionStatus, TaskStatus
18 changes: 10 additions & 8 deletions src/isar/models/mission.py → src/isar/models/mission/mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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(
Expand All @@ -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:
Expand All @@ -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

Expand All @@ -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

Expand Down
19 changes: 19 additions & 0 deletions src/isar/models/mission/status.py
Original file line number Diff line number Diff line change
@@ -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"
16 changes: 9 additions & 7 deletions src/isar/state_machine/state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()))
Expand Down Expand Up @@ -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(
Expand All @@ -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,
Expand Down
9 changes: 0 additions & 9 deletions src/isar/state_machine/states/finalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
4 changes: 3 additions & 1 deletion src/isar/state_machine/states/initiate_step.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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. "
Expand Down
4 changes: 2 additions & 2 deletions src/isar/state_machine/states/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ 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"
)
finished = True
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
3 changes: 1 addition & 2 deletions src/robot_interface/models/mission/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
6 changes: 3 additions & 3 deletions tests/isar/state_machine/states/test_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
],
)
Expand All @@ -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),
],
)
Expand Down
2 changes: 1 addition & 1 deletion tests/mocks/robot_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")),
Expand Down

0 comments on commit ce0f4e0

Please sign in to comment.