isar 1.33.8__py3-none-any.whl → 1.34.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 (49) hide show
  1. isar/apis/models/models.py +2 -5
  2. isar/config/settings.py +1 -8
  3. isar/models/events.py +9 -10
  4. isar/modules.py +5 -11
  5. isar/robot/robot.py +109 -24
  6. isar/robot/robot_monitor_mission.py +399 -0
  7. isar/robot/robot_status.py +0 -10
  8. isar/robot/robot_stop_mission.py +7 -7
  9. isar/robot/robot_upload_inspection.py +80 -0
  10. isar/services/utilities/scheduling_utilities.py +30 -10
  11. isar/state_machine/state_machine.py +7 -223
  12. isar/state_machine/states/await_next_mission.py +7 -6
  13. isar/state_machine/states/going_to_lockdown.py +29 -39
  14. isar/state_machine/states/home.py +11 -15
  15. isar/state_machine/states/intervention_needed.py +7 -6
  16. isar/state_machine/states/lockdown.py +8 -7
  17. isar/state_machine/states/monitor.py +39 -53
  18. isar/state_machine/states/paused.py +19 -17
  19. isar/state_machine/states/pausing.py +12 -27
  20. isar/state_machine/states/pausing_return_home.py +12 -27
  21. isar/state_machine/states/recharging.py +17 -11
  22. isar/state_machine/states/return_home_paused.py +27 -23
  23. isar/state_machine/states/returning_home.py +62 -55
  24. isar/state_machine/states/stopping.py +13 -31
  25. isar/state_machine/states/stopping_go_to_lockdown.py +18 -38
  26. isar/state_machine/states/stopping_return_home.py +26 -26
  27. isar/state_machine/states/unknown_status.py +7 -4
  28. isar/state_machine/transitions/functions/fail_mission.py +1 -9
  29. isar/state_machine/transitions/functions/finish_mission.py +2 -32
  30. isar/state_machine/transitions/functions/pause.py +8 -7
  31. isar/state_machine/transitions/functions/resume.py +3 -12
  32. isar/state_machine/transitions/functions/return_home.py +1 -16
  33. isar/state_machine/transitions/functions/robot_status.py +2 -12
  34. isar/state_machine/transitions/functions/start_mission.py +2 -44
  35. isar/state_machine/transitions/functions/stop.py +4 -33
  36. isar/state_machine/transitions/mission.py +2 -17
  37. isar/state_machine/transitions/return_home.py +3 -24
  38. isar/state_machine/utils/common_event_handlers.py +39 -122
  39. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/METADATA +2 -63
  40. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/RECORD +46 -47
  41. robot_interface/models/mission/task.py +0 -10
  42. robot_interface/robot_interface.py +25 -1
  43. isar/mission_planner/sequential_task_selector.py +0 -23
  44. isar/mission_planner/task_selector_interface.py +0 -31
  45. isar/robot/robot_task_status.py +0 -87
  46. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/WHEEL +0 -0
  47. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/entry_points.txt +0 -0
  48. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/licenses/LICENSE +0 -0
  49. {isar-1.33.8.dist-info → isar-1.34.0.dist-info}/top_level.txt +0 -0
@@ -3,20 +3,14 @@ import logging
3
3
  from collections import deque
4
4
  from datetime import datetime, timezone
5
5
  from threading import Event
6
- from typing import Deque, List, Optional, Tuple
6
+ from typing import Deque, List, Optional
7
7
 
8
8
  from transitions import Machine
9
9
  from transitions.core import State
10
10
 
11
- from isar.apis.models.models import ControlMissionResponse
12
11
  from isar.config.settings import settings
13
- from isar.mission_planner.task_selector_interface import (
14
- TaskSelectorInterface,
15
- TaskSelectorStop,
16
- )
17
12
  from isar.models.events import Events, SharedState
18
13
  from isar.models.status import IsarStatus
19
- from isar.services.service_connections.mqtt.mqtt_client import props_expiry
20
14
  from isar.state_machine.states.await_next_mission import AwaitNextMission
21
15
  from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
22
16
  from isar.state_machine.states.going_to_lockdown import GoingToLockdown
@@ -39,23 +33,13 @@ from isar.state_machine.states_enum import States
39
33
  from isar.state_machine.transitions.mission import get_mission_transitions
40
34
  from isar.state_machine.transitions.return_home import get_return_home_transitions
41
35
  from isar.state_machine.transitions.robot_status import get_robot_status_transitions
42
- from robot_interface.models.exceptions.robot_exceptions import (
43
- ErrorMessage,
44
- RobotException,
45
- RobotRetrieveInspectionException,
46
- )
47
- from robot_interface.models.inspection.inspection import Inspection
48
36
  from robot_interface.models.mission.mission import Mission
49
- from robot_interface.models.mission.status import TaskStatus
50
- from robot_interface.models.mission.task import TASKS, InspectionTask, Task
51
37
  from robot_interface.robot_interface import RobotInterface
52
38
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
53
39
  from robot_interface.telemetry.payloads import (
54
40
  InterventionNeededPayload,
55
41
  IsarStatusPayload,
56
42
  MissionAbortedPayload,
57
- MissionPayload,
58
- TaskPayload,
59
43
  )
60
44
  from robot_interface.utilities.json_service import EnhancedJSONEncoder
61
45
 
@@ -69,7 +53,6 @@ class StateMachine(object):
69
53
  shared_state: SharedState,
70
54
  robot: RobotInterface,
71
55
  mqtt_publisher: MqttClientInterface,
72
- task_selector: TaskSelectorInterface,
73
56
  sleep_time: float = settings.FSM_SLEEP_TIME,
74
57
  stop_robot_attempts_limit: int = settings.STOP_ROBOT_ATTEMPTS_LIMIT,
75
58
  transitions_log_length: int = settings.STATE_TRANSITIONS_LOG_LENGTH,
@@ -98,7 +81,6 @@ class StateMachine(object):
98
81
  self.shared_state: SharedState = shared_state
99
82
  self.robot: RobotInterface = robot
100
83
  self.mqtt_publisher: Optional[MqttClientInterface] = mqtt_publisher
101
- self.task_selector: TaskSelectorInterface = task_selector
102
84
 
103
85
  self.signal_state_machine_to_stop: Event = Event()
104
86
 
@@ -165,23 +147,14 @@ class StateMachine(object):
165
147
  self.stop_robot_attempts_limit: int = stop_robot_attempts_limit
166
148
  self.sleep_time: float = sleep_time
167
149
 
168
- self.current_mission: Optional[Mission] = None
169
- self.current_task: Optional[TASKS] = None
170
-
171
- self.mission_ongoing: bool = False
172
-
173
150
  self.current_state: State = States(self.state) # type: ignore
174
151
 
175
- self.awaiting_task_status: bool = False
176
-
177
152
  self.transitions_log_length: int = transitions_log_length
178
153
  self.transitions_list: Deque[States] = deque([], self.transitions_log_length)
179
154
 
180
155
  #################################################################################
181
156
 
182
- def _finalize(self) -> None:
183
- self.publish_mission_status()
184
- self.log_mission_overview(mission=self.current_mission)
157
+ def print_transitions(self) -> None:
185
158
  state_transitions: str = ", ".join(
186
159
  [
187
160
  f"\n {transition}" if (i + 1) % 10 == 0 else f"{transition}"
@@ -189,7 +162,7 @@ class StateMachine(object):
189
162
  ]
190
163
  )
191
164
  self.logger.info("State transitions:\n %s", state_transitions)
192
- self.reset_state_machine()
165
+ self.transitions = []
193
166
 
194
167
  def begin(self):
195
168
  """Starts the state machine. Transitions into unknown status state."""
@@ -199,20 +172,6 @@ class StateMachine(object):
199
172
  self.logger.info("Stopping state machine")
200
173
  self.signal_state_machine_to_stop.set()
201
174
 
202
- def iterate_current_task(self):
203
- if self.current_task is None:
204
- raise ValueError("No current task is set")
205
-
206
- if self.current_task.is_finished():
207
- try:
208
- self.current_task = self.task_selector.next_task()
209
- self.current_task.status = TaskStatus.InProgress
210
- self.publish_task_status(task=self.current_task)
211
- except TaskSelectorStop:
212
- # Indicates that all tasks are finished
213
- self.current_task = None
214
- self.send_task_status()
215
-
216
175
  def battery_level_is_above_mission_start_threshold(self):
217
176
  if not self.shared_state.robot_battery_level.check():
218
177
  self.logger.warning("Battery level is None")
@@ -226,45 +185,19 @@ class StateMachine(object):
226
185
  """Updates the current state of the state machine."""
227
186
  self.current_state = States(self.state) # type: ignore
228
187
  self.shared_state.state.update(self.current_state)
229
- self._log_state_transition(self.current_state)
188
+ self.transitions_list.append(self.current_state)
230
189
  self.logger.info("State: %s", self.current_state)
231
190
  self.publish_status()
232
191
 
233
- def reset_state_machine(self) -> None:
234
- self.logger.info("Resetting state machine")
235
- self.current_task = None
236
- self.send_task_status()
237
- self.current_mission = None
238
- self.mission_ongoing = False
239
-
240
192
  def start_mission(self, mission: Mission):
241
193
  """Starts a scheduled mission."""
242
- self.current_mission = mission
243
-
244
- self.task_selector.initialize(tasks=self.current_mission.tasks)
245
-
246
- def send_task_status(self):
247
- self.shared_state.state_machine_current_task.update(self.current_task)
248
-
249
- def report_task_status(self, task: Task) -> None:
250
- if task.status == TaskStatus.Failed:
251
- self.logger.warning(
252
- f"Task: {str(task.id)[:8]} was reported as failed by the robot"
253
- )
254
- elif task.status == TaskStatus.Successful:
255
- self.logger.info(
256
- f"{type(task).__name__} task: {str(task.id)[:8]} completed"
257
- )
258
- else:
259
- self.logger.info(
260
- f"Task: {str(task.id)[:8]} was reported as task.status by the robot"
261
- )
194
+ self.events.state_machine_events.start_mission.trigger_event(mission)
262
195
 
263
196
  def publish_mission_aborted(self, reason: str, can_be_continued: bool) -> None:
264
197
  if not self.mqtt_publisher:
265
198
  return
266
199
 
267
- if self.current_mission is None:
200
+ if self.shared_state.mission_id.check() is None:
268
201
  self.logger.warning(
269
202
  "Could not publish mission aborted message. No ongoing mission."
270
203
  )
@@ -273,7 +206,7 @@ class StateMachine(object):
273
206
  payload: MissionAbortedPayload = MissionAbortedPayload(
274
207
  isar_id=settings.ISAR_ID,
275
208
  robot_name=settings.ROBOT_NAME,
276
- mission_id=self.current_mission.id,
209
+ mission_id=self.shared_state.mission_id.check(),
277
210
  reason=reason,
278
211
  can_be_continued=can_be_continued,
279
212
  timestamp=datetime.now(timezone.utc),
@@ -286,68 +219,6 @@ class StateMachine(object):
286
219
  retain=True,
287
220
  )
288
221
 
289
- def publish_mission_status(self) -> None:
290
- if not self.mqtt_publisher:
291
- return
292
-
293
- error_message: Optional[ErrorMessage] = None
294
- if self.current_mission:
295
- if self.current_mission.error_message:
296
- error_message = self.current_mission.error_message
297
-
298
- payload: MissionPayload = MissionPayload(
299
- isar_id=settings.ISAR_ID,
300
- robot_name=settings.ROBOT_NAME,
301
- mission_id=self.current_mission.id if self.current_mission else None,
302
- status=self.current_mission.status if self.current_mission else None,
303
- error_reason=error_message.error_reason if error_message else None,
304
- error_description=(
305
- error_message.error_description if error_message else None
306
- ),
307
- timestamp=datetime.now(timezone.utc),
308
- )
309
-
310
- if self.current_mission:
311
- self.mqtt_publisher.publish(
312
- topic=settings.TOPIC_ISAR_MISSION + f"/{self.current_mission.id}",
313
- payload=json.dumps(payload, cls=EnhancedJSONEncoder),
314
- qos=1,
315
- retain=True,
316
- properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
317
- )
318
-
319
- def publish_task_status(self, task: TASKS) -> None:
320
- """Publishes the task status to the MQTT Broker"""
321
- if not self.mqtt_publisher:
322
- return
323
-
324
- error_message: Optional[ErrorMessage] = None
325
- if task:
326
- if task.error_message:
327
- error_message = task.error_message
328
-
329
- payload: TaskPayload = TaskPayload(
330
- isar_id=settings.ISAR_ID,
331
- robot_name=settings.ROBOT_NAME,
332
- mission_id=self.current_mission.id if self.current_mission else None,
333
- task_id=task.id if task else None,
334
- status=task.status if task else None,
335
- task_type=task.type if task else None,
336
- error_reason=error_message.error_reason if error_message else None,
337
- error_description=(
338
- error_message.error_description if error_message else None
339
- ),
340
- timestamp=datetime.now(timezone.utc),
341
- )
342
-
343
- self.mqtt_publisher.publish(
344
- topic=settings.TOPIC_ISAR_TASK + f"/{task.id}",
345
- payload=json.dumps(payload, cls=EnhancedJSONEncoder),
346
- qos=1,
347
- retain=True,
348
- properties=props_expiry(settings.MQTT_MISSION_AND_TASK_EXPIRY),
349
- )
350
-
351
222
  def publish_intervention_needed(self, error_message: str) -> None:
352
223
  """Publishes the intervention needed message to the MQTT Broker"""
353
224
  if not self.mqtt_publisher:
@@ -411,93 +282,6 @@ class StateMachine(object):
411
282
  else:
412
283
  return IsarStatus.Busy
413
284
 
414
- def _log_state_transition(self, next_state) -> None:
415
- """Logs all state transitions that are not self-transitions."""
416
- self.transitions_list.append(next_state)
417
-
418
- def log_mission_overview(self, mission: Mission) -> None:
419
- """Log an overview of the tasks in a mission"""
420
- log_statements: List[str] = []
421
- for task in mission.tasks:
422
- log_statements.append(
423
- f"{type(task).__name__:<20} {str(task.id)[:8]:<32} -- {task.status}"
424
- )
425
- log_statement: str = "\n".join(log_statements)
426
-
427
- self.logger.info("Mission overview:\n%s", log_statement)
428
-
429
- def _make_control_mission_response(self) -> ControlMissionResponse:
430
- return ControlMissionResponse(
431
- mission_id=self.current_mission.id if self.current_mission else None,
432
- mission_status=(
433
- self.current_mission.status if self.current_mission else None
434
- ),
435
- task_id=self.current_task.id if self.current_task else None,
436
- task_status=self.current_task.status if self.current_task else None,
437
- )
438
-
439
- def _queue_empty_response(self) -> None:
440
- self.events.api_requests.stop_mission.response.trigger_event(
441
- ControlMissionResponse(
442
- mission_id="None",
443
- mission_status="None",
444
- task_id="None",
445
- task_status="None",
446
- )
447
- )
448
-
449
- def should_upload_inspections(self) -> bool:
450
- if settings.UPLOAD_INSPECTIONS_ASYNC:
451
- return False
452
-
453
- return (
454
- self.current_task.is_finished()
455
- and self.current_task.status == TaskStatus.Successful
456
- and isinstance(self.current_task, InspectionTask)
457
- )
458
-
459
- def queue_inspections_for_upload(
460
- self, mission: Mission, current_task: InspectionTask, logger: logging.Logger
461
- ) -> None:
462
- try:
463
- inspection: Inspection = self.robot.get_inspection(task=current_task)
464
- if current_task.inspection_id != inspection.id:
465
- logger.warning(
466
- f"The inspection_id of task ({current_task.inspection_id}) "
467
- f"and result ({inspection.id}) is not matching. "
468
- f"This may lead to confusions when accessing the inspection later"
469
- )
470
-
471
- except (RobotRetrieveInspectionException, RobotException) as e:
472
- error_message: ErrorMessage = ErrorMessage(
473
- error_reason=e.error_reason, error_description=e.error_description
474
- )
475
- self.current_task.error_message = error_message
476
- logger.error(
477
- f"Failed to retrieve inspections because: {e.error_description}"
478
- )
479
- return
480
-
481
- except Exception as e:
482
- logger.error(
483
- f"Failed to retrieve inspections because of unexpected error: {e}"
484
- )
485
- return
486
-
487
- if not inspection:
488
- logger.warning(
489
- f"No inspection result data retrieved for task {str(current_task.id)[:8]}"
490
- )
491
-
492
- inspection.metadata.tag_id = current_task.tag_id
493
-
494
- message: Tuple[Inspection, Mission] = (
495
- inspection,
496
- mission,
497
- )
498
- self.events.upload_queue.put(message)
499
- logger.info(f"Inspection result: {str(inspection.id)[:8]} queued for upload")
500
-
501
285
 
502
286
  def main(state_machine: StateMachine):
503
287
  """Starts a state machine instance."""
@@ -27,12 +27,13 @@ class AwaitNextMission(EventHandlerBase):
27
27
  event: Event[bool],
28
28
  ) -> Optional[Callable]:
29
29
  should_lockdown: bool = event.consume_event()
30
- if should_lockdown:
31
- events.api_requests.send_to_lockdown.response.trigger_event(
32
- LockdownResponse(lockdown_started=True)
33
- )
34
- return state_machine.request_lockdown_mission # type: ignore
35
- return None
30
+ if not should_lockdown:
31
+ return None
32
+
33
+ events.api_requests.send_to_lockdown.response.trigger_event(
34
+ LockdownResponse(lockdown_started=True)
35
+ )
36
+ return state_machine.request_lockdown_mission # type: ignore
36
37
 
37
38
  event_handlers: List[EventHandlerMapping] = [
38
39
  EventHandlerMapping(
@@ -2,13 +2,9 @@ from typing import TYPE_CHECKING, Callable, List, Optional
2
2
 
3
3
  from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
4
  from isar.models.events import Event
5
- from isar.state_machine.utils.common_event_handlers import (
6
- mission_started_event_handler,
7
- task_status_event_handler,
8
- task_status_failed_event_handler,
9
- )
10
- from robot_interface.models.exceptions.robot_exceptions import ErrorMessage, ErrorReason
11
- from robot_interface.models.mission.status import TaskStatus
5
+ from isar.state_machine.utils.common_event_handlers import mission_started_event_handler
6
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
7
+ from robot_interface.models.mission.status import MissionStatus
12
8
 
13
9
  if TYPE_CHECKING:
14
10
  from isar.state_machine.state_machine import StateMachine
@@ -19,30 +15,33 @@ class GoingToLockdown(EventHandlerBase):
19
15
  def __init__(self, state_machine: "StateMachine"):
20
16
  events = state_machine.events
21
17
 
22
- def _handle_task_completed(status: TaskStatus):
23
- if status != TaskStatus.Successful:
24
- state_machine.current_mission.error_message = ErrorMessage(
25
- error_reason=ErrorReason.RobotActionException,
26
- error_description="Lock down mission failed.",
27
- )
28
- return state_machine.lockdown_mission_failed # type: ignore
29
- return state_machine.reached_lockdown # type: ignore
30
-
31
18
  def _mission_failed_event_handler(
32
19
  event: Event[Optional[ErrorMessage]],
33
20
  ) -> Optional[Callable]:
34
21
  mission_failed: Optional[ErrorMessage] = event.consume_event()
35
- if mission_failed is not None:
36
- state_machine.logger.warning(
37
- f"Failed to initiate mission "
38
- f"{str(state_machine.current_mission.id)[:8]} because: "
39
- f"{mission_failed.error_description}"
40
- )
41
- state_machine.current_mission.error_message = ErrorMessage(
42
- error_reason=mission_failed.error_reason,
43
- error_description=mission_failed.error_description,
44
- )
45
- return state_machine.lockdown_mission_failed # type: ignore
22
+ if mission_failed is None:
23
+ return None
24
+
25
+ state_machine.logger.warning(
26
+ f"Failed to go to lockdown because: "
27
+ f"{mission_failed.error_description}"
28
+ )
29
+ return state_machine.lockdown_mission_failed # type: ignore
30
+
31
+ def _mission_status_event_handler(
32
+ event: Event[MissionStatus],
33
+ ) -> Optional[Callable]:
34
+ mission_status: Optional[MissionStatus] = event.consume_event()
35
+
36
+ if mission_status and mission_status not in [
37
+ MissionStatus.InProgress,
38
+ MissionStatus.NotStarted,
39
+ MissionStatus.Paused,
40
+ ]:
41
+ if mission_status != MissionStatus.Successful:
42
+ return state_machine.lockdown_mission_failed # type: ignore
43
+
44
+ return state_machine.reached_lockdown # type: ignore
46
45
  return None
47
46
 
48
47
  event_handlers: List[EventHandlerMapping] = [
@@ -59,18 +58,9 @@ class GoingToLockdown(EventHandlerBase):
59
58
  handler=_mission_failed_event_handler,
60
59
  ),
61
60
  EventHandlerMapping(
62
- name="task_status_failed_event",
63
- event=events.robot_service_events.task_status_failed,
64
- handler=lambda event: task_status_failed_event_handler(
65
- state_machine, _handle_task_completed, event
66
- ),
67
- ),
68
- EventHandlerMapping(
69
- name="task_status_event",
70
- event=events.robot_service_events.task_status_updated,
71
- handler=lambda event: task_status_event_handler(
72
- state_machine, _handle_task_completed, event
73
- ),
61
+ name="mission_status_event",
62
+ event=events.robot_service_events.mission_status_updated,
63
+ handler=_mission_status_event_handler,
74
64
  ),
75
65
  ]
76
66
  super().__init__(
@@ -22,21 +22,21 @@ class Home(EventHandlerBase):
22
22
 
23
23
  def _send_to_lockdown_event_handler(event: Event[bool]):
24
24
  should_send_robot_home: bool = event.consume_event()
25
- if should_send_robot_home:
26
- events.api_requests.send_to_lockdown.response.trigger_event(
27
- LockdownResponse(lockdown_started=True)
28
- )
29
- return state_machine.reached_lockdown # type: ignore
30
- return None
25
+ if not should_send_robot_home:
26
+ return None
27
+
28
+ events.api_requests.send_to_lockdown.response.trigger_event(
29
+ LockdownResponse(lockdown_started=True)
30
+ )
31
+ return state_machine.reached_lockdown # type: ignore
31
32
 
32
33
  def _robot_status_event_handler(
33
- state_machine: "StateMachine",
34
34
  status_changed_event: Event[bool],
35
- status_event: Event[RobotStatus],
36
35
  ) -> Optional[Callable]:
37
- if not status_changed_event.consume_event():
36
+ has_changed = status_changed_event.consume_event()
37
+ if not has_changed:
38
38
  return None
39
- robot_status: Optional[RobotStatus] = status_event.check()
39
+ robot_status: Optional[RobotStatus] = shared_state.robot_status.check()
40
40
  if not (
41
41
  robot_status == RobotStatus.Available
42
42
  or robot_status == RobotStatus.Home
@@ -65,11 +65,7 @@ class Home(EventHandlerBase):
65
65
  EventHandlerMapping(
66
66
  name="robot_status_event",
67
67
  event=events.robot_service_events.robot_status_changed,
68
- handler=lambda event: _robot_status_event_handler(
69
- state_machine=state_machine,
70
- status_changed_event=event,
71
- status_event=shared_state.robot_status,
72
- ),
68
+ handler=_robot_status_event_handler,
73
69
  ),
74
70
  EventHandlerMapping(
75
71
  name="send_to_lockdown_event",
@@ -17,12 +17,13 @@ class InterventionNeeded(EventHandlerBase):
17
17
  def release_intervention_needed_handler(
18
18
  event: Event[bool],
19
19
  ) -> Optional[Callable]:
20
- if event.consume_event():
21
- state_machine.events.api_requests.release_intervention_needed.response.trigger_event(
22
- True
23
- )
24
- return state_machine.release_intervention_needed # type: ignore
25
- return None
20
+ if not event.consume_event():
21
+ return None
22
+
23
+ state_machine.events.api_requests.release_intervention_needed.response.trigger_event(
24
+ True
25
+ )
26
+ return state_machine.release_intervention_needed # type: ignore
26
27
 
27
28
  event_handlers: List[EventHandlerMapping] = [
28
29
  EventHandlerMapping(
@@ -14,13 +14,14 @@ class Lockdown(EventHandlerBase):
14
14
 
15
15
  def _release_from_lockdown_handler(event: Event[bool]):
16
16
  should_release_from_lockdown: bool = event.consume_event()
17
- if should_release_from_lockdown:
18
- events.api_requests.release_from_lockdown.response.trigger_event(True)
19
- if state_machine.battery_level_is_above_mission_start_threshold():
20
- return state_machine.release_from_lockdown # type: ignore
21
- else:
22
- return state_machine.starting_recharging # type: ignore
23
- return None
17
+ if not should_release_from_lockdown:
18
+ return None
19
+
20
+ events.api_requests.release_from_lockdown.response.trigger_event(True)
21
+ if state_machine.battery_level_is_above_mission_start_threshold():
22
+ return state_machine.release_from_lockdown # type: ignore
23
+ else:
24
+ return state_machine.starting_recharging # type: ignore
24
25
 
25
26
  event_handlers: List[EventHandlerMapping] = [
26
27
  EventHandlerMapping(
@@ -1,19 +1,14 @@
1
- import logging
2
- from copy import deepcopy
3
1
  from typing import TYPE_CHECKING, Callable, List, Optional
4
2
 
5
3
  from isar.config.settings import settings
6
4
  from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
7
5
  from isar.models.events import Event
8
- from isar.services.utilities.threaded_request import ThreadedRequest
9
6
  from isar.state_machine.utils.common_event_handlers import (
10
7
  mission_failed_event_handler,
11
8
  mission_started_event_handler,
12
9
  stop_mission_event_handler,
13
- task_status_event_handler,
14
- task_status_failed_event_handler,
15
10
  )
16
- from robot_interface.models.mission.status import TaskStatus
11
+ from robot_interface.models.mission.status import MissionStatus
17
12
 
18
13
  if TYPE_CHECKING:
19
14
  from isar.state_machine.state_machine import StateMachine
@@ -22,56 +17,56 @@ if TYPE_CHECKING:
22
17
  class Monitor(EventHandlerBase):
23
18
 
24
19
  def __init__(self, state_machine: "StateMachine"):
25
- logger = logging.getLogger("state_machine")
26
20
  events = state_machine.events
27
21
  shared_state = state_machine.shared_state
28
22
 
29
23
  def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
30
- if event.consume_event():
31
- return state_machine.pause # type: ignore
32
- return None
33
-
34
- def _handle_task_completed(task_status: TaskStatus):
35
- if state_machine.should_upload_inspections():
36
- get_inspection_thread = ThreadedRequest(
37
- state_machine.queue_inspections_for_upload
38
- )
39
- get_inspection_thread.start_thread(
40
- deepcopy(state_machine.current_mission),
41
- deepcopy(state_machine.current_task),
42
- logger,
43
- name="State Machine Get Inspections",
44
- )
24
+ if not event.consume_event():
25
+ return None
45
26
 
46
- state_machine.iterate_current_task()
47
- if state_machine.current_task is None:
48
- return state_machine.mission_finished # type: ignore
49
- return None
27
+ return state_machine.pause # type: ignore
50
28
 
51
29
  def _robot_battery_level_updated_handler(
52
30
  event: Event[float],
53
31
  ) -> Optional[Callable]:
54
32
  battery_level: float = event.check()
55
- if battery_level < settings.ROBOT_MISSION_BATTERY_START_THRESHOLD:
56
- state_machine.publish_mission_aborted(
57
- "Robot battery too low to continue mission", True
58
- )
59
- state_machine._finalize()
60
- state_machine.logger.warning(
61
- "Cancelling current mission due to low battery"
62
- )
63
- return state_machine.stop # type: ignore
64
- return None
33
+ if battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD:
34
+ return None
35
+
36
+ state_machine.publish_mission_aborted(
37
+ "Robot battery too low to continue mission", True
38
+ )
39
+ state_machine.logger.warning(
40
+ "Cancelling current mission due to low battery"
41
+ )
42
+ return state_machine.stop # type: ignore
65
43
 
66
44
  def _send_to_lockdown_event_handler(
67
45
  event: Event[bool],
68
46
  ) -> Optional[Callable]:
69
47
  should_lockdown: bool = event.consume_event()
70
- if should_lockdown:
71
- state_machine.logger.warning(
72
- "Cancelling current mission due to robot going to lockdown"
73
- )
74
- return state_machine.stop_go_to_lockdown # type: ignore
48
+ if not should_lockdown:
49
+ return None
50
+
51
+ state_machine.logger.warning(
52
+ "Cancelling current mission due to robot going to lockdown"
53
+ )
54
+ return state_machine.stop_go_to_lockdown # type: ignore
55
+
56
+ def _mission_status_event_handler(
57
+ event: Event[MissionStatus],
58
+ ) -> Optional[Callable]:
59
+ mission_status: Optional[MissionStatus] = event.consume_event()
60
+ if mission_status:
61
+ if mission_status not in [
62
+ MissionStatus.InProgress,
63
+ MissionStatus.NotStarted,
64
+ MissionStatus.Paused,
65
+ ]:
66
+ state_machine.logger.info(
67
+ f"Mission completed with status {mission_status}"
68
+ )
69
+ return state_machine.mission_finished # type: ignore
75
70
  return None
76
71
 
77
72
  event_handlers: List[EventHandlerMapping] = [
@@ -100,18 +95,9 @@ class Monitor(EventHandlerBase):
100
95
  ),
101
96
  ),
102
97
  EventHandlerMapping(
103
- name="task_status_failed_event",
104
- event=events.robot_service_events.task_status_failed,
105
- handler=lambda event: task_status_failed_event_handler(
106
- state_machine, _handle_task_completed, event
107
- ),
108
- ),
109
- EventHandlerMapping(
110
- name="task_status_event",
111
- event=events.robot_service_events.task_status_updated,
112
- handler=lambda event: task_status_event_handler(
113
- state_machine, _handle_task_completed, event
114
- ),
98
+ name="mission_status_event",
99
+ event=events.robot_service_events.mission_status_updated,
100
+ handler=_mission_status_event_handler,
115
101
  ),
116
102
  EventHandlerMapping(
117
103
  name="robot_battery_update_event",