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.

Files changed (42) hide show
  1. isar/apis/models/start_mission_definition.py +55 -108
  2. isar/apis/robot_control/robot_controller.py +5 -4
  3. isar/apis/schedule/scheduling_controller.py +4 -32
  4. isar/apis/security/authentication.py +2 -2
  5. isar/config/settings.env +1 -3
  6. isar/config/settings.py +5 -5
  7. isar/models/communication/message.py +0 -4
  8. isar/models/communication/queues/queue_utils.py +27 -0
  9. isar/models/communication/queues/queues.py +23 -5
  10. isar/robot/robot.py +91 -0
  11. isar/robot/robot_start_mission.py +73 -0
  12. isar/robot/robot_status.py +46 -0
  13. isar/robot/robot_task_status.py +92 -0
  14. isar/script.py +7 -0
  15. isar/services/utilities/scheduling_utilities.py +15 -26
  16. isar/state_machine/state_machine.py +94 -183
  17. isar/state_machine/states/blocked_protective_stop.py +7 -19
  18. isar/state_machine/states/idle.py +12 -54
  19. isar/state_machine/states/monitor.py +43 -90
  20. isar/state_machine/states/offline.py +8 -33
  21. isar/state_machine/states/paused.py +1 -1
  22. isar/state_machine/states_enum.py +0 -2
  23. isar/state_machine/transitions/fail_mission.py +13 -0
  24. isar/state_machine/transitions/finish_mission.py +39 -0
  25. isar/state_machine/transitions/pause.py +24 -0
  26. isar/state_machine/transitions/resume.py +27 -0
  27. isar/state_machine/transitions/start_mission.py +73 -0
  28. isar/state_machine/transitions/stop.py +33 -0
  29. isar/state_machine/transitions/utils.py +10 -0
  30. isar/storage/slimm_storage.py +2 -2
  31. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/METADATA +2 -3
  32. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/RECORD +40 -30
  33. robot_interface/models/mission/task.py +1 -1
  34. robot_interface/telemetry/mqtt_client.py +0 -1
  35. robot_interface/telemetry/payloads.py +3 -3
  36. robot_interface/utilities/json_service.py +1 -1
  37. isar/state_machine/states/initialize.py +0 -70
  38. isar/state_machine/states/initiate.py +0 -111
  39. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/LICENSE +0 -0
  40. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/WHEEL +0 -0
  41. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/entry_points.txt +0 -0
  42. {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 MissionStatus, RobotStatus, TaskStatus
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._mission_paused,
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": "mission_finished",
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.initialize_state,
147
- "before": self._mission_started,
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": "initialization_successful",
151
- "source": self.initialize_state,
152
- "dest": self.initiate_state,
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": "initialization_failed",
157
- "source": self.initialize_state,
158
+ "trigger": "mission_failed_to_start",
159
+ "source": self.monitor_state,
158
160
  "dest": self.idle_state,
159
- "before": self._initialization_failed,
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._resume,
167
+ "before": def_transition(self, resume_mission),
166
168
  },
167
169
  {
168
- "trigger": "initiate_failed",
169
- "source": self.initiate_state,
170
+ "trigger": "mission_finished",
171
+ "source": self.monitor_state,
170
172
  "dest": self.idle_state,
171
- "before": self._initiate_failed,
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._mission_stopped,
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
- self.initial_pose: Optional[Pose] = None
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, initial_pose: Pose):
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.start_mission.input.get(block=False)
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.stop_mission.input.get(block=False)
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.pause_mission.input.get(block=False)
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.resume_mission.input.get(block=False)
313
+ return self.queues.api_resume_mission.input.get(block=False)
412
314
  except queue.Empty:
413
315
  return False
414
316
 
415
- def send_state_status(self):
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.stop_mission.output.put(
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.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
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
- try:
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
- except RobotException as e:
54
- self.logger.error(
55
- f"Failed to get robot status because: {e.error_description}"
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.robot_status_thread = None
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
- if self.robot_status_thread:
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
- if self.status_checked_at_least_once:
57
- start_mission: Optional[StartMissionMessage] = (
58
- self.state_machine.should_start_mission()
59
- )
60
- if start_mission:
61
- self.state_machine.start_mission(
62
- mission=start_mission.mission,
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.robot_status_thread = None
102
-
60
+ time.sleep(self.state_machine.sleep_time)
103
61
  transition()