isar 1.26.3__py3-none-any.whl → 1.27.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of isar might be problematic. Click here for more details.
- isar/apis/api.py +25 -0
- isar/apis/models/start_mission_definition.py +1 -7
- isar/apis/schedule/scheduling_controller.py +17 -1
- isar/config/settings.py +4 -1
- isar/models/communication/queues/events.py +1 -0
- isar/models/communication/queues/queue_utils.py +6 -0
- isar/modules.py +13 -6
- isar/robot/robot.py +10 -1
- isar/robot/robot_status.py +11 -5
- isar/services/utilities/scheduling_utilities.py +60 -7
- isar/state_machine/state_machine.py +51 -121
- isar/state_machine/states/await_next_mission.py +92 -0
- isar/state_machine/states/blocked_protective_stop.py +2 -5
- isar/state_machine/states/home.py +87 -0
- isar/state_machine/states/monitor.py +8 -3
- isar/state_machine/states/offline.py +3 -5
- isar/state_machine/states/returning_home.py +186 -0
- isar/state_machine/states/{idle.py → robot_standing_still.py} +20 -8
- isar/state_machine/states/{stop.py → stopping.py} +16 -4
- isar/state_machine/states/unknown_status.py +74 -0
- isar/state_machine/states_enum.py +6 -3
- isar/state_machine/transitions/functions/return_home.py +38 -0
- isar/state_machine/transitions/functions/robot_status.py +27 -0
- isar/state_machine/transitions/{start_mission.py → functions/start_mission.py} +1 -1
- isar/state_machine/transitions/mission.py +119 -0
- isar/state_machine/transitions/return_home.py +69 -0
- isar/state_machine/transitions/robot_status.py +73 -0
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/METADATA +14 -7
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/RECORD +43 -35
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/WHEEL +1 -1
- robot_interface/models/exceptions/robot_exceptions.py +14 -0
- robot_interface/models/mission/mission.py +8 -1
- robot_interface/models/mission/status.py +2 -0
- robot_interface/models/mission/task.py +0 -1
- isar/state_machine/states/off.py +0 -18
- /isar/state_machine/transitions/{fail_mission.py → functions/fail_mission.py} +0 -0
- /isar/state_machine/transitions/{finish_mission.py → functions/finish_mission.py} +0 -0
- /isar/state_machine/transitions/{pause.py → functions/pause.py} +0 -0
- /isar/state_machine/transitions/{resume.py → functions/resume.py} +0 -0
- /isar/state_machine/transitions/{stop.py → functions/stop.py} +0 -0
- /isar/state_machine/transitions/{utils.py → functions/utils.py} +0 -0
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/entry_points.txt +0 -0
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/licenses/LICENSE +0 -0
- {isar-1.26.3.dist-info → isar-1.27.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from queue import Queue
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from transitions import State
|
|
7
|
+
|
|
8
|
+
from isar.models.communication.message import StartMissionMessage
|
|
9
|
+
from isar.models.communication.queues.queue_io import QueueIO
|
|
10
|
+
from isar.models.communication.queues.queue_utils import (
|
|
11
|
+
check_for_event,
|
|
12
|
+
check_shared_state,
|
|
13
|
+
)
|
|
14
|
+
from isar.models.communication.queues.status_queue import StatusQueue
|
|
15
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from isar.state_machine.state_machine import StateMachine
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Home(State):
|
|
22
|
+
def __init__(self, state_machine: "StateMachine") -> None:
|
|
23
|
+
super().__init__(name="home", on_enter=self.start, on_exit=self.stop)
|
|
24
|
+
self.state_machine: "StateMachine" = state_machine
|
|
25
|
+
self.logger = logging.getLogger("state_machine")
|
|
26
|
+
self.events = self.state_machine.events
|
|
27
|
+
self.shared_state = self.state_machine.shared_state
|
|
28
|
+
self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
|
|
29
|
+
|
|
30
|
+
def start(self) -> None:
|
|
31
|
+
self.state_machine.update_state()
|
|
32
|
+
self._run()
|
|
33
|
+
|
|
34
|
+
def stop(self) -> None:
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
def _check_and_handle_start_mission_event(
|
|
38
|
+
self, event: Queue[StartMissionMessage]
|
|
39
|
+
) -> bool:
|
|
40
|
+
start_mission: Optional[StartMissionMessage] = check_for_event(event)
|
|
41
|
+
if start_mission:
|
|
42
|
+
self.state_machine.start_mission(mission=start_mission.mission)
|
|
43
|
+
self.state_machine.request_mission_start() # type: ignore
|
|
44
|
+
return True
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def _check_and_handle_return_home_event(self, event: QueueIO) -> bool:
|
|
48
|
+
if check_for_event(event.input):
|
|
49
|
+
event.output.put(True)
|
|
50
|
+
self.state_machine.request_return_home() # type: ignore
|
|
51
|
+
return True
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
def _check_and_handle_robot_status_event(
|
|
55
|
+
self, event: StatusQueue[RobotStatus]
|
|
56
|
+
) -> bool:
|
|
57
|
+
robot_status: RobotStatus = check_shared_state(event)
|
|
58
|
+
if robot_status != RobotStatus.Home:
|
|
59
|
+
self.state_machine.robot_status_changed() # type: ignore
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def _run(self) -> None:
|
|
65
|
+
while True:
|
|
66
|
+
if self.signal_state_machine_to_stop.is_set():
|
|
67
|
+
self.logger.info(
|
|
68
|
+
"Stopping state machine from %s state", self.__class__.__name__
|
|
69
|
+
)
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
if self._check_and_handle_start_mission_event(
|
|
73
|
+
self.events.api_requests.start_mission.input
|
|
74
|
+
):
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
if self._check_and_handle_return_home_event(
|
|
78
|
+
self.events.api_requests.return_home
|
|
79
|
+
):
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
if self._check_and_handle_robot_status_event(
|
|
83
|
+
self.shared_state.robot_status
|
|
84
|
+
):
|
|
85
|
+
break
|
|
86
|
+
|
|
87
|
+
time.sleep(self.state_machine.sleep_time)
|
|
@@ -61,9 +61,8 @@ class Monitor(State):
|
|
|
61
61
|
return False
|
|
62
62
|
|
|
63
63
|
def _check_and_handle_mission_started_event(self, event: Queue) -> None:
|
|
64
|
-
if
|
|
65
|
-
|
|
66
|
-
self.state_machine.mission_ongoing = True
|
|
64
|
+
if check_for_event(event):
|
|
65
|
+
self.state_machine.mission_ongoing = True
|
|
67
66
|
|
|
68
67
|
def _check_and_handle_mission_failed_event(self, event: Queue) -> bool:
|
|
69
68
|
mission_failed: Optional[ErrorMessage] = check_for_event(event)
|
|
@@ -83,6 +82,9 @@ class Monitor(State):
|
|
|
83
82
|
return False
|
|
84
83
|
|
|
85
84
|
def _check_and_handle_task_status_failed_event(self, event: Queue) -> bool:
|
|
85
|
+
if not self.state_machine.mission_ongoing:
|
|
86
|
+
return False
|
|
87
|
+
|
|
86
88
|
task_failure: Optional[ErrorMessage] = check_for_event(event)
|
|
87
89
|
if task_failure is not None:
|
|
88
90
|
self.awaiting_task_status = False
|
|
@@ -101,6 +103,9 @@ class Monitor(State):
|
|
|
101
103
|
return False
|
|
102
104
|
|
|
103
105
|
def _check_and_handle_task_status_event(self, event: Queue) -> bool:
|
|
106
|
+
if not self.state_machine.mission_ongoing:
|
|
107
|
+
return False
|
|
108
|
+
|
|
104
109
|
status: Optional[TaskStatus] = check_for_event(event)
|
|
105
110
|
if status is not None:
|
|
106
111
|
self.awaiting_task_status = False
|
|
@@ -37,11 +37,9 @@ class Offline(State):
|
|
|
37
37
|
robot_status: RobotStatus = check_shared_state(
|
|
38
38
|
self.shared_state.robot_status
|
|
39
39
|
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
elif robot_status != RobotStatus.Offline:
|
|
44
|
-
transition = self.state_machine.robot_turned_online # type: ignore
|
|
40
|
+
|
|
41
|
+
if robot_status != RobotStatus.Offline:
|
|
42
|
+
transition = self.state_machine.robot_status_changed # type: ignore
|
|
45
43
|
break
|
|
46
44
|
|
|
47
45
|
time.sleep(self.state_machine.sleep_time)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from transitions import State
|
|
6
|
+
|
|
7
|
+
from isar.models.communication.message import StartMissionMessage
|
|
8
|
+
from isar.models.communication.queues.queue_utils import (
|
|
9
|
+
check_for_event,
|
|
10
|
+
check_for_event_without_consumption,
|
|
11
|
+
trigger_event,
|
|
12
|
+
)
|
|
13
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
14
|
+
from robot_interface.models.mission.status import TaskStatus
|
|
15
|
+
from robot_interface.models.mission.task import Task
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from isar.state_machine.state_machine import StateMachine
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ReturningHome(State):
|
|
22
|
+
def __init__(self, state_machine: "StateMachine") -> None:
|
|
23
|
+
super().__init__(name="returning_home", on_enter=self.start, on_exit=self.stop)
|
|
24
|
+
self.state_machine: "StateMachine" = state_machine
|
|
25
|
+
|
|
26
|
+
self.logger = logging.getLogger("state_machine")
|
|
27
|
+
self.events = self.state_machine.events
|
|
28
|
+
|
|
29
|
+
self.awaiting_task_status: bool = False
|
|
30
|
+
self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
|
|
31
|
+
|
|
32
|
+
def start(self) -> None:
|
|
33
|
+
self.state_machine.update_state()
|
|
34
|
+
self._run()
|
|
35
|
+
|
|
36
|
+
def stop(self) -> None:
|
|
37
|
+
self.state_machine.mission_ongoing = False
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
|
|
41
|
+
if check_for_event(event):
|
|
42
|
+
self.state_machine.stop() # type: ignore
|
|
43
|
+
return True
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def _check_and_handle_mission_started_event(self, event: Queue) -> bool:
|
|
47
|
+
if self.state_machine.mission_ongoing:
|
|
48
|
+
return False
|
|
49
|
+
|
|
50
|
+
if check_for_event(event):
|
|
51
|
+
self.state_machine.mission_ongoing = True
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
def _check_and_handle_mission_failed_event(self, event: Queue) -> bool:
|
|
57
|
+
mission_failed: Optional[ErrorMessage] = check_for_event(event)
|
|
58
|
+
if mission_failed is not None:
|
|
59
|
+
self.state_machine.logger.warning(
|
|
60
|
+
f"Failed to initiate mission "
|
|
61
|
+
f"{str(self.state_machine.current_mission.id)[:8]} because: "
|
|
62
|
+
f"{mission_failed.error_description}"
|
|
63
|
+
)
|
|
64
|
+
self.state_machine.current_mission.error_message = ErrorMessage(
|
|
65
|
+
error_reason=mission_failed.error_reason,
|
|
66
|
+
error_description=mission_failed.error_description,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
self.state_machine.mission_failed_to_start() # type: ignore
|
|
70
|
+
return True
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
def _check_and_handle_task_status_failed_event(self, event: Queue) -> bool:
|
|
74
|
+
task_failure: Optional[ErrorMessage] = check_for_event(event)
|
|
75
|
+
if task_failure is not None:
|
|
76
|
+
self.awaiting_task_status = False
|
|
77
|
+
self.state_machine.current_task.error_message = task_failure
|
|
78
|
+
self.logger.error(
|
|
79
|
+
f"Monitoring task {self.state_machine.current_task.id[:8]} failed "
|
|
80
|
+
f"because: {task_failure.error_description}"
|
|
81
|
+
)
|
|
82
|
+
return self._handle_new_task_status(TaskStatus.Failed)
|
|
83
|
+
elif not self.awaiting_task_status:
|
|
84
|
+
trigger_event(
|
|
85
|
+
self.events.state_machine_events.task_status_request,
|
|
86
|
+
self.state_machine.current_task.id,
|
|
87
|
+
)
|
|
88
|
+
self.awaiting_task_status = True
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
def _check_and_handle_task_status_event(self, event: Queue) -> bool:
|
|
92
|
+
status: Optional[TaskStatus] = check_for_event(event)
|
|
93
|
+
if status is not None:
|
|
94
|
+
self.awaiting_task_status = False
|
|
95
|
+
return self._handle_new_task_status(status)
|
|
96
|
+
elif not self.awaiting_task_status:
|
|
97
|
+
trigger_event(
|
|
98
|
+
self.events.state_machine_events.task_status_request,
|
|
99
|
+
self.state_machine.current_task.id,
|
|
100
|
+
)
|
|
101
|
+
self.awaiting_task_status = True
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
def _handle_new_task_status(self, status: TaskStatus) -> bool:
|
|
105
|
+
if self.state_machine.current_task is None:
|
|
106
|
+
self.state_machine.iterate_current_task()
|
|
107
|
+
|
|
108
|
+
self.state_machine.current_task.status = status
|
|
109
|
+
|
|
110
|
+
if self.state_machine.current_task.is_finished():
|
|
111
|
+
self._report_task_status(self.state_machine.current_task)
|
|
112
|
+
self.state_machine.publish_task_status(task=self.state_machine.current_task)
|
|
113
|
+
|
|
114
|
+
self.state_machine.iterate_current_task()
|
|
115
|
+
if self.state_machine.current_task is None:
|
|
116
|
+
if status != TaskStatus.Successful:
|
|
117
|
+
self.state_machine.return_home_failed() # type: ignore
|
|
118
|
+
self.state_machine.returned_home() # type: ignore
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
# Report and update next task
|
|
122
|
+
self.state_machine.current_task.update_task_status()
|
|
123
|
+
self.state_machine.publish_task_status(task=self.state_machine.current_task)
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
def _check_and_handle_start_mission_event(
|
|
127
|
+
self, event: Queue[StartMissionMessage]
|
|
128
|
+
) -> bool:
|
|
129
|
+
if check_for_event_without_consumption(event):
|
|
130
|
+
self.state_machine.stop() # type: ignore
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def _run(self) -> None:
|
|
136
|
+
while True:
|
|
137
|
+
if self.signal_state_machine_to_stop.is_set():
|
|
138
|
+
self.logger.info(
|
|
139
|
+
"Stopping state machine from %s state", self.__class__.__name__
|
|
140
|
+
)
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
if self._check_and_handle_stop_mission_event(
|
|
144
|
+
self.events.api_requests.stop_mission.input
|
|
145
|
+
):
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
if self._check_and_handle_mission_started_event(
|
|
149
|
+
self.events.robot_service_events.mission_started
|
|
150
|
+
):
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
if self._check_and_handle_task_status_event(
|
|
154
|
+
self.events.robot_service_events.task_status_updated
|
|
155
|
+
):
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
if self._check_and_handle_start_mission_event(
|
|
159
|
+
self.events.api_requests.start_mission.input
|
|
160
|
+
):
|
|
161
|
+
break
|
|
162
|
+
|
|
163
|
+
if self._check_and_handle_mission_failed_event(
|
|
164
|
+
self.events.robot_service_events.mission_failed
|
|
165
|
+
):
|
|
166
|
+
break
|
|
167
|
+
|
|
168
|
+
if self._check_and_handle_task_status_failed_event(
|
|
169
|
+
self.events.robot_service_events.task_status_failed
|
|
170
|
+
):
|
|
171
|
+
break
|
|
172
|
+
|
|
173
|
+
if self._check_and_handle_task_status_event(
|
|
174
|
+
self.events.robot_service_events.task_status_updated
|
|
175
|
+
):
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
def _report_task_status(self, task: Task) -> None:
|
|
179
|
+
if task.status == TaskStatus.Failed:
|
|
180
|
+
self.logger.warning(
|
|
181
|
+
f"Task: {str(task.id)[:8]} was reported as failed by the robot"
|
|
182
|
+
)
|
|
183
|
+
elif task.status == TaskStatus.Successful:
|
|
184
|
+
self.logger.info(
|
|
185
|
+
f"{type(task).__name__} task: {str(task.id)[:8]} completed"
|
|
186
|
+
)
|
|
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
6
6
|
from transitions import State
|
|
7
7
|
|
|
8
8
|
from isar.models.communication.message import StartMissionMessage
|
|
9
|
+
from isar.models.communication.queues.queue_io import QueueIO
|
|
9
10
|
from isar.models.communication.queues.queue_utils import (
|
|
10
11
|
check_for_event,
|
|
11
12
|
check_shared_state,
|
|
@@ -17,12 +18,13 @@ if TYPE_CHECKING:
|
|
|
17
18
|
from isar.state_machine.state_machine import StateMachine
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class
|
|
21
|
+
class RobotStandingStill(State):
|
|
21
22
|
def __init__(self, state_machine: "StateMachine") -> None:
|
|
22
|
-
super().__init__(
|
|
23
|
+
super().__init__(
|
|
24
|
+
name="robot_standing_still", on_enter=self.start, on_exit=self.stop
|
|
25
|
+
)
|
|
23
26
|
self.state_machine: "StateMachine" = state_machine
|
|
24
27
|
self.logger = logging.getLogger("state_machine")
|
|
25
|
-
self.last_robot_status_poll_time: float = time.time()
|
|
26
28
|
self.events = self.state_machine.events
|
|
27
29
|
self.shared_state = self.state_machine.shared_state
|
|
28
30
|
self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
|
|
@@ -50,16 +52,21 @@ class Idle(State):
|
|
|
50
52
|
return True
|
|
51
53
|
return False
|
|
52
54
|
|
|
55
|
+
def _check_and_handle_return_home_event(self, event: QueueIO) -> bool:
|
|
56
|
+
if check_for_event(event.input):
|
|
57
|
+
event.output.put(True)
|
|
58
|
+
self.state_machine.request_return_home() # type: ignore
|
|
59
|
+
return True
|
|
60
|
+
return False
|
|
61
|
+
|
|
53
62
|
def _check_and_handle_robot_status_event(
|
|
54
63
|
self, event: StatusQueue[RobotStatus]
|
|
55
64
|
) -> bool:
|
|
56
65
|
robot_status: RobotStatus = check_shared_state(event)
|
|
57
|
-
if robot_status
|
|
58
|
-
self.state_machine.
|
|
59
|
-
return True
|
|
60
|
-
elif robot_status == RobotStatus.BlockedProtectiveStop:
|
|
61
|
-
self.state_machine.robot_protective_stop_engaged() # type: ignore
|
|
66
|
+
if robot_status != RobotStatus.Available:
|
|
67
|
+
self.state_machine.robot_status_changed() # type: ignore
|
|
62
68
|
return True
|
|
69
|
+
|
|
63
70
|
return False
|
|
64
71
|
|
|
65
72
|
def _run(self) -> None:
|
|
@@ -80,6 +87,11 @@ class Idle(State):
|
|
|
80
87
|
):
|
|
81
88
|
break
|
|
82
89
|
|
|
90
|
+
if self._check_and_handle_return_home_event(
|
|
91
|
+
self.events.api_requests.return_home
|
|
92
|
+
):
|
|
93
|
+
break
|
|
94
|
+
|
|
83
95
|
if self._check_and_handle_robot_status_event(
|
|
84
96
|
self.shared_state.robot_status
|
|
85
97
|
):
|
|
@@ -13,18 +13,23 @@ if TYPE_CHECKING:
|
|
|
13
13
|
from isar.state_machine.state_machine import StateMachine
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class
|
|
16
|
+
class Stopping(State):
|
|
17
17
|
def __init__(self, state_machine: "StateMachine") -> None:
|
|
18
|
-
super().__init__(name="
|
|
18
|
+
super().__init__(name="stopping", on_enter=self.start, on_exit=self.stop)
|
|
19
19
|
self.state_machine: "StateMachine" = state_machine
|
|
20
20
|
self.logger = logging.getLogger("state_machine")
|
|
21
21
|
self.events = self.state_machine.events
|
|
22
22
|
self.stop_thread: Optional[ThreadedRequest] = None
|
|
23
23
|
self._count_number_retries: int = 0
|
|
24
24
|
self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
|
|
25
|
+
self.stopping_return_home_mission: bool = False
|
|
25
26
|
|
|
26
27
|
def start(self) -> None:
|
|
27
28
|
self.state_machine.update_state()
|
|
29
|
+
if self.state_machine.current_mission is not None:
|
|
30
|
+
self.stopping_return_home_mission = (
|
|
31
|
+
self.state_machine.current_mission._is_return_to_home_mission()
|
|
32
|
+
)
|
|
28
33
|
self._run()
|
|
29
34
|
|
|
30
35
|
def stop(self) -> None:
|
|
@@ -32,18 +37,25 @@ class Stop(State):
|
|
|
32
37
|
self.stop_thread.wait_for_thread()
|
|
33
38
|
self.stop_thread = None
|
|
34
39
|
self._count_number_retries = 0
|
|
40
|
+
self.stopping_return_home_mission = False
|
|
35
41
|
|
|
36
42
|
def _check_and_handle_failed_stop(self, event: Queue[ErrorMessage]) -> bool:
|
|
37
43
|
error_message: Optional[ErrorMessage] = check_for_event(event)
|
|
38
44
|
if error_message is not None:
|
|
39
45
|
self.logger.warning(error_message.error_description)
|
|
40
|
-
self.
|
|
46
|
+
if self.stopping_return_home_mission:
|
|
47
|
+
self.state_machine.return_home_mission_stopped() # type: ignore
|
|
48
|
+
else:
|
|
49
|
+
self.state_machine.mission_stopped() # type: ignore
|
|
41
50
|
return True
|
|
42
51
|
return False
|
|
43
52
|
|
|
44
53
|
def _check_and_handle_successful_stop(self, event: Queue[bool]) -> bool:
|
|
45
54
|
if check_for_event(event):
|
|
46
|
-
self.
|
|
55
|
+
if self.stopping_return_home_mission:
|
|
56
|
+
self.state_machine.return_home_mission_stopped() # type: ignore
|
|
57
|
+
else:
|
|
58
|
+
self.state_machine.mission_stopped() # type: ignore
|
|
47
59
|
return True
|
|
48
60
|
return False
|
|
49
61
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from queue import Queue
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from transitions import State
|
|
7
|
+
|
|
8
|
+
from isar.models.communication.queues.queue_utils import (
|
|
9
|
+
check_for_event,
|
|
10
|
+
check_shared_state,
|
|
11
|
+
)
|
|
12
|
+
from isar.models.communication.queues.status_queue import StatusQueue
|
|
13
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from isar.state_machine.state_machine import StateMachine
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UnknownStatus(State):
|
|
20
|
+
def __init__(self, state_machine: "StateMachine") -> None:
|
|
21
|
+
super().__init__(name="unknown_status", on_enter=self.start, on_exit=self.stop)
|
|
22
|
+
self.state_machine: "StateMachine" = state_machine
|
|
23
|
+
self.logger = logging.getLogger("state_machine")
|
|
24
|
+
self.events = self.state_machine.events
|
|
25
|
+
self.shared_state = self.state_machine.shared_state
|
|
26
|
+
self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
|
|
27
|
+
|
|
28
|
+
def start(self) -> None:
|
|
29
|
+
self.state_machine.update_state()
|
|
30
|
+
self._run()
|
|
31
|
+
|
|
32
|
+
def stop(self) -> None:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
def _check_and_handle_stop_mission_event(self, event: Queue) -> bool:
|
|
36
|
+
if check_for_event(event):
|
|
37
|
+
self.state_machine.stop() # type: ignore
|
|
38
|
+
return True
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
def _check_and_handle_robot_status_event(
|
|
42
|
+
self, event: StatusQueue[RobotStatus]
|
|
43
|
+
) -> bool:
|
|
44
|
+
robot_status: RobotStatus = check_shared_state(event)
|
|
45
|
+
if (
|
|
46
|
+
robot_status == RobotStatus.Home
|
|
47
|
+
or robot_status == RobotStatus.Offline
|
|
48
|
+
or robot_status == RobotStatus.BlockedProtectiveStop
|
|
49
|
+
or robot_status == RobotStatus.Available
|
|
50
|
+
):
|
|
51
|
+
self.state_machine.robot_status_changed() # type: ignore
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
def _run(self) -> None:
|
|
57
|
+
while True:
|
|
58
|
+
if self.signal_state_machine_to_stop.is_set():
|
|
59
|
+
self.logger.info(
|
|
60
|
+
"Stopping state machine from %s state", self.__class__.__name__
|
|
61
|
+
)
|
|
62
|
+
break
|
|
63
|
+
|
|
64
|
+
if self._check_and_handle_stop_mission_event(
|
|
65
|
+
self.events.api_requests.stop_mission.input
|
|
66
|
+
):
|
|
67
|
+
break
|
|
68
|
+
|
|
69
|
+
if self._check_and_handle_robot_status_event(
|
|
70
|
+
self.shared_state.robot_status
|
|
71
|
+
):
|
|
72
|
+
break
|
|
73
|
+
|
|
74
|
+
time.sleep(self.state_machine.sleep_time)
|
|
@@ -2,13 +2,16 @@ from enum import Enum
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class States(str, Enum):
|
|
5
|
-
Off = "off"
|
|
6
|
-
Idle = "idle"
|
|
7
5
|
Monitor = "monitor"
|
|
6
|
+
ReturningHome = "returning_home"
|
|
7
|
+
Stopping = "stopping"
|
|
8
8
|
Paused = "paused"
|
|
9
|
-
|
|
9
|
+
AwaitNextMission = "await_next_mission"
|
|
10
|
+
Home = "home"
|
|
11
|
+
RobotStandingStill = "robot_standing_still"
|
|
10
12
|
Offline = "offline"
|
|
11
13
|
BlockedProtectiveStop = "blocked_protective_stop"
|
|
14
|
+
UnknownStatus = "unknown_status"
|
|
12
15
|
|
|
13
16
|
def __repr__(self):
|
|
14
17
|
return self.value
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from isar.state_machine.state_machine import StateMachine
|
|
5
|
+
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from robot_interface.models.mission.mission import Mission
|
|
9
|
+
from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
10
|
+
from robot_interface.models.mission.task import ReturnToHome
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from isar.state_machine.state_machine import StateMachine
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def start_return_home_mission(state_machine: "StateMachine") -> bool:
|
|
17
|
+
state_machine.start_mission(
|
|
18
|
+
Mission(
|
|
19
|
+
tasks=[ReturnToHome()],
|
|
20
|
+
name="Return Home",
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_return_home_status(state_machine: "StateMachine") -> bool:
|
|
27
|
+
state_machine.log_mission_overview(mission=state_machine.current_mission)
|
|
28
|
+
state_machine.current_mission.status = MissionStatus.InProgress
|
|
29
|
+
|
|
30
|
+
state_machine.current_task = state_machine.task_selector.next_task()
|
|
31
|
+
state_machine.current_task.status = TaskStatus.InProgress
|
|
32
|
+
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def return_home_finished(state_machine: "StateMachine") -> bool:
|
|
37
|
+
state_machine.reset_state_machine()
|
|
38
|
+
return True
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from isar.models.communication.queues.queue_utils import check_shared_state
|
|
4
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from isar.state_machine.state_machine import StateMachine
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_offline(state_machine: "StateMachine") -> bool:
|
|
11
|
+
robot_status = check_shared_state(state_machine.shared_state.robot_status)
|
|
12
|
+
return robot_status == RobotStatus.Offline
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_available(state_machine: "StateMachine") -> bool:
|
|
16
|
+
robot_status = check_shared_state(state_machine.shared_state.robot_status)
|
|
17
|
+
return robot_status == RobotStatus.Available
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_home(state_machine: "StateMachine") -> bool:
|
|
21
|
+
robot_status = check_shared_state(state_machine.shared_state.robot_status)
|
|
22
|
+
return robot_status == RobotStatus.Home
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_blocked_protective_stop(state_machine: "StateMachine") -> bool:
|
|
26
|
+
robot_status = check_shared_state(state_machine.shared_state.robot_status)
|
|
27
|
+
return robot_status == RobotStatus.BlockedProtectiveStop
|
|
@@ -61,7 +61,7 @@ def set_mission_to_in_progress(state_machine: "StateMachine") -> bool:
|
|
|
61
61
|
return True
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def
|
|
64
|
+
def trigger_start_mission_event(state_machine: "StateMachine") -> bool:
|
|
65
65
|
state_machine.events.state_machine_events.start_mission.put(
|
|
66
66
|
state_machine.current_mission
|
|
67
67
|
)
|