isar 1.25.9__py3-none-any.whl → 1.26.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/models/start_mission_definition.py +55 -108
- isar/apis/robot_control/robot_controller.py +5 -4
- isar/apis/schedule/scheduling_controller.py +4 -32
- isar/apis/security/authentication.py +2 -2
- isar/config/settings.env +1 -3
- isar/config/settings.py +5 -5
- isar/models/communication/message.py +0 -4
- isar/models/communication/queues/queue_utils.py +27 -0
- isar/models/communication/queues/queues.py +23 -5
- isar/robot/robot.py +91 -0
- isar/robot/robot_start_mission.py +73 -0
- isar/robot/robot_status.py +46 -0
- isar/robot/robot_task_status.py +92 -0
- isar/script.py +7 -0
- isar/services/utilities/scheduling_utilities.py +15 -26
- isar/state_machine/state_machine.py +94 -183
- isar/state_machine/states/blocked_protective_stop.py +7 -19
- isar/state_machine/states/idle.py +12 -54
- isar/state_machine/states/monitor.py +43 -90
- isar/state_machine/states/offline.py +8 -33
- isar/state_machine/states/paused.py +1 -1
- isar/state_machine/states_enum.py +0 -2
- isar/state_machine/transitions/fail_mission.py +13 -0
- isar/state_machine/transitions/finish_mission.py +39 -0
- isar/state_machine/transitions/pause.py +24 -0
- isar/state_machine/transitions/resume.py +27 -0
- isar/state_machine/transitions/start_mission.py +73 -0
- isar/state_machine/transitions/stop.py +33 -0
- isar/state_machine/transitions/utils.py +10 -0
- isar/storage/slimm_storage.py +2 -2
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/METADATA +2 -3
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/RECORD +40 -30
- robot_interface/models/mission/task.py +1 -1
- robot_interface/telemetry/mqtt_client.py +0 -1
- robot_interface/telemetry/payloads.py +3 -3
- robot_interface/utilities/json_service.py +1 -1
- isar/state_machine/states/initialize.py +0 -70
- isar/state_machine/states/initiate.py +0 -111
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/LICENSE +0 -0
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/WHEEL +0 -0
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/entry_points.txt +0 -0
- {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,6 @@ from collections import deque
|
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
6
|
from typing import Deque, List, Optional
|
|
7
7
|
|
|
8
|
-
from alitra import Pose
|
|
9
8
|
from injector import inject
|
|
10
9
|
from transitions import Machine
|
|
11
10
|
from transitions.core import State
|
|
@@ -18,25 +17,38 @@ from isar.mission_planner.task_selector_interface import (
|
|
|
18
17
|
)
|
|
19
18
|
from isar.models.communication.message import StartMissionMessage
|
|
20
19
|
from isar.models.communication.queues.queues import Queues
|
|
20
|
+
from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
|
|
21
21
|
from isar.state_machine.states.idle import Idle
|
|
22
|
-
from isar.state_machine.states.initialize import Initialize
|
|
23
|
-
from isar.state_machine.states.initiate import Initiate
|
|
24
22
|
from isar.state_machine.states.monitor import Monitor
|
|
25
23
|
from isar.state_machine.states.off import Off
|
|
26
24
|
from isar.state_machine.states.offline import Offline
|
|
27
|
-
from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
|
|
28
25
|
from isar.state_machine.states.paused import Paused
|
|
29
26
|
from isar.state_machine.states.stop import Stop
|
|
30
27
|
from isar.state_machine.states_enum import States
|
|
28
|
+
from isar.state_machine.transitions.fail_mission import (
|
|
29
|
+
report_failed_mission_and_finalize,
|
|
30
|
+
)
|
|
31
|
+
from isar.state_machine.transitions.finish_mission import finish_mission
|
|
32
|
+
from isar.state_machine.transitions.pause import pause_mission
|
|
33
|
+
from isar.state_machine.transitions.resume import resume_mission
|
|
34
|
+
from isar.state_machine.transitions.start_mission import (
|
|
35
|
+
initialize_robot,
|
|
36
|
+
initiate_mission,
|
|
37
|
+
put_start_mission_on_queue,
|
|
38
|
+
set_mission_to_in_progress,
|
|
39
|
+
trigger_start_mission_or_task_event,
|
|
40
|
+
)
|
|
41
|
+
from isar.state_machine.transitions.stop import stop_mission
|
|
42
|
+
from isar.state_machine.transitions.utils import def_transition
|
|
31
43
|
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
32
44
|
from robot_interface.models.mission.mission import Mission
|
|
33
|
-
from robot_interface.models.mission.status import
|
|
45
|
+
from robot_interface.models.mission.status import RobotStatus, TaskStatus
|
|
34
46
|
from robot_interface.models.mission.task import TASKS
|
|
35
47
|
from robot_interface.robot_interface import RobotInterface
|
|
36
48
|
from robot_interface.telemetry.mqtt_client import MqttClientInterface
|
|
37
49
|
from robot_interface.telemetry.payloads import (
|
|
38
|
-
RobotStatusPayload,
|
|
39
50
|
MissionPayload,
|
|
51
|
+
RobotStatusPayload,
|
|
40
52
|
TaskPayload,
|
|
41
53
|
)
|
|
42
54
|
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
@@ -85,9 +97,7 @@ class StateMachine(object):
|
|
|
85
97
|
self.stop_state: State = Stop(self)
|
|
86
98
|
self.paused_state: State = Paused(self)
|
|
87
99
|
self.idle_state: State = Idle(self)
|
|
88
|
-
self.initialize_state: State = Initialize(self)
|
|
89
100
|
self.monitor_state: State = Monitor(self)
|
|
90
|
-
self.initiate_state: State = Initiate(self)
|
|
91
101
|
self.off_state: State = Off(self)
|
|
92
102
|
self.offline_state: State = Offline(self)
|
|
93
103
|
self.blocked_protective_stop: State = BlockedProtectiveStop(self)
|
|
@@ -95,8 +105,6 @@ class StateMachine(object):
|
|
|
95
105
|
self.states: List[State] = [
|
|
96
106
|
self.off_state,
|
|
97
107
|
self.idle_state,
|
|
98
|
-
self.initialize_state,
|
|
99
|
-
self.initiate_state,
|
|
100
108
|
self.monitor_state,
|
|
101
109
|
self.stop_state,
|
|
102
110
|
self.paused_state,
|
|
@@ -112,73 +120,67 @@ class StateMachine(object):
|
|
|
112
120
|
"source": self.off_state,
|
|
113
121
|
"dest": self.idle_state,
|
|
114
122
|
},
|
|
115
|
-
{
|
|
116
|
-
"trigger": "initiated",
|
|
117
|
-
"source": self.initiate_state,
|
|
118
|
-
"dest": self.monitor_state,
|
|
119
|
-
"before": self._initiated,
|
|
120
|
-
},
|
|
121
123
|
{
|
|
122
124
|
"trigger": "pause",
|
|
123
125
|
"source": self.monitor_state,
|
|
124
126
|
"dest": self.paused_state,
|
|
125
|
-
"before": self
|
|
127
|
+
"before": def_transition(self, pause_mission),
|
|
126
128
|
},
|
|
127
129
|
{
|
|
128
130
|
"trigger": "stop",
|
|
129
131
|
"source": [
|
|
130
132
|
self.idle_state,
|
|
131
|
-
self.initiate_state,
|
|
132
133
|
self.monitor_state,
|
|
133
134
|
self.paused_state,
|
|
134
135
|
],
|
|
135
136
|
"dest": self.stop_state,
|
|
136
137
|
},
|
|
137
138
|
{
|
|
138
|
-
"trigger": "
|
|
139
|
-
"source": self.monitor_state,
|
|
140
|
-
"dest": self.idle_state,
|
|
141
|
-
"before": self._mission_finished,
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
"trigger": "mission_started",
|
|
139
|
+
"trigger": "request_mission_start",
|
|
145
140
|
"source": self.idle_state,
|
|
146
|
-
"dest": self.
|
|
147
|
-
"
|
|
141
|
+
"dest": self.monitor_state,
|
|
142
|
+
"prepare": def_transition(self, put_start_mission_on_queue),
|
|
143
|
+
"conditions": [
|
|
144
|
+
def_transition(self, initiate_mission),
|
|
145
|
+
def_transition(self, initialize_robot),
|
|
146
|
+
],
|
|
147
|
+
"before": [
|
|
148
|
+
def_transition(self, set_mission_to_in_progress),
|
|
149
|
+
def_transition(self, trigger_start_mission_or_task_event),
|
|
150
|
+
],
|
|
148
151
|
},
|
|
149
152
|
{
|
|
150
|
-
"trigger": "
|
|
151
|
-
"source": self.
|
|
152
|
-
"dest": self.
|
|
153
|
-
"before": self._initialization_successful,
|
|
153
|
+
"trigger": "request_mission_start",
|
|
154
|
+
"source": self.idle_state,
|
|
155
|
+
"dest": self.idle_state,
|
|
154
156
|
},
|
|
155
157
|
{
|
|
156
|
-
"trigger": "
|
|
157
|
-
"source": self.
|
|
158
|
+
"trigger": "mission_failed_to_start",
|
|
159
|
+
"source": self.monitor_state,
|
|
158
160
|
"dest": self.idle_state,
|
|
159
|
-
"before": self
|
|
161
|
+
"before": def_transition(self, report_failed_mission_and_finalize),
|
|
160
162
|
},
|
|
161
163
|
{
|
|
162
164
|
"trigger": "resume",
|
|
163
165
|
"source": self.paused_state,
|
|
164
166
|
"dest": self.monitor_state,
|
|
165
|
-
"before": self
|
|
167
|
+
"before": def_transition(self, resume_mission),
|
|
166
168
|
},
|
|
167
169
|
{
|
|
168
|
-
"trigger": "
|
|
169
|
-
"source": self.
|
|
170
|
+
"trigger": "mission_finished",
|
|
171
|
+
"source": self.monitor_state,
|
|
170
172
|
"dest": self.idle_state,
|
|
171
|
-
"before": self
|
|
173
|
+
"before": def_transition(self, finish_mission),
|
|
172
174
|
},
|
|
173
175
|
{
|
|
174
176
|
"trigger": "mission_stopped",
|
|
175
177
|
"source": self.stop_state,
|
|
176
178
|
"dest": self.idle_state,
|
|
177
|
-
"before": self
|
|
179
|
+
"before": def_transition(self, stop_mission),
|
|
178
180
|
},
|
|
179
181
|
{
|
|
180
182
|
"trigger": "robot_turned_offline",
|
|
181
|
-
"source": self.idle_state,
|
|
183
|
+
"source": [self.idle_state, self.blocked_protective_stop],
|
|
182
184
|
"dest": self.offline_state,
|
|
183
185
|
},
|
|
184
186
|
{
|
|
@@ -188,7 +190,7 @@ class StateMachine(object):
|
|
|
188
190
|
},
|
|
189
191
|
{
|
|
190
192
|
"trigger": "robot_protective_stop_engaged",
|
|
191
|
-
"source": self.idle_state,
|
|
193
|
+
"source": [self.idle_state, self.offline_state],
|
|
192
194
|
"dest": self.blocked_protective_stop,
|
|
193
195
|
},
|
|
194
196
|
{
|
|
@@ -204,137 +206,14 @@ class StateMachine(object):
|
|
|
204
206
|
|
|
205
207
|
self.current_mission: Optional[Mission] = None
|
|
206
208
|
self.current_task: Optional[TASKS] = None
|
|
207
|
-
|
|
209
|
+
|
|
210
|
+
self.mission_ongoing: bool = False
|
|
208
211
|
|
|
209
212
|
self.current_state: State = States(self.state) # type: ignore
|
|
210
213
|
|
|
211
214
|
self.transitions_log_length: int = transitions_log_length
|
|
212
215
|
self.transitions_list: Deque[States] = deque([], self.transitions_log_length)
|
|
213
216
|
|
|
214
|
-
#################################################################################
|
|
215
|
-
# Transition Callbacks
|
|
216
|
-
def _initialization_successful(self) -> None:
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
def _initialization_failed(self) -> None:
|
|
220
|
-
self.queues.start_mission.output.put(False)
|
|
221
|
-
self._finalize()
|
|
222
|
-
|
|
223
|
-
def _initiated(self) -> None:
|
|
224
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
225
|
-
self.publish_task_status(task=self.current_task)
|
|
226
|
-
self.logger.info(
|
|
227
|
-
f"Successfully initiated "
|
|
228
|
-
f"{type(self.current_task).__name__} "
|
|
229
|
-
f"task: {str(self.current_task.id)[:8]}"
|
|
230
|
-
)
|
|
231
|
-
|
|
232
|
-
def _resume(self) -> None:
|
|
233
|
-
self.logger.info(f"Resuming mission: {self.current_mission.id}")
|
|
234
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
235
|
-
self.current_mission.error_message = None
|
|
236
|
-
self.current_task.status = TaskStatus.InProgress
|
|
237
|
-
|
|
238
|
-
self.publish_mission_status()
|
|
239
|
-
self.publish_task_status(task=self.current_task)
|
|
240
|
-
|
|
241
|
-
resume_mission_response: ControlMissionResponse = (
|
|
242
|
-
self._make_control_mission_response()
|
|
243
|
-
)
|
|
244
|
-
self.queues.resume_mission.output.put(resume_mission_response)
|
|
245
|
-
|
|
246
|
-
self.robot.resume()
|
|
247
|
-
|
|
248
|
-
def _mission_finished(self) -> None:
|
|
249
|
-
fail_statuses: List[TaskStatus] = [
|
|
250
|
-
TaskStatus.Cancelled,
|
|
251
|
-
TaskStatus.Failed,
|
|
252
|
-
]
|
|
253
|
-
partially_fail_statuses = fail_statuses + [TaskStatus.PartiallySuccessful]
|
|
254
|
-
|
|
255
|
-
if len(self.current_mission.tasks) == 0:
|
|
256
|
-
self.current_mission.status = MissionStatus.Successful
|
|
257
|
-
elif all(task.status in fail_statuses for task in self.current_mission.tasks):
|
|
258
|
-
self.current_mission.error_message = ErrorMessage(
|
|
259
|
-
error_reason=None,
|
|
260
|
-
error_description="The mission failed because all tasks in the mission "
|
|
261
|
-
"failed",
|
|
262
|
-
)
|
|
263
|
-
self.current_mission.status = MissionStatus.Failed
|
|
264
|
-
elif any(
|
|
265
|
-
task.status in partially_fail_statuses
|
|
266
|
-
for task in self.current_mission.tasks
|
|
267
|
-
):
|
|
268
|
-
self.current_mission.status = MissionStatus.PartiallySuccessful
|
|
269
|
-
else:
|
|
270
|
-
self.current_mission.status = MissionStatus.Successful
|
|
271
|
-
self._finalize()
|
|
272
|
-
|
|
273
|
-
def _mission_started(self) -> None:
|
|
274
|
-
self.queues.start_mission.output.put(True)
|
|
275
|
-
self.logger.info(
|
|
276
|
-
f"Initialization successful. Starting new mission: "
|
|
277
|
-
f"{self.current_mission.id}"
|
|
278
|
-
)
|
|
279
|
-
self.log_mission_overview(mission=self.current_mission)
|
|
280
|
-
|
|
281
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
282
|
-
self.publish_mission_status()
|
|
283
|
-
self.current_task = self.task_selector.next_task()
|
|
284
|
-
if self.current_task is None:
|
|
285
|
-
self._mission_finished()
|
|
286
|
-
else:
|
|
287
|
-
self.current_task.status = TaskStatus.InProgress
|
|
288
|
-
self.publish_task_status(task=self.current_task)
|
|
289
|
-
|
|
290
|
-
def _full_mission_finished(self) -> None:
|
|
291
|
-
self.current_task = None
|
|
292
|
-
|
|
293
|
-
def _mission_paused(self) -> None:
|
|
294
|
-
self.logger.info(f"Pausing mission: {self.current_mission.id}")
|
|
295
|
-
self.current_mission.status = MissionStatus.Paused
|
|
296
|
-
self.current_task.status = TaskStatus.Paused
|
|
297
|
-
|
|
298
|
-
paused_mission_response: ControlMissionResponse = (
|
|
299
|
-
self._make_control_mission_response()
|
|
300
|
-
)
|
|
301
|
-
self.queues.pause_mission.output.put(paused_mission_response)
|
|
302
|
-
|
|
303
|
-
self.publish_mission_status()
|
|
304
|
-
self.publish_task_status(task=self.current_task)
|
|
305
|
-
|
|
306
|
-
self.robot.pause()
|
|
307
|
-
|
|
308
|
-
def _initiate_failed(self) -> None:
|
|
309
|
-
self.current_task.status = TaskStatus.Failed
|
|
310
|
-
self.current_mission.status = MissionStatus.Failed
|
|
311
|
-
self.publish_task_status(task=self.current_task)
|
|
312
|
-
self._finalize()
|
|
313
|
-
|
|
314
|
-
def _mission_stopped(self) -> None:
|
|
315
|
-
if self.current_mission is None:
|
|
316
|
-
self._queue_empty_response()
|
|
317
|
-
self.reset_state_machine()
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
self.current_mission.status = MissionStatus.Cancelled
|
|
321
|
-
|
|
322
|
-
for task in self.current_mission.tasks:
|
|
323
|
-
if task.status in [
|
|
324
|
-
TaskStatus.NotStarted,
|
|
325
|
-
TaskStatus.InProgress,
|
|
326
|
-
TaskStatus.Paused,
|
|
327
|
-
]:
|
|
328
|
-
task.status = TaskStatus.Cancelled
|
|
329
|
-
|
|
330
|
-
stopped_mission_response: ControlMissionResponse = (
|
|
331
|
-
self._make_control_mission_response()
|
|
332
|
-
)
|
|
333
|
-
self.queues.stop_mission.output.put(stopped_mission_response)
|
|
334
|
-
|
|
335
|
-
self.publish_task_status(task=self.current_task)
|
|
336
|
-
self._finalize()
|
|
337
|
-
|
|
338
217
|
#################################################################################
|
|
339
218
|
|
|
340
219
|
def _finalize(self) -> None:
|
|
@@ -350,11 +229,7 @@ class StateMachine(object):
|
|
|
350
229
|
self.reset_state_machine()
|
|
351
230
|
|
|
352
231
|
def begin(self):
|
|
353
|
-
"""Starts the state machine.
|
|
354
|
-
|
|
355
|
-
Transitions into idle state.
|
|
356
|
-
|
|
357
|
-
"""
|
|
232
|
+
"""Starts the state machine. Transitions into idle state."""
|
|
358
233
|
self.to_idle() # type: ignore
|
|
359
234
|
|
|
360
235
|
def iterate_current_task(self):
|
|
@@ -366,6 +241,7 @@ class StateMachine(object):
|
|
|
366
241
|
except TaskSelectorStop:
|
|
367
242
|
# Indicates that all tasks are finished
|
|
368
243
|
self.current_task = None
|
|
244
|
+
self.send_task_status()
|
|
369
245
|
|
|
370
246
|
def update_state(self):
|
|
371
247
|
"""Updates the current state of the state machine."""
|
|
@@ -378,43 +254,78 @@ class StateMachine(object):
|
|
|
378
254
|
def reset_state_machine(self) -> None:
|
|
379
255
|
self.logger.info("Resetting state machine")
|
|
380
256
|
self.current_task = None
|
|
257
|
+
self.send_task_status()
|
|
381
258
|
self.current_mission = None
|
|
382
|
-
self.initial_pose = None
|
|
383
259
|
|
|
384
|
-
def start_mission(self, mission: Mission
|
|
260
|
+
def start_mission(self, mission: Mission):
|
|
385
261
|
"""Starts a scheduled mission."""
|
|
386
262
|
self.current_mission = mission
|
|
387
|
-
self.initial_pose = initial_pose
|
|
388
263
|
|
|
389
264
|
self.task_selector.initialize(tasks=self.current_mission.tasks)
|
|
390
265
|
|
|
391
266
|
def should_start_mission(self) -> Optional[StartMissionMessage]:
|
|
392
267
|
try:
|
|
393
|
-
return self.queues.
|
|
268
|
+
return self.queues.api_start_mission.input.get(block=False)
|
|
394
269
|
except queue.Empty:
|
|
395
270
|
return None
|
|
396
271
|
|
|
397
272
|
def should_stop_mission(self) -> bool:
|
|
398
273
|
try:
|
|
399
|
-
return self.queues.
|
|
274
|
+
return self.queues.api_stop_mission.input.get(block=False)
|
|
400
275
|
except queue.Empty:
|
|
401
276
|
return False
|
|
402
277
|
|
|
403
278
|
def should_pause_mission(self) -> bool:
|
|
404
279
|
try:
|
|
405
|
-
return self.queues.
|
|
280
|
+
return self.queues.api_pause_mission.input.get(block=False)
|
|
281
|
+
except queue.Empty:
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
def get_task_status_event(self) -> Optional[TaskStatus]:
|
|
285
|
+
try:
|
|
286
|
+
return self.queues.robot_task_status.input.get(block=False)
|
|
287
|
+
except queue.Empty:
|
|
288
|
+
return None
|
|
289
|
+
|
|
290
|
+
def request_task_status(self, task_id: str) -> None:
|
|
291
|
+
self.queues.state_machine_task_status_request.input.put(task_id)
|
|
292
|
+
|
|
293
|
+
def get_mission_started_event(self) -> bool:
|
|
294
|
+
try:
|
|
295
|
+
return self.queues.robot_mission_started.input.get(block=False)
|
|
406
296
|
except queue.Empty:
|
|
407
297
|
return False
|
|
408
298
|
|
|
299
|
+
def get_mission_failed_event(self) -> Optional[ErrorMessage]:
|
|
300
|
+
try:
|
|
301
|
+
return self.queues.robot_mission_failed.input.get(block=False)
|
|
302
|
+
except queue.Empty:
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
def get_task_failure_event(self) -> Optional[ErrorMessage]:
|
|
306
|
+
try:
|
|
307
|
+
return self.queues.robot_task_status_failed.input.get(block=False)
|
|
308
|
+
except queue.Empty:
|
|
309
|
+
return None
|
|
310
|
+
|
|
409
311
|
def should_resume_mission(self) -> bool:
|
|
410
312
|
try:
|
|
411
|
-
return self.queues.
|
|
313
|
+
return self.queues.api_resume_mission.input.get(block=False)
|
|
412
314
|
except queue.Empty:
|
|
413
315
|
return False
|
|
414
316
|
|
|
415
|
-
def
|
|
317
|
+
def get_robot_status(self) -> bool:
|
|
318
|
+
try:
|
|
319
|
+
return self.queues.robot_status.check()
|
|
320
|
+
except queue.Empty:
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
def send_state_status(self) -> None:
|
|
416
324
|
self.queues.state.update(self.current_state)
|
|
417
325
|
|
|
326
|
+
def send_task_status(self):
|
|
327
|
+
self.queues.state_machine_current_task.update(self.current_task)
|
|
328
|
+
|
|
418
329
|
def publish_mission_status(self) -> None:
|
|
419
330
|
if not self.mqtt_publisher:
|
|
420
331
|
return
|
|
@@ -502,11 +413,11 @@ class StateMachine(object):
|
|
|
502
413
|
else:
|
|
503
414
|
return RobotStatus.Busy
|
|
504
415
|
|
|
505
|
-
def _log_state_transition(self, next_state):
|
|
416
|
+
def _log_state_transition(self, next_state) -> None:
|
|
506
417
|
"""Logs all state transitions that are not self-transitions."""
|
|
507
418
|
self.transitions_list.append(next_state)
|
|
508
419
|
|
|
509
|
-
def log_mission_overview(self, mission: Mission):
|
|
420
|
+
def log_mission_overview(self, mission: Mission) -> None:
|
|
510
421
|
"""Log an overview of the tasks in a mission"""
|
|
511
422
|
log_statements: List[str] = []
|
|
512
423
|
for task in mission.tasks:
|
|
@@ -525,8 +436,8 @@ class StateMachine(object):
|
|
|
525
436
|
task_status=self.current_task.status,
|
|
526
437
|
)
|
|
527
438
|
|
|
528
|
-
def _queue_empty_response(self):
|
|
529
|
-
self.queues.
|
|
439
|
+
def _queue_empty_response(self) -> None:
|
|
440
|
+
self.queues.api_stop_mission.output.put(
|
|
530
441
|
ControlMissionResponse(
|
|
531
442
|
mission_id="None",
|
|
532
443
|
mission_status="None",
|
|
@@ -4,12 +4,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
|
-
from isar.
|
|
8
|
-
from isar.services.utilities.threaded_request import (
|
|
9
|
-
ThreadedRequest,
|
|
10
|
-
ThreadedRequestNotFinishedError,
|
|
11
|
-
)
|
|
12
|
-
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
7
|
+
from isar.services.utilities.threaded_request import ThreadedRequest
|
|
13
8
|
from robot_interface.models.mission.status import RobotStatus
|
|
14
9
|
|
|
15
10
|
if TYPE_CHECKING:
|
|
@@ -44,22 +39,15 @@ class BlockedProtectiveStop(State):
|
|
|
44
39
|
name="State Machine BlockedProtectiveStop Get Robot Status"
|
|
45
40
|
)
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
robot_status: RobotStatus = self.robot_status_thread.get_output()
|
|
49
|
-
except ThreadedRequestNotFinishedError:
|
|
50
|
-
time.sleep(self.state_machine.sleep_time)
|
|
51
|
-
continue
|
|
42
|
+
robot_status = self.state_machine.get_robot_status()
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
self.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if robot_status != RobotStatus.BlockedProtectiveStop:
|
|
44
|
+
if robot_status == RobotStatus.Offline:
|
|
45
|
+
transition = self.state_machine.robot_turned_offline # type: ignore
|
|
46
|
+
break
|
|
47
|
+
elif robot_status != RobotStatus.BlockedProtectiveStop:
|
|
59
48
|
transition = self.state_machine.robot_protective_stop_disengaged # type: ignore
|
|
60
49
|
break
|
|
61
50
|
|
|
62
|
-
self.
|
|
63
|
-
time.sleep(settings.ROBOT_API_STATUS_POLL_INTERVAL)
|
|
51
|
+
time.sleep(self.state_machine.sleep_time)
|
|
64
52
|
|
|
65
53
|
transition()
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
from typing import TYPE_CHECKING, Callable, Optional
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
7
|
from isar.config.settings import settings
|
|
8
8
|
from isar.models.communication.message import StartMissionMessage
|
|
9
|
-
from isar.services.utilities.threaded_request import (
|
|
10
|
-
ThreadedRequest,
|
|
11
|
-
ThreadedRequestNotFinishedError,
|
|
12
|
-
)
|
|
13
|
-
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
14
9
|
from robot_interface.models.mission.status import RobotStatus
|
|
15
10
|
|
|
16
11
|
if TYPE_CHECKING:
|
|
@@ -22,24 +17,16 @@ class Idle(State):
|
|
|
22
17
|
super().__init__(name="idle", on_enter=self.start, on_exit=self.stop)
|
|
23
18
|
self.state_machine: "StateMachine" = state_machine
|
|
24
19
|
self.logger = logging.getLogger("state_machine")
|
|
25
|
-
self.robot_status_thread: Optional[ThreadedRequest] = None
|
|
26
20
|
self.last_robot_status_poll_time: float = time.time()
|
|
27
|
-
self.status_checked_at_least_once: bool = False
|
|
28
21
|
|
|
29
22
|
def start(self) -> None:
|
|
30
23
|
self.state_machine.update_state()
|
|
31
24
|
self._run()
|
|
32
25
|
|
|
33
26
|
def stop(self) -> None:
|
|
34
|
-
|
|
35
|
-
self.robot_status_thread.wait_for_thread()
|
|
36
|
-
self.robot_status_thread = None
|
|
37
|
-
self.status_checked_at_least_once = False
|
|
27
|
+
return
|
|
38
28
|
|
|
39
29
|
def _is_ready_to_poll_for_status(self) -> bool:
|
|
40
|
-
if not self.status_checked_at_least_once:
|
|
41
|
-
return True
|
|
42
|
-
|
|
43
30
|
time_since_last_robot_status_poll = (
|
|
44
31
|
time.time() - self.last_robot_status_poll_time
|
|
45
32
|
)
|
|
@@ -48,49 +35,21 @@ class Idle(State):
|
|
|
48
35
|
)
|
|
49
36
|
|
|
50
37
|
def _run(self) -> None:
|
|
38
|
+
transition: Callable
|
|
51
39
|
while True:
|
|
52
40
|
if self.state_machine.should_stop_mission():
|
|
53
41
|
transition = self.state_machine.stop # type: ignore
|
|
54
42
|
break
|
|
55
43
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
initial_pose=start_mission.initial_pose,
|
|
64
|
-
)
|
|
65
|
-
transition = self.state_machine.mission_started # type: ignore
|
|
66
|
-
break
|
|
67
|
-
time.sleep(self.state_machine.sleep_time)
|
|
68
|
-
|
|
69
|
-
if not self._is_ready_to_poll_for_status():
|
|
70
|
-
continue
|
|
71
|
-
|
|
72
|
-
if not self.robot_status_thread:
|
|
73
|
-
self.robot_status_thread = ThreadedRequest(
|
|
74
|
-
request_func=self.state_machine.robot.robot_status
|
|
75
|
-
)
|
|
76
|
-
self.robot_status_thread.start_thread(
|
|
77
|
-
name="State Machine Offline Get Robot Status"
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
try:
|
|
81
|
-
robot_status: RobotStatus = self.robot_status_thread.get_output()
|
|
82
|
-
self.status_checked_at_least_once = True
|
|
83
|
-
except ThreadedRequestNotFinishedError:
|
|
84
|
-
time.sleep(self.state_machine.sleep_time)
|
|
85
|
-
continue
|
|
86
|
-
|
|
87
|
-
except RobotException as e:
|
|
88
|
-
self.logger.error(
|
|
89
|
-
f"Failed to get robot status because: {e.error_description}"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
self.last_robot_status_poll_time = time.time()
|
|
44
|
+
start_mission: Optional[StartMissionMessage] = (
|
|
45
|
+
self.state_machine.should_start_mission()
|
|
46
|
+
)
|
|
47
|
+
if start_mission:
|
|
48
|
+
self.state_machine.start_mission(mission=start_mission.mission)
|
|
49
|
+
transition = self.state_machine.request_mission_start # type: ignore
|
|
50
|
+
break
|
|
93
51
|
|
|
52
|
+
robot_status = self.state_machine.get_robot_status()
|
|
94
53
|
if robot_status == RobotStatus.Offline:
|
|
95
54
|
transition = self.state_machine.robot_turned_offline # type: ignore
|
|
96
55
|
break
|
|
@@ -98,6 +57,5 @@ class Idle(State):
|
|
|
98
57
|
transition = self.state_machine.robot_protective_stop_engaged # type: ignore
|
|
99
58
|
break
|
|
100
59
|
|
|
101
|
-
self.
|
|
102
|
-
|
|
60
|
+
time.sleep(self.state_machine.sleep_time)
|
|
103
61
|
transition()
|