isar 1.25.8__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 -187
- 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.8.dist-info → isar-1.26.0.dist-info}/METADATA +2 -3
- {isar-1.25.8.dist-info → isar-1.26.0.dist-info}/RECORD +42 -33
- robot_interface/models/exceptions/robot_exceptions.py +0 -24
- robot_interface/models/mission/task.py +1 -1
- robot_interface/robot_interface.py +1 -6
- 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 -71
- isar/state_machine/states/initiate.py +0 -111
- robot_interface/models/initialize/initialize_params.py +0 -9
- {isar-1.25.8.dist-info → isar-1.26.0.dist-info}/LICENSE +0 -0
- {isar-1.25.8.dist-info → isar-1.26.0.dist-info}/WHEEL +0 -0
- {isar-1.25.8.dist-info → isar-1.26.0.dist-info}/entry_points.txt +0 -0
- {isar-1.25.8.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,26 +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
|
-
from robot_interface.models.initialize.initialize_params import InitializeParams
|
|
33
44
|
from robot_interface.models.mission.mission import Mission
|
|
34
|
-
from robot_interface.models.mission.status import
|
|
45
|
+
from robot_interface.models.mission.status import RobotStatus, TaskStatus
|
|
35
46
|
from robot_interface.models.mission.task import TASKS
|
|
36
47
|
from robot_interface.robot_interface import RobotInterface
|
|
37
48
|
from robot_interface.telemetry.mqtt_client import MqttClientInterface
|
|
38
49
|
from robot_interface.telemetry.payloads import (
|
|
39
|
-
RobotStatusPayload,
|
|
40
50
|
MissionPayload,
|
|
51
|
+
RobotStatusPayload,
|
|
41
52
|
TaskPayload,
|
|
42
53
|
)
|
|
43
54
|
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
@@ -86,9 +97,7 @@ class StateMachine(object):
|
|
|
86
97
|
self.stop_state: State = Stop(self)
|
|
87
98
|
self.paused_state: State = Paused(self)
|
|
88
99
|
self.idle_state: State = Idle(self)
|
|
89
|
-
self.initialize_state: State = Initialize(self)
|
|
90
100
|
self.monitor_state: State = Monitor(self)
|
|
91
|
-
self.initiate_state: State = Initiate(self)
|
|
92
101
|
self.off_state: State = Off(self)
|
|
93
102
|
self.offline_state: State = Offline(self)
|
|
94
103
|
self.blocked_protective_stop: State = BlockedProtectiveStop(self)
|
|
@@ -96,8 +105,6 @@ class StateMachine(object):
|
|
|
96
105
|
self.states: List[State] = [
|
|
97
106
|
self.off_state,
|
|
98
107
|
self.idle_state,
|
|
99
|
-
self.initialize_state,
|
|
100
|
-
self.initiate_state,
|
|
101
108
|
self.monitor_state,
|
|
102
109
|
self.stop_state,
|
|
103
110
|
self.paused_state,
|
|
@@ -113,73 +120,67 @@ class StateMachine(object):
|
|
|
113
120
|
"source": self.off_state,
|
|
114
121
|
"dest": self.idle_state,
|
|
115
122
|
},
|
|
116
|
-
{
|
|
117
|
-
"trigger": "initiated",
|
|
118
|
-
"source": self.initiate_state,
|
|
119
|
-
"dest": self.monitor_state,
|
|
120
|
-
"before": self._initiated,
|
|
121
|
-
},
|
|
122
123
|
{
|
|
123
124
|
"trigger": "pause",
|
|
124
125
|
"source": self.monitor_state,
|
|
125
126
|
"dest": self.paused_state,
|
|
126
|
-
"before": self
|
|
127
|
+
"before": def_transition(self, pause_mission),
|
|
127
128
|
},
|
|
128
129
|
{
|
|
129
130
|
"trigger": "stop",
|
|
130
131
|
"source": [
|
|
131
132
|
self.idle_state,
|
|
132
|
-
self.initiate_state,
|
|
133
133
|
self.monitor_state,
|
|
134
134
|
self.paused_state,
|
|
135
135
|
],
|
|
136
136
|
"dest": self.stop_state,
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
|
-
"trigger": "
|
|
140
|
-
"source": self.monitor_state,
|
|
141
|
-
"dest": self.idle_state,
|
|
142
|
-
"before": self._mission_finished,
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
"trigger": "mission_started",
|
|
139
|
+
"trigger": "request_mission_start",
|
|
146
140
|
"source": self.idle_state,
|
|
147
|
-
"dest": self.
|
|
148
|
-
"
|
|
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
|
+
],
|
|
149
151
|
},
|
|
150
152
|
{
|
|
151
|
-
"trigger": "
|
|
152
|
-
"source": self.
|
|
153
|
-
"dest": self.
|
|
154
|
-
"before": self._initialization_successful,
|
|
153
|
+
"trigger": "request_mission_start",
|
|
154
|
+
"source": self.idle_state,
|
|
155
|
+
"dest": self.idle_state,
|
|
155
156
|
},
|
|
156
157
|
{
|
|
157
|
-
"trigger": "
|
|
158
|
-
"source": self.
|
|
158
|
+
"trigger": "mission_failed_to_start",
|
|
159
|
+
"source": self.monitor_state,
|
|
159
160
|
"dest": self.idle_state,
|
|
160
|
-
"before": self
|
|
161
|
+
"before": def_transition(self, report_failed_mission_and_finalize),
|
|
161
162
|
},
|
|
162
163
|
{
|
|
163
164
|
"trigger": "resume",
|
|
164
165
|
"source": self.paused_state,
|
|
165
166
|
"dest": self.monitor_state,
|
|
166
|
-
"before": self
|
|
167
|
+
"before": def_transition(self, resume_mission),
|
|
167
168
|
},
|
|
168
169
|
{
|
|
169
|
-
"trigger": "
|
|
170
|
-
"source": self.
|
|
170
|
+
"trigger": "mission_finished",
|
|
171
|
+
"source": self.monitor_state,
|
|
171
172
|
"dest": self.idle_state,
|
|
172
|
-
"before": self
|
|
173
|
+
"before": def_transition(self, finish_mission),
|
|
173
174
|
},
|
|
174
175
|
{
|
|
175
176
|
"trigger": "mission_stopped",
|
|
176
177
|
"source": self.stop_state,
|
|
177
178
|
"dest": self.idle_state,
|
|
178
|
-
"before": self
|
|
179
|
+
"before": def_transition(self, stop_mission),
|
|
179
180
|
},
|
|
180
181
|
{
|
|
181
182
|
"trigger": "robot_turned_offline",
|
|
182
|
-
"source": self.idle_state,
|
|
183
|
+
"source": [self.idle_state, self.blocked_protective_stop],
|
|
183
184
|
"dest": self.offline_state,
|
|
184
185
|
},
|
|
185
186
|
{
|
|
@@ -189,7 +190,7 @@ class StateMachine(object):
|
|
|
189
190
|
},
|
|
190
191
|
{
|
|
191
192
|
"trigger": "robot_protective_stop_engaged",
|
|
192
|
-
"source": self.idle_state,
|
|
193
|
+
"source": [self.idle_state, self.offline_state],
|
|
193
194
|
"dest": self.blocked_protective_stop,
|
|
194
195
|
},
|
|
195
196
|
{
|
|
@@ -205,137 +206,14 @@ class StateMachine(object):
|
|
|
205
206
|
|
|
206
207
|
self.current_mission: Optional[Mission] = None
|
|
207
208
|
self.current_task: Optional[TASKS] = None
|
|
208
|
-
|
|
209
|
+
|
|
210
|
+
self.mission_ongoing: bool = False
|
|
209
211
|
|
|
210
212
|
self.current_state: State = States(self.state) # type: ignore
|
|
211
213
|
|
|
212
214
|
self.transitions_log_length: int = transitions_log_length
|
|
213
215
|
self.transitions_list: Deque[States] = deque([], self.transitions_log_length)
|
|
214
216
|
|
|
215
|
-
#################################################################################
|
|
216
|
-
# Transition Callbacks
|
|
217
|
-
def _initialization_successful(self) -> None:
|
|
218
|
-
return
|
|
219
|
-
|
|
220
|
-
def _initialization_failed(self) -> None:
|
|
221
|
-
self.queues.start_mission.output.put(False)
|
|
222
|
-
self._finalize()
|
|
223
|
-
|
|
224
|
-
def _initiated(self) -> None:
|
|
225
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
226
|
-
self.publish_task_status(task=self.current_task)
|
|
227
|
-
self.logger.info(
|
|
228
|
-
f"Successfully initiated "
|
|
229
|
-
f"{type(self.current_task).__name__} "
|
|
230
|
-
f"task: {str(self.current_task.id)[:8]}"
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
def _resume(self) -> None:
|
|
234
|
-
self.logger.info(f"Resuming mission: {self.current_mission.id}")
|
|
235
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
236
|
-
self.current_mission.error_message = None
|
|
237
|
-
self.current_task.status = TaskStatus.InProgress
|
|
238
|
-
|
|
239
|
-
self.publish_mission_status()
|
|
240
|
-
self.publish_task_status(task=self.current_task)
|
|
241
|
-
|
|
242
|
-
resume_mission_response: ControlMissionResponse = (
|
|
243
|
-
self._make_control_mission_response()
|
|
244
|
-
)
|
|
245
|
-
self.queues.resume_mission.output.put(resume_mission_response)
|
|
246
|
-
|
|
247
|
-
self.robot.resume()
|
|
248
|
-
|
|
249
|
-
def _mission_finished(self) -> None:
|
|
250
|
-
fail_statuses: List[TaskStatus] = [
|
|
251
|
-
TaskStatus.Cancelled,
|
|
252
|
-
TaskStatus.Failed,
|
|
253
|
-
]
|
|
254
|
-
partially_fail_statuses = fail_statuses + [TaskStatus.PartiallySuccessful]
|
|
255
|
-
|
|
256
|
-
if len(self.current_mission.tasks) == 0:
|
|
257
|
-
self.current_mission.status = MissionStatus.Successful
|
|
258
|
-
elif all(task.status in fail_statuses for task in self.current_mission.tasks):
|
|
259
|
-
self.current_mission.error_message = ErrorMessage(
|
|
260
|
-
error_reason=None,
|
|
261
|
-
error_description="The mission failed because all tasks in the mission "
|
|
262
|
-
"failed",
|
|
263
|
-
)
|
|
264
|
-
self.current_mission.status = MissionStatus.Failed
|
|
265
|
-
elif any(
|
|
266
|
-
task.status in partially_fail_statuses
|
|
267
|
-
for task in self.current_mission.tasks
|
|
268
|
-
):
|
|
269
|
-
self.current_mission.status = MissionStatus.PartiallySuccessful
|
|
270
|
-
else:
|
|
271
|
-
self.current_mission.status = MissionStatus.Successful
|
|
272
|
-
self._finalize()
|
|
273
|
-
|
|
274
|
-
def _mission_started(self) -> None:
|
|
275
|
-
self.queues.start_mission.output.put(True)
|
|
276
|
-
self.logger.info(
|
|
277
|
-
f"Initialization successful. Starting new mission: "
|
|
278
|
-
f"{self.current_mission.id}"
|
|
279
|
-
)
|
|
280
|
-
self.log_mission_overview(mission=self.current_mission)
|
|
281
|
-
|
|
282
|
-
self.current_mission.status = MissionStatus.InProgress
|
|
283
|
-
self.publish_mission_status()
|
|
284
|
-
self.current_task = self.task_selector.next_task()
|
|
285
|
-
if self.current_task is None:
|
|
286
|
-
self._mission_finished()
|
|
287
|
-
else:
|
|
288
|
-
self.current_task.status = TaskStatus.InProgress
|
|
289
|
-
self.publish_task_status(task=self.current_task)
|
|
290
|
-
|
|
291
|
-
def _full_mission_finished(self) -> None:
|
|
292
|
-
self.current_task = None
|
|
293
|
-
|
|
294
|
-
def _mission_paused(self) -> None:
|
|
295
|
-
self.logger.info(f"Pausing mission: {self.current_mission.id}")
|
|
296
|
-
self.current_mission.status = MissionStatus.Paused
|
|
297
|
-
self.current_task.status = TaskStatus.Paused
|
|
298
|
-
|
|
299
|
-
paused_mission_response: ControlMissionResponse = (
|
|
300
|
-
self._make_control_mission_response()
|
|
301
|
-
)
|
|
302
|
-
self.queues.pause_mission.output.put(paused_mission_response)
|
|
303
|
-
|
|
304
|
-
self.publish_mission_status()
|
|
305
|
-
self.publish_task_status(task=self.current_task)
|
|
306
|
-
|
|
307
|
-
self.robot.pause()
|
|
308
|
-
|
|
309
|
-
def _initiate_failed(self) -> None:
|
|
310
|
-
self.current_task.status = TaskStatus.Failed
|
|
311
|
-
self.current_mission.status = MissionStatus.Failed
|
|
312
|
-
self.publish_task_status(task=self.current_task)
|
|
313
|
-
self._finalize()
|
|
314
|
-
|
|
315
|
-
def _mission_stopped(self) -> None:
|
|
316
|
-
if self.current_mission is None:
|
|
317
|
-
self._queue_empty_response()
|
|
318
|
-
self.reset_state_machine()
|
|
319
|
-
return
|
|
320
|
-
|
|
321
|
-
self.current_mission.status = MissionStatus.Cancelled
|
|
322
|
-
|
|
323
|
-
for task in self.current_mission.tasks:
|
|
324
|
-
if task.status in [
|
|
325
|
-
TaskStatus.NotStarted,
|
|
326
|
-
TaskStatus.InProgress,
|
|
327
|
-
TaskStatus.Paused,
|
|
328
|
-
]:
|
|
329
|
-
task.status = TaskStatus.Cancelled
|
|
330
|
-
|
|
331
|
-
stopped_mission_response: ControlMissionResponse = (
|
|
332
|
-
self._make_control_mission_response()
|
|
333
|
-
)
|
|
334
|
-
self.queues.stop_mission.output.put(stopped_mission_response)
|
|
335
|
-
|
|
336
|
-
self.publish_task_status(task=self.current_task)
|
|
337
|
-
self._finalize()
|
|
338
|
-
|
|
339
217
|
#################################################################################
|
|
340
218
|
|
|
341
219
|
def _finalize(self) -> None:
|
|
@@ -351,11 +229,7 @@ class StateMachine(object):
|
|
|
351
229
|
self.reset_state_machine()
|
|
352
230
|
|
|
353
231
|
def begin(self):
|
|
354
|
-
"""Starts the state machine.
|
|
355
|
-
|
|
356
|
-
Transitions into idle state.
|
|
357
|
-
|
|
358
|
-
"""
|
|
232
|
+
"""Starts the state machine. Transitions into idle state."""
|
|
359
233
|
self.to_idle() # type: ignore
|
|
360
234
|
|
|
361
235
|
def iterate_current_task(self):
|
|
@@ -367,6 +241,7 @@ class StateMachine(object):
|
|
|
367
241
|
except TaskSelectorStop:
|
|
368
242
|
# Indicates that all tasks are finished
|
|
369
243
|
self.current_task = None
|
|
244
|
+
self.send_task_status()
|
|
370
245
|
|
|
371
246
|
def update_state(self):
|
|
372
247
|
"""Updates the current state of the state machine."""
|
|
@@ -379,46 +254,78 @@ class StateMachine(object):
|
|
|
379
254
|
def reset_state_machine(self) -> None:
|
|
380
255
|
self.logger.info("Resetting state machine")
|
|
381
256
|
self.current_task = None
|
|
257
|
+
self.send_task_status()
|
|
382
258
|
self.current_mission = None
|
|
383
|
-
self.initial_pose = None
|
|
384
259
|
|
|
385
|
-
def start_mission(self, mission: Mission
|
|
260
|
+
def start_mission(self, mission: Mission):
|
|
386
261
|
"""Starts a scheduled mission."""
|
|
387
262
|
self.current_mission = mission
|
|
388
|
-
self.initial_pose = initial_pose
|
|
389
263
|
|
|
390
264
|
self.task_selector.initialize(tasks=self.current_mission.tasks)
|
|
391
265
|
|
|
392
|
-
def get_initialize_params(self):
|
|
393
|
-
return InitializeParams(initial_pose=self.initial_pose)
|
|
394
|
-
|
|
395
266
|
def should_start_mission(self) -> Optional[StartMissionMessage]:
|
|
396
267
|
try:
|
|
397
|
-
return self.queues.
|
|
268
|
+
return self.queues.api_start_mission.input.get(block=False)
|
|
398
269
|
except queue.Empty:
|
|
399
270
|
return None
|
|
400
271
|
|
|
401
272
|
def should_stop_mission(self) -> bool:
|
|
402
273
|
try:
|
|
403
|
-
return self.queues.
|
|
274
|
+
return self.queues.api_stop_mission.input.get(block=False)
|
|
404
275
|
except queue.Empty:
|
|
405
276
|
return False
|
|
406
277
|
|
|
407
278
|
def should_pause_mission(self) -> bool:
|
|
408
279
|
try:
|
|
409
|
-
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)
|
|
410
296
|
except queue.Empty:
|
|
411
297
|
return False
|
|
412
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
|
+
|
|
413
311
|
def should_resume_mission(self) -> bool:
|
|
414
312
|
try:
|
|
415
|
-
return self.queues.
|
|
313
|
+
return self.queues.api_resume_mission.input.get(block=False)
|
|
416
314
|
except queue.Empty:
|
|
417
315
|
return False
|
|
418
316
|
|
|
419
|
-
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:
|
|
420
324
|
self.queues.state.update(self.current_state)
|
|
421
325
|
|
|
326
|
+
def send_task_status(self):
|
|
327
|
+
self.queues.state_machine_current_task.update(self.current_task)
|
|
328
|
+
|
|
422
329
|
def publish_mission_status(self) -> None:
|
|
423
330
|
if not self.mqtt_publisher:
|
|
424
331
|
return
|
|
@@ -506,11 +413,11 @@ class StateMachine(object):
|
|
|
506
413
|
else:
|
|
507
414
|
return RobotStatus.Busy
|
|
508
415
|
|
|
509
|
-
def _log_state_transition(self, next_state):
|
|
416
|
+
def _log_state_transition(self, next_state) -> None:
|
|
510
417
|
"""Logs all state transitions that are not self-transitions."""
|
|
511
418
|
self.transitions_list.append(next_state)
|
|
512
419
|
|
|
513
|
-
def log_mission_overview(self, mission: Mission):
|
|
420
|
+
def log_mission_overview(self, mission: Mission) -> None:
|
|
514
421
|
"""Log an overview of the tasks in a mission"""
|
|
515
422
|
log_statements: List[str] = []
|
|
516
423
|
for task in mission.tasks:
|
|
@@ -529,8 +436,8 @@ class StateMachine(object):
|
|
|
529
436
|
task_status=self.current_task.status,
|
|
530
437
|
)
|
|
531
438
|
|
|
532
|
-
def _queue_empty_response(self):
|
|
533
|
-
self.queues.
|
|
439
|
+
def _queue_empty_response(self) -> None:
|
|
440
|
+
self.queues.api_stop_mission.output.put(
|
|
534
441
|
ControlMissionResponse(
|
|
535
442
|
mission_id="None",
|
|
536
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()
|