isar 1.20.2__py3-none-any.whl → 1.34.9__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.
- isar/apis/api.py +148 -76
- isar/apis/models/__init__.py +0 -1
- isar/apis/models/models.py +21 -11
- isar/apis/models/start_mission_definition.py +110 -168
- isar/apis/robot_control/robot_controller.py +41 -0
- isar/apis/schedule/scheduling_controller.py +124 -162
- isar/apis/security/authentication.py +5 -5
- isar/config/certs/ca-cert.pem +33 -31
- isar/config/keyvault/keyvault_service.py +1 -1
- isar/config/log.py +45 -40
- isar/config/logging.conf +16 -31
- isar/config/open_telemetry.py +102 -0
- isar/config/predefined_mission_definition/default_exr.json +0 -2
- isar/config/predefined_mission_definition/default_mission.json +1 -5
- isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
- isar/config/predefined_missions/default.json +67 -87
- isar/config/predefined_missions/default_extra_capabilities.json +107 -0
- isar/config/settings.py +76 -111
- isar/eventhandlers/eventhandler.py +123 -0
- isar/mission_planner/local_planner.py +6 -20
- isar/mission_planner/mission_planner_interface.py +1 -1
- isar/models/events.py +184 -0
- isar/models/status.py +18 -0
- isar/modules.py +118 -199
- isar/robot/robot.py +377 -0
- isar/robot/robot_battery.py +60 -0
- isar/robot/robot_monitor_mission.py +357 -0
- isar/robot/robot_pause_mission.py +74 -0
- isar/robot/robot_resume_mission.py +67 -0
- isar/robot/robot_start_mission.py +66 -0
- isar/robot/robot_status.py +61 -0
- isar/robot/robot_stop_mission.py +68 -0
- isar/robot/robot_upload_inspection.py +75 -0
- isar/script.py +57 -40
- isar/services/service_connections/mqtt/mqtt_client.py +47 -11
- isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +5 -2
- isar/services/service_connections/mqtt/robot_info_publisher.py +3 -3
- isar/services/service_connections/persistent_memory.py +69 -0
- isar/services/utilities/mqtt_utilities.py +93 -0
- isar/services/utilities/robot_utilities.py +20 -0
- isar/services/utilities/scheduling_utilities.py +393 -65
- isar/state_machine/state_machine.py +219 -538
- isar/state_machine/states/__init__.py +0 -8
- isar/state_machine/states/await_next_mission.py +114 -0
- isar/state_machine/states/blocked_protective_stop.py +60 -0
- isar/state_machine/states/going_to_lockdown.py +95 -0
- isar/state_machine/states/going_to_recharging.py +92 -0
- isar/state_machine/states/home.py +115 -0
- isar/state_machine/states/intervention_needed.py +77 -0
- isar/state_machine/states/lockdown.py +38 -0
- isar/state_machine/states/maintenance.py +36 -0
- isar/state_machine/states/monitor.py +137 -247
- isar/state_machine/states/offline.py +51 -53
- isar/state_machine/states/paused.py +92 -23
- isar/state_machine/states/pausing.py +48 -0
- isar/state_machine/states/pausing_return_home.py +48 -0
- isar/state_machine/states/recharging.py +80 -0
- isar/state_machine/states/resuming.py +57 -0
- isar/state_machine/states/resuming_return_home.py +64 -0
- isar/state_machine/states/return_home_paused.py +109 -0
- isar/state_machine/states/returning_home.py +217 -0
- isar/state_machine/states/stopping.py +61 -0
- isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
- isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
- isar/state_machine/states/stopping_go_to_recharge.py +51 -0
- isar/state_machine/states/stopping_return_home.py +77 -0
- isar/state_machine/states/unknown_status.py +72 -0
- isar/state_machine/states_enum.py +21 -5
- isar/state_machine/transitions/mission.py +192 -0
- isar/state_machine/transitions/return_home.py +106 -0
- isar/state_machine/transitions/robot_status.py +80 -0
- isar/state_machine/utils/common_event_handlers.py +73 -0
- isar/storage/blob_storage.py +70 -52
- isar/storage/local_storage.py +25 -12
- isar/storage/storage_interface.py +28 -7
- isar/storage/uploader.py +174 -55
- isar/storage/utilities.py +32 -29
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/METADATA +73 -110
- isar-1.34.9.dist-info/RECORD +135 -0
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/entry_points.txt +1 -0
- robot_interface/models/exceptions/robot_exceptions.py +91 -41
- robot_interface/models/initialize/__init__.py +0 -1
- robot_interface/models/inspection/__init__.py +0 -13
- robot_interface/models/inspection/inspection.py +42 -33
- robot_interface/models/mission/mission.py +14 -15
- robot_interface/models/mission/status.py +20 -26
- robot_interface/models/mission/task.py +154 -121
- robot_interface/models/robots/battery_state.py +6 -0
- robot_interface/models/robots/media.py +13 -0
- robot_interface/models/robots/robot_model.py +7 -7
- robot_interface/robot_interface.py +119 -84
- robot_interface/telemetry/mqtt_client.py +74 -12
- robot_interface/telemetry/payloads.py +91 -13
- robot_interface/utilities/json_service.py +7 -1
- isar/config/predefined_missions/default_turtlebot.json +0 -110
- isar/config/predefined_poses/__init__.py +0 -0
- isar/config/predefined_poses/predefined_poses.py +0 -616
- isar/config/settings.env +0 -25
- isar/mission_planner/sequential_task_selector.py +0 -23
- isar/mission_planner/task_selector_interface.py +0 -31
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -12
- isar/models/communication/queues/__init__.py +0 -4
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queues.py +0 -19
- isar/models/communication/queues/status_queue.py +0 -20
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/readers/__init__.py +0 -0
- isar/services/readers/base_reader.py +0 -37
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/utilities/queue_utilities.py +0 -39
- isar/state_machine/states/idle.py +0 -85
- isar/state_machine/states/initialize.py +0 -71
- isar/state_machine/states/initiate.py +0 -142
- isar/state_machine/states/off.py +0 -18
- isar/state_machine/states/stop.py +0 -95
- isar/storage/slimm_storage.py +0 -191
- isar-1.20.2.dist-info/RECORD +0 -116
- robot_interface/models/initialize/initialize_params.py +0 -9
- robot_interface/models/mission/step.py +0 -234
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
|
@@ -1,263 +1,153 @@
|
|
|
1
|
-
import
|
|
2
|
-
import time
|
|
3
|
-
from copy import deepcopy
|
|
4
|
-
from typing import TYPE_CHECKING, Callable, Optional, Sequence, Tuple, Union
|
|
5
|
-
|
|
6
|
-
from injector import inject
|
|
7
|
-
from transitions import State
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
8
2
|
|
|
3
|
+
from isar.apis.models.models import ControlMissionResponse
|
|
9
4
|
from isar.config.settings import settings
|
|
10
|
-
from isar.
|
|
11
|
-
from isar.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
from robot_interface.models.exceptions.robot_exceptions import (
|
|
16
|
-
ErrorMessage,
|
|
17
|
-
RobotCommunicationTimeoutException,
|
|
18
|
-
RobotException,
|
|
19
|
-
RobotMissionStatusException,
|
|
20
|
-
RobotRetrieveInspectionException,
|
|
21
|
-
RobotStepStatusException,
|
|
5
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
6
|
+
from isar.models.events import Event
|
|
7
|
+
from isar.state_machine.utils.common_event_handlers import (
|
|
8
|
+
mission_started_event_handler,
|
|
9
|
+
stop_mission_event_handler,
|
|
22
10
|
)
|
|
23
|
-
from robot_interface.models.
|
|
24
|
-
from robot_interface.models.mission.
|
|
25
|
-
from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
26
|
-
from robot_interface.models.mission.step import InspectionStep, Step, StepStatus
|
|
11
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
12
|
+
from robot_interface.models.mission.status import MissionStatus
|
|
27
13
|
|
|
28
14
|
if TYPE_CHECKING:
|
|
29
15
|
from isar.state_machine.state_machine import StateMachine
|
|
30
16
|
|
|
31
17
|
|
|
32
|
-
class Monitor(
|
|
33
|
-
@inject
|
|
34
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
35
|
-
super().__init__(name="monitor", on_enter=self.start, on_exit=self.stop)
|
|
36
|
-
self.state_machine: "StateMachine" = state_machine
|
|
37
|
-
self.request_status_failure_counter: int = 0
|
|
38
|
-
self.request_status_failure_counter_limit: int = (
|
|
39
|
-
settings.REQUEST_STATUS_FAILURE_COUNTER_LIMIT
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
self.logger = logging.getLogger("state_machine")
|
|
43
|
-
self.step_status_thread: Optional[ThreadedRequest] = None
|
|
18
|
+
class Monitor(EventHandlerBase):
|
|
44
19
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
20
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
21
|
+
events = state_machine.events
|
|
22
|
+
shared_state = state_machine.shared_state
|
|
48
23
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
self.step_status_thread = None
|
|
24
|
+
def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
|
|
25
|
+
if not event.consume_event():
|
|
26
|
+
return None
|
|
53
27
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
while True:
|
|
57
|
-
if self.state_machine.should_stop_mission():
|
|
58
|
-
transition = self.state_machine.stop # type: ignore
|
|
59
|
-
break
|
|
60
|
-
|
|
61
|
-
if self.state_machine.should_pause_mission():
|
|
62
|
-
transition = self.state_machine.pause # type: ignore
|
|
63
|
-
break
|
|
64
|
-
|
|
65
|
-
if not self.step_status_thread:
|
|
66
|
-
self._run_get_status_thread(
|
|
67
|
-
status_function=self.state_machine.robot.step_status,
|
|
68
|
-
thread_name="State Machine Monitor Get Step Status",
|
|
69
|
-
)
|
|
70
|
-
try:
|
|
71
|
-
status: Union[StepStatus, MissionStatus] = (
|
|
72
|
-
self.step_status_thread.get_output()
|
|
73
|
-
)
|
|
74
|
-
except ThreadedRequestNotFinishedError:
|
|
75
|
-
time.sleep(self.state_machine.sleep_time)
|
|
76
|
-
continue
|
|
77
|
-
|
|
78
|
-
except RobotCommunicationTimeoutException as e:
|
|
79
|
-
self.state_machine.current_mission.error_message = ErrorMessage(
|
|
80
|
-
error_reason=e.error_reason, error_description=e.error_description
|
|
81
|
-
)
|
|
82
|
-
self.step_status_thread = None
|
|
83
|
-
self.request_status_failure_counter += 1
|
|
84
|
-
self.logger.warning(
|
|
85
|
-
f"Monitoring step {self.state_machine.current_step.id} failed #: "
|
|
86
|
-
f"{self.request_status_failure_counter} failed because: {e.error_description}"
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
self.request_status_failure_counter
|
|
91
|
-
>= self.request_status_failure_counter_limit
|
|
92
|
-
):
|
|
93
|
-
self.state_machine.current_step.error_message = ErrorMessage(
|
|
94
|
-
error_reason=e.error_reason,
|
|
95
|
-
error_description=e.error_description,
|
|
96
|
-
)
|
|
97
|
-
self.logger.error(
|
|
98
|
-
f"Step will be cancelled after failing to get step status "
|
|
99
|
-
f"{self.request_status_failure_counter} times because: "
|
|
100
|
-
f"{e.error_description}"
|
|
101
|
-
)
|
|
102
|
-
status = StepStatus.Failed
|
|
103
|
-
else:
|
|
104
|
-
continue
|
|
105
|
-
|
|
106
|
-
except RobotStepStatusException as e:
|
|
107
|
-
self.state_machine.current_step.error_message = ErrorMessage(
|
|
108
|
-
error_reason=e.error_reason, error_description=e.error_description
|
|
109
|
-
)
|
|
110
|
-
self.logger.error(
|
|
111
|
-
f"Monitoring step {self.state_machine.current_step.id[:8]} failed "
|
|
112
|
-
f"because: {e.error_description}"
|
|
113
|
-
)
|
|
114
|
-
status = StepStatus.Failed
|
|
115
|
-
|
|
116
|
-
except RobotMissionStatusException as e:
|
|
117
|
-
self.state_machine.current_mission.error_message = ErrorMessage(
|
|
118
|
-
error_reason=e.error_reason, error_description=e.error_description
|
|
119
|
-
)
|
|
120
|
-
self.logger.error(
|
|
121
|
-
f"Monitoring mission {self.state_machine.current_mission.id} "
|
|
122
|
-
f"failed because: {e.error_description}"
|
|
123
|
-
)
|
|
124
|
-
status = MissionStatus.Failed
|
|
125
|
-
|
|
126
|
-
except RobotException as e:
|
|
127
|
-
self._set_error_message(e)
|
|
128
|
-
status = StepStatus.Failed
|
|
129
|
-
|
|
130
|
-
self.logger.error(
|
|
131
|
-
f"Retrieving the status failed because: {e.error_description}"
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
if isinstance(status, StepStatus):
|
|
135
|
-
self.state_machine.current_step.status = status
|
|
136
|
-
elif isinstance(status, MissionStatus):
|
|
137
|
-
self.state_machine.current_mission.status = status
|
|
138
|
-
self.logger.error(
|
|
139
|
-
f"Received an invalid status update when monitoring mission. Only StepStatus is expected."
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
if self._should_upload_inspections():
|
|
143
|
-
get_inspections_thread = ThreadedRequest(
|
|
144
|
-
self._queue_inspections_for_upload
|
|
145
|
-
)
|
|
146
|
-
get_inspections_thread.start_thread(
|
|
147
|
-
deepcopy(self.state_machine.current_mission),
|
|
148
|
-
deepcopy(self.state_machine.current_step),
|
|
149
|
-
name="State Machine Get Inspections",
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
if self.state_machine.stepwise_mission:
|
|
153
|
-
if self._step_finished(self.state_machine.current_step):
|
|
154
|
-
transition = self.state_machine.step_finished # type: ignore
|
|
155
|
-
break
|
|
156
|
-
else:
|
|
157
|
-
if isinstance(status, StepStatus):
|
|
158
|
-
if self._step_finished(self.state_machine.current_step):
|
|
159
|
-
self.state_machine.update_current_task()
|
|
160
|
-
if self.state_machine.current_task == None:
|
|
161
|
-
transition = self.state_machine.full_mission_finished # type: ignore
|
|
162
|
-
break
|
|
163
|
-
self.state_machine.update_current_step()
|
|
164
|
-
self.state_machine.current_task.update_task_status()
|
|
165
|
-
else: # If not all steps are done
|
|
166
|
-
self.state_machine.current_task.status = TaskStatus.InProgress
|
|
167
|
-
|
|
168
|
-
self.state_machine.publish_task_status(
|
|
169
|
-
self.state_machine.current_task
|
|
170
|
-
)
|
|
171
|
-
if self.state_machine.current_task.status == TaskStatus.Successful:
|
|
172
|
-
try:
|
|
173
|
-
self.state_machine.current_task = (
|
|
174
|
-
self.state_machine.task_selector.next_task()
|
|
175
|
-
)
|
|
176
|
-
except TaskSelectorStop:
|
|
177
|
-
# Indicates that all tasks are finished
|
|
178
|
-
self.state_machine.current_task = None
|
|
179
|
-
transition = self.state_machine.full_mission_finished # type: ignore
|
|
180
|
-
break
|
|
181
|
-
self.state_machine.update_current_step()
|
|
182
|
-
elif self._mission_finished(self.state_machine.current_mission):
|
|
183
|
-
transition = self.state_machine.full_mission_finished # type: ignore
|
|
184
|
-
break
|
|
185
|
-
|
|
186
|
-
self.step_status_thread = None
|
|
187
|
-
time.sleep(self.state_machine.sleep_time)
|
|
188
|
-
|
|
189
|
-
transition()
|
|
190
|
-
|
|
191
|
-
def _run_get_status_thread(
|
|
192
|
-
self, status_function: Callable, thread_name: str
|
|
193
|
-
) -> None:
|
|
194
|
-
self.step_status_thread = ThreadedRequest(request_func=status_function)
|
|
195
|
-
self.step_status_thread.start_thread(name=thread_name)
|
|
196
|
-
|
|
197
|
-
def _queue_inspections_for_upload(
|
|
198
|
-
self, mission: Mission, current_step: InspectionStep
|
|
199
|
-
) -> None:
|
|
200
|
-
try:
|
|
201
|
-
inspections: Sequence[Inspection] = (
|
|
202
|
-
self.state_machine.robot.get_inspections(step=current_step)
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
except (RobotRetrieveInspectionException, RobotException) as e:
|
|
206
|
-
self._set_error_message(e)
|
|
207
|
-
self.logger.error(
|
|
208
|
-
f"Failed to retrieve inspections because: {e.error_description}"
|
|
28
|
+
state_machine.events.api_requests.pause_mission.response.trigger_event(
|
|
29
|
+
ControlMissionResponse(success=True)
|
|
209
30
|
)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
31
|
+
state_machine.events.state_machine_events.pause_mission.trigger_event(True)
|
|
32
|
+
return state_machine.pause # type: ignore
|
|
33
|
+
|
|
34
|
+
def _robot_battery_level_updated_handler(
|
|
35
|
+
event: Event[float],
|
|
36
|
+
) -> Optional[Callable]:
|
|
37
|
+
battery_level: float = event.check()
|
|
38
|
+
if (
|
|
39
|
+
battery_level is None
|
|
40
|
+
or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
|
|
41
|
+
):
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
state_machine.logger.warning(
|
|
45
|
+
"Cancelling current mission due to low battery"
|
|
223
46
|
)
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
47
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
48
|
+
return state_machine.stop_go_to_recharge # type: ignore
|
|
49
|
+
|
|
50
|
+
def _send_to_lockdown_event_handler(
|
|
51
|
+
event: Event[bool],
|
|
52
|
+
) -> Optional[Callable]:
|
|
53
|
+
should_lockdown: bool = event.consume_event()
|
|
54
|
+
if not should_lockdown:
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
state_machine.logger.warning(
|
|
58
|
+
"Cancelling current mission due to robot going to lockdown"
|
|
232
59
|
)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
60
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
61
|
+
return state_machine.stop_go_to_lockdown # type: ignore
|
|
62
|
+
|
|
63
|
+
def _mission_status_event_handler(
|
|
64
|
+
event: Event[MissionStatus],
|
|
65
|
+
) -> Optional[Callable]:
|
|
66
|
+
mission_status: Optional[MissionStatus] = event.consume_event()
|
|
67
|
+
if mission_status:
|
|
68
|
+
if mission_status not in [
|
|
69
|
+
MissionStatus.InProgress,
|
|
70
|
+
MissionStatus.NotStarted,
|
|
71
|
+
MissionStatus.Paused,
|
|
72
|
+
]:
|
|
73
|
+
state_machine.logger.info(
|
|
74
|
+
f"Mission completed with status {mission_status}"
|
|
75
|
+
)
|
|
76
|
+
state_machine.print_transitions()
|
|
77
|
+
return state_machine.mission_finished # type: ignore
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
81
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
82
|
+
if should_set_maintenande_mode:
|
|
83
|
+
state_machine.logger.warning(
|
|
84
|
+
"Cancelling current mission due to robot going to maintenance mode"
|
|
85
|
+
)
|
|
86
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(
|
|
87
|
+
True
|
|
88
|
+
)
|
|
89
|
+
return state_machine.stop_due_to_maintenance # type: ignore
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def _mission_failed_event_handler(
|
|
93
|
+
event: Event[Optional[ErrorMessage]],
|
|
94
|
+
) -> Optional[Callable]:
|
|
95
|
+
mission_failed: Optional[ErrorMessage] = event.consume_event()
|
|
96
|
+
if mission_failed is None:
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
state_machine.logger.warning(
|
|
100
|
+
f"Failed to initiate mission because: "
|
|
101
|
+
f"{mission_failed.error_description}"
|
|
237
102
|
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
103
|
+
return state_machine.mission_failed_to_start # type: ignore
|
|
104
|
+
|
|
105
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
106
|
+
EventHandlerMapping(
|
|
107
|
+
name="stop_mission_event",
|
|
108
|
+
event=events.api_requests.stop_mission.request,
|
|
109
|
+
handler=lambda event: stop_mission_event_handler(state_machine, event),
|
|
110
|
+
),
|
|
111
|
+
EventHandlerMapping(
|
|
112
|
+
name="pause_mission_event",
|
|
113
|
+
event=events.api_requests.pause_mission.request,
|
|
114
|
+
handler=_pause_mission_event_handler,
|
|
115
|
+
),
|
|
116
|
+
EventHandlerMapping(
|
|
117
|
+
name="mission_started_event",
|
|
118
|
+
event=events.robot_service_events.mission_started,
|
|
119
|
+
handler=lambda event: mission_started_event_handler(
|
|
120
|
+
state_machine, event
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
EventHandlerMapping(
|
|
124
|
+
name="mission_failed_event",
|
|
125
|
+
event=events.robot_service_events.mission_failed,
|
|
126
|
+
handler=_mission_failed_event_handler,
|
|
127
|
+
),
|
|
128
|
+
EventHandlerMapping(
|
|
129
|
+
name="mission_status_event",
|
|
130
|
+
event=events.robot_service_events.mission_status_updated,
|
|
131
|
+
handler=_mission_status_event_handler,
|
|
132
|
+
),
|
|
133
|
+
EventHandlerMapping(
|
|
134
|
+
name="robot_battery_update_event",
|
|
135
|
+
event=shared_state.robot_battery_level,
|
|
136
|
+
handler=_robot_battery_level_updated_handler,
|
|
137
|
+
),
|
|
138
|
+
EventHandlerMapping(
|
|
139
|
+
name="send_to_lockdown_event",
|
|
140
|
+
event=events.api_requests.send_to_lockdown.request,
|
|
141
|
+
handler=_send_to_lockdown_event_handler,
|
|
142
|
+
),
|
|
143
|
+
EventHandlerMapping(
|
|
144
|
+
name="set_maintenance_mode",
|
|
145
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
146
|
+
handler=_set_maintenance_mode_event_handler,
|
|
147
|
+
),
|
|
148
|
+
]
|
|
149
|
+
super().__init__(
|
|
150
|
+
state_name="monitor",
|
|
151
|
+
state_machine=state_machine,
|
|
152
|
+
event_handler_mappings=event_handlers,
|
|
262
153
|
)
|
|
263
|
-
self.state_machine.current_step.error_message = error_message
|
|
@@ -1,62 +1,60 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
from isar.config.settings import settings
|
|
8
|
-
from isar.services.utilities.threaded_request import (
|
|
9
|
-
ThreadedRequest,
|
|
10
|
-
ThreadedRequestNotFinishedError,
|
|
11
|
-
)
|
|
12
|
-
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from isar.apis.models.models import MaintenanceResponse
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
13
6
|
from robot_interface.models.mission.status import RobotStatus
|
|
14
7
|
|
|
15
8
|
if TYPE_CHECKING:
|
|
16
9
|
from isar.state_machine.state_machine import StateMachine
|
|
17
10
|
|
|
18
11
|
|
|
19
|
-
class Offline(
|
|
20
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
21
|
-
super().__init__(name="offline", on_enter=self.start, on_exit=self.stop)
|
|
22
|
-
self.state_machine: "StateMachine" = state_machine
|
|
23
|
-
self.logger = logging.getLogger("state_machine")
|
|
24
|
-
self.robot_status_thread: Optional[ThreadedRequest] = None
|
|
25
|
-
|
|
26
|
-
def start(self) -> None:
|
|
27
|
-
self.state_machine.update_state()
|
|
28
|
-
self._run()
|
|
12
|
+
class Offline(EventHandlerBase):
|
|
29
13
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
self.robot_status_thread = None
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
shared_state = state_machine.shared_state
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
self.robot_status_thread.start_thread(
|
|
42
|
-
name="State Machine Offline Get Robot Status"
|
|
18
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
19
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
20
|
+
if should_set_maintenande_mode:
|
|
21
|
+
events.api_requests.set_maintenance_mode.response.trigger_event(
|
|
22
|
+
MaintenanceResponse(is_maintenance_mode=True)
|
|
43
23
|
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
24
|
+
return state_machine.set_maintenance_mode # type: ignore
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
def _robot_status_event_handler(
|
|
28
|
+
status_changed_event: Event[bool],
|
|
29
|
+
) -> Optional[Callable]:
|
|
30
|
+
has_changed = status_changed_event.consume_event()
|
|
31
|
+
if not has_changed:
|
|
32
|
+
return None
|
|
33
|
+
robot_status: Optional[RobotStatus] = shared_state.robot_status.check()
|
|
34
|
+
if robot_status == RobotStatus.Offline:
|
|
35
|
+
return None
|
|
36
|
+
elif robot_status == RobotStatus.Home:
|
|
37
|
+
return state_machine.robot_status_home # type: ignore
|
|
38
|
+
elif robot_status == RobotStatus.Available:
|
|
39
|
+
return state_machine.robot_status_available # type: ignore
|
|
40
|
+
elif robot_status == RobotStatus.BlockedProtectiveStop:
|
|
41
|
+
return state_machine.robot_status_blocked_protective_stop # type: ignore
|
|
42
|
+
return state_machine.robot_status_unknown # type: ignore
|
|
43
|
+
|
|
44
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
45
|
+
EventHandlerMapping(
|
|
46
|
+
name="robot_status_event",
|
|
47
|
+
event=events.robot_service_events.robot_status_changed,
|
|
48
|
+
handler=_robot_status_event_handler,
|
|
49
|
+
),
|
|
50
|
+
EventHandlerMapping(
|
|
51
|
+
name="set_maintenance_mode",
|
|
52
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
53
|
+
handler=_set_maintenance_mode_event_handler,
|
|
54
|
+
),
|
|
55
|
+
]
|
|
56
|
+
super().__init__(
|
|
57
|
+
state_name="offline",
|
|
58
|
+
state_machine=state_machine,
|
|
59
|
+
event_handler_mappings=event_handlers,
|
|
60
|
+
)
|
|
@@ -1,34 +1,103 @@
|
|
|
1
|
-
import
|
|
2
|
-
import time
|
|
3
|
-
from typing import TYPE_CHECKING, Callable
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
4
2
|
|
|
5
|
-
from
|
|
3
|
+
from isar.config.settings import settings
|
|
4
|
+
from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
|
|
5
|
+
from isar.models.events import Event
|
|
6
|
+
from isar.state_machine.utils.common_event_handlers import stop_mission_event_handler
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
8
9
|
from isar.state_machine.state_machine import StateMachine
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
class Paused(
|
|
12
|
-
def __init__(self, state_machine: "StateMachine") -> None:
|
|
13
|
-
super().__init__(name="paused", on_enter=self.start)
|
|
14
|
-
self.state_machine: "StateMachine" = state_machine
|
|
15
|
-
self.logger = logging.getLogger("state_machine")
|
|
12
|
+
class Paused(EventHandlerBase):
|
|
16
13
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
def __init__(self, state_machine: "StateMachine"):
|
|
15
|
+
events = state_machine.events
|
|
16
|
+
shared_state = state_machine.shared_state
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
def _robot_battery_level_updated_handler(
|
|
19
|
+
event: Event[float],
|
|
20
|
+
) -> Optional[Callable]:
|
|
21
|
+
battery_level: float = event.check()
|
|
22
|
+
if (
|
|
23
|
+
battery_level is None
|
|
24
|
+
or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
|
|
25
|
+
):
|
|
26
|
+
return None
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
state_machine.publish_mission_aborted(
|
|
29
|
+
"Robot battery too low to continue mission", True
|
|
30
|
+
)
|
|
31
|
+
state_machine.print_transitions()
|
|
32
|
+
state_machine.logger.warning(
|
|
33
|
+
"Cancelling current mission due to low battery"
|
|
34
|
+
)
|
|
35
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
36
|
+
return state_machine.stop # type: ignore
|
|
31
37
|
|
|
32
|
-
|
|
38
|
+
def _send_to_lockdown_event_handler(
|
|
39
|
+
event: Event[bool],
|
|
40
|
+
) -> Optional[Callable]:
|
|
41
|
+
should_lockdown: bool = event.consume_event()
|
|
42
|
+
if not should_lockdown:
|
|
43
|
+
return None
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
state_machine.print_transitions()
|
|
46
|
+
state_machine.logger.warning(
|
|
47
|
+
"Cancelling current mission due to robot going to lockdown"
|
|
48
|
+
)
|
|
49
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(True)
|
|
50
|
+
return state_machine.stop_go_to_lockdown # type: ignore
|
|
51
|
+
|
|
52
|
+
def _set_maintenance_mode_event_handler(event: Event[bool]):
|
|
53
|
+
should_set_maintenande_mode: bool = event.consume_event()
|
|
54
|
+
if should_set_maintenande_mode:
|
|
55
|
+
state_machine.logger.warning(
|
|
56
|
+
"Cancelling current mission due to robot going to maintenance mode"
|
|
57
|
+
)
|
|
58
|
+
state_machine.events.state_machine_events.stop_mission.trigger_event(
|
|
59
|
+
True
|
|
60
|
+
)
|
|
61
|
+
return state_machine.stop_due_to_maintenance # type: ignore
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
def _resume_misison_event_handler(event: Event[bool]):
|
|
65
|
+
if event.consume_event():
|
|
66
|
+
state_machine.events.state_machine_events.resume_mission.trigger_event(
|
|
67
|
+
True
|
|
68
|
+
)
|
|
69
|
+
return state_machine.resume # type: ignore
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
event_handlers: List[EventHandlerMapping] = [
|
|
73
|
+
EventHandlerMapping(
|
|
74
|
+
name="stop_mission_event",
|
|
75
|
+
event=events.api_requests.stop_mission.request,
|
|
76
|
+
handler=lambda event: stop_mission_event_handler(state_machine, event),
|
|
77
|
+
),
|
|
78
|
+
EventHandlerMapping(
|
|
79
|
+
name="resume_mission_event",
|
|
80
|
+
event=events.api_requests.resume_mission.request,
|
|
81
|
+
handler=_resume_misison_event_handler,
|
|
82
|
+
),
|
|
83
|
+
EventHandlerMapping(
|
|
84
|
+
name="robot_battery_update_event",
|
|
85
|
+
event=shared_state.robot_battery_level,
|
|
86
|
+
handler=_robot_battery_level_updated_handler,
|
|
87
|
+
),
|
|
88
|
+
EventHandlerMapping(
|
|
89
|
+
name="send_to_lockdown_event",
|
|
90
|
+
event=events.api_requests.send_to_lockdown.request,
|
|
91
|
+
handler=_send_to_lockdown_event_handler,
|
|
92
|
+
),
|
|
93
|
+
EventHandlerMapping(
|
|
94
|
+
name="set_maintenance_mode",
|
|
95
|
+
event=events.api_requests.set_maintenance_mode.request,
|
|
96
|
+
handler=_set_maintenance_mode_event_handler,
|
|
97
|
+
),
|
|
98
|
+
]
|
|
99
|
+
super().__init__(
|
|
100
|
+
state_name="paused",
|
|
101
|
+
state_machine=state_machine,
|
|
102
|
+
event_handler_mappings=event_handlers,
|
|
103
|
+
)
|