isar 1.31.0__py3-none-any.whl → 1.31.1__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 (46) hide show
  1. isar/config/open_telemetry.py +2 -8
  2. isar/eventhandlers/eventhandler.py +93 -0
  3. isar/models/events.py +118 -0
  4. isar/modules.py +1 -1
  5. isar/robot/robot.py +16 -18
  6. isar/robot/robot_start_mission.py +8 -14
  7. isar/robot/robot_status.py +2 -3
  8. isar/robot/robot_stop_mission.py +3 -9
  9. isar/robot/robot_task_status.py +3 -7
  10. isar/script.py +2 -1
  11. isar/services/utilities/scheduling_utilities.py +26 -24
  12. isar/state_machine/state_machine.py +79 -9
  13. isar/state_machine/states/await_next_mission.py +46 -11
  14. isar/state_machine/states/blocked_protective_stop.py +24 -15
  15. isar/state_machine/states/home.py +40 -9
  16. isar/state_machine/states/monitor.py +83 -12
  17. isar/state_machine/states/offline.py +25 -13
  18. isar/state_machine/states/paused.py +24 -38
  19. isar/state_machine/states/returning_home.py +75 -14
  20. isar/state_machine/states/robot_standing_still.py +41 -11
  21. isar/state_machine/states/stopping.py +52 -67
  22. isar/state_machine/states/unknown_status.py +37 -64
  23. isar/state_machine/transitions/functions/robot_status.py +4 -5
  24. isar/state_machine/transitions/functions/stop.py +3 -12
  25. isar/state_machine/utils/common_event_handlers.py +166 -0
  26. isar/storage/uploader.py +1 -1
  27. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/METADATA +1 -1
  28. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/RECORD +32 -43
  29. isar/models/communication/__init__.py +0 -0
  30. isar/models/communication/message.py +0 -8
  31. isar/models/communication/queues/__init__.py +0 -0
  32. isar/models/communication/queues/events.py +0 -58
  33. isar/models/communication/queues/queue_io.py +0 -12
  34. isar/models/communication/queues/queue_timeout_error.py +0 -2
  35. isar/models/communication/queues/queue_utils.py +0 -38
  36. isar/models/communication/queues/status_queue.py +0 -22
  37. isar/models/mission_metadata/__init__.py +0 -0
  38. isar/services/service_connections/stid/__init__.py +0 -0
  39. isar/services/utilities/queue_utilities.py +0 -39
  40. isar/state_machine/generic_states/idle.py +0 -133
  41. isar/state_machine/generic_states/ongoing_mission.py +0 -309
  42. isar/state_machine/generic_states/robot_unavailable.py +0 -61
  43. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/WHEEL +0 -0
  44. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/entry_points.txt +0 -0
  45. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/licenses/LICENSE +0 -0
  46. {isar-1.31.0.dist-info → isar-1.31.1.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ 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
6
+ from typing import Deque, List, Optional, Tuple
7
7
 
8
8
  from transitions import Machine
9
9
  from transitions.core import State
@@ -14,8 +14,7 @@ from isar.mission_planner.task_selector_interface import (
14
14
  TaskSelectorInterface,
15
15
  TaskSelectorStop,
16
16
  )
17
- from isar.models.communication.queues.events import Events, SharedState
18
- from isar.models.communication.queues.queue_utils import update_shared_state
17
+ from isar.models.events import Events, SharedState
19
18
  from isar.state_machine.states.await_next_mission import AwaitNextMission
20
19
  from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
21
20
  from isar.state_machine.states.home import Home
@@ -30,10 +29,15 @@ from isar.state_machine.states_enum import States
30
29
  from isar.state_machine.transitions.mission import get_mission_transitions
31
30
  from isar.state_machine.transitions.return_home import get_return_home_transitions
32
31
  from isar.state_machine.transitions.robot_status import get_robot_status_transitions
33
- from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
32
+ from robot_interface.models.exceptions.robot_exceptions import (
33
+ ErrorMessage,
34
+ RobotException,
35
+ RobotRetrieveInspectionException,
36
+ )
37
+ from robot_interface.models.inspection.inspection import Inspection
34
38
  from robot_interface.models.mission.mission import Mission
35
39
  from robot_interface.models.mission.status import RobotStatus, TaskStatus
36
- from robot_interface.models.mission.task import TASKS
40
+ from robot_interface.models.mission.task import TASKS, InspectionTask, Task
37
41
  from robot_interface.robot_interface import RobotInterface
38
42
  from robot_interface.telemetry.mqtt_client import MqttClientInterface
39
43
  from robot_interface.telemetry.payloads import (
@@ -140,6 +144,8 @@ class StateMachine(object):
140
144
 
141
145
  self.current_state: State = States(self.state) # type: ignore
142
146
 
147
+ self.awaiting_task_status: bool = False
148
+
143
149
  self.transitions_log_length: int = transitions_log_length
144
150
  self.transitions_list: Deque[States] = deque([], self.transitions_log_length)
145
151
 
@@ -182,7 +188,7 @@ class StateMachine(object):
182
188
  def update_state(self):
183
189
  """Updates the current state of the state machine."""
184
190
  self.current_state = States(self.state) # type: ignore
185
- update_shared_state(self.shared_state.state, self.current_state)
191
+ self.shared_state.state.update(self.current_state)
186
192
  self._log_state_transition(self.current_state)
187
193
  self.logger.info("State: %s", self.current_state)
188
194
  self.publish_status()
@@ -201,9 +207,21 @@ class StateMachine(object):
201
207
  self.task_selector.initialize(tasks=self.current_mission.tasks)
202
208
 
203
209
  def send_task_status(self):
204
- update_shared_state(
205
- self.shared_state.state_machine_current_task, self.current_task
206
- )
210
+ self.shared_state.state_machine_current_task.update(self.current_task)
211
+
212
+ def report_task_status(self, task: Task) -> None:
213
+ if task.status == TaskStatus.Failed:
214
+ self.logger.warning(
215
+ f"Task: {str(task.id)[:8]} was reported as failed by the robot"
216
+ )
217
+ elif task.status == TaskStatus.Successful:
218
+ self.logger.info(
219
+ f"{type(task).__name__} task: {str(task.id)[:8]} completed"
220
+ )
221
+ else:
222
+ self.logger.info(
223
+ f"Task: {str(task.id)[:8]} was reported as task.status by the robot"
224
+ )
207
225
 
208
226
  def publish_mission_status(self) -> None:
209
227
  if not self.mqtt_publisher:
@@ -334,6 +352,58 @@ class StateMachine(object):
334
352
  )
335
353
  )
336
354
 
355
+ def should_upload_inspections(self) -> bool:
356
+ if settings.UPLOAD_INSPECTIONS_ASYNC:
357
+ return False
358
+
359
+ return (
360
+ self.current_task.is_finished()
361
+ and self.current_task.status == TaskStatus.Successful
362
+ and isinstance(self.current_task, InspectionTask)
363
+ )
364
+
365
+ def queue_inspections_for_upload(
366
+ self, mission: Mission, current_task: InspectionTask, logger: logging.Logger
367
+ ) -> None:
368
+ try:
369
+ inspection: Inspection = self.robot.get_inspection(task=current_task)
370
+ if current_task.inspection_id != inspection.id:
371
+ logger.warning(
372
+ f"The inspection_id of task ({current_task.inspection_id}) "
373
+ f"and result ({inspection.id}) is not matching. "
374
+ f"This may lead to confusions when accessing the inspection later"
375
+ )
376
+
377
+ except (RobotRetrieveInspectionException, RobotException) as e:
378
+ error_message: ErrorMessage = ErrorMessage(
379
+ error_reason=e.error_reason, error_description=e.error_description
380
+ )
381
+ self.current_task.error_message = error_message
382
+ logger.error(
383
+ f"Failed to retrieve inspections because: {e.error_description}"
384
+ )
385
+ return
386
+
387
+ except Exception as e:
388
+ logger.error(
389
+ f"Failed to retrieve inspections because of unexpected error: {e}"
390
+ )
391
+ return
392
+
393
+ if not inspection:
394
+ logger.warning(
395
+ f"No inspection result data retrieved for task {str(current_task.id)[:8]}"
396
+ )
397
+
398
+ inspection.metadata.tag_id = current_task.tag_id
399
+
400
+ message: Tuple[Inspection, Mission] = (
401
+ inspection,
402
+ mission,
403
+ )
404
+ self.events.upload_queue.put(message)
405
+ logger.info(f"Inspection result: {str(inspection.id)[:8]} queued for upload")
406
+
337
407
 
338
408
  def main(state_machine: StateMachine):
339
409
  """Starts a state machine instance."""
@@ -1,20 +1,55 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, List
2
2
 
3
- from transitions import State
3
+ from isar.config.settings import settings
4
+ from isar.eventhandlers.eventhandler import (
5
+ EventHandlerBase,
6
+ EventHandlerMapping,
7
+ TimeoutHandlerMapping,
8
+ )
9
+ from isar.state_machine.utils.common_event_handlers import (
10
+ return_home_event_handler,
11
+ start_mission_event_handler,
12
+ stop_mission_event_handler,
13
+ )
4
14
 
5
15
  if TYPE_CHECKING:
6
16
  from isar.state_machine.state_machine import StateMachine
7
17
 
8
- from isar.state_machine.generic_states.idle import Idle, IdleStates
9
18
 
19
+ class AwaitNextMission(EventHandlerBase):
10
20
 
11
- class AwaitNextMission(State, Idle):
12
- def __init__(self, state_machine: "StateMachine") -> None:
13
- State.__init__(
14
- self, name="await_next_mission", on_enter=self.start, on_exit=self.stop
15
- )
16
- Idle.__init__(
17
- self,
21
+ def __init__(self, state_machine: "StateMachine"):
22
+ events = state_machine.events
23
+
24
+ event_handlers: List[EventHandlerMapping] = [
25
+ EventHandlerMapping(
26
+ name="start_mission_event",
27
+ event=events.api_requests.start_mission.input,
28
+ handler=lambda event: start_mission_event_handler(state_machine, event),
29
+ ),
30
+ EventHandlerMapping(
31
+ name="return_home_event",
32
+ event=events.api_requests.return_home.input,
33
+ handler=lambda event: return_home_event_handler(state_machine, event),
34
+ ),
35
+ EventHandlerMapping(
36
+ name="stop_mission_event",
37
+ event=events.api_requests.return_home.input,
38
+ handler=lambda event: stop_mission_event_handler(state_machine, event),
39
+ ),
40
+ ]
41
+
42
+ timers: List[TimeoutHandlerMapping] = [
43
+ TimeoutHandlerMapping(
44
+ name="should_return_home_timer",
45
+ timeout_in_seconds=settings.RETURN_HOME_DELAY,
46
+ handler=lambda: state_machine.request_return_home, # type: ignore
47
+ )
48
+ ]
49
+
50
+ super().__init__(
51
+ state_name="await_next_mission",
18
52
  state_machine=state_machine,
19
- state=IdleStates.AwaitNextMission,
53
+ event_handler_mappings=event_handlers,
54
+ timers=timers,
20
55
  )
@@ -1,24 +1,33 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, List
2
2
 
3
- from transitions import State
4
-
5
- from isar.state_machine.generic_states.robot_unavailable import (
6
- RobotUnavailable,
7
- RobotUnavailableStates,
8
- )
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
+ from isar.models.events import Event
5
+ from robot_interface.models.mission.status import RobotStatus
9
6
 
10
7
  if TYPE_CHECKING:
11
8
  from isar.state_machine.state_machine import StateMachine
12
9
 
13
10
 
14
- class BlockedProtectiveStop(State, RobotUnavailable):
15
- def __init__(self, state_machine: "StateMachine") -> None:
16
- State.__init__(
17
- self, name="blocked_protective_stop", on_enter=self.start, on_exit=self.stop
18
- )
11
+ class BlockedProtectiveStop(EventHandlerBase):
12
+
13
+ def __init__(self, state_machine: "StateMachine"):
14
+ shared_state = state_machine.shared_state
15
+
16
+ def _robot_status_event_handler(event: Event[RobotStatus]):
17
+ robot_status: RobotStatus = event.check()
18
+ if robot_status != RobotStatus.BlockedProtectiveStop:
19
+ return state_machine.robot_status_changed # type: ignore
20
+ return None
19
21
 
20
- RobotUnavailable.__init__(
21
- self,
22
+ event_handlers: List[EventHandlerMapping] = [
23
+ EventHandlerMapping(
24
+ name="robot_status_event",
25
+ event=shared_state.robot_status,
26
+ handler=_robot_status_event_handler,
27
+ ),
28
+ ]
29
+ super().__init__(
30
+ state_name="blocked_protective_stop",
22
31
  state_machine=state_machine,
23
- state=RobotUnavailableStates.BlockedProtectiveStop,
32
+ event_handler_mappings=event_handlers,
24
33
  )
@@ -1,19 +1,50 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, List
2
2
 
3
- from transitions import State
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
+ from isar.state_machine.utils.common_event_handlers import (
5
+ return_home_event_handler,
6
+ robot_status_event_handler,
7
+ start_mission_event_handler,
8
+ stop_mission_event_handler,
9
+ )
10
+ from robot_interface.models.mission.status import RobotStatus
4
11
 
5
12
  if TYPE_CHECKING:
6
13
  from isar.state_machine.state_machine import StateMachine
7
14
 
8
- from isar.state_machine.generic_states.idle import Idle, IdleStates
9
15
 
16
+ class Home(EventHandlerBase):
10
17
 
11
- class Home(State, Idle):
12
- def __init__(self, state_machine: "StateMachine") -> None:
13
- State.__init__(self, name="home", on_enter=self.start, on_exit=self.stop)
18
+ def __init__(self, state_machine: "StateMachine"):
19
+ events = state_machine.events
20
+ shared_state = state_machine.shared_state
14
21
 
15
- Idle.__init__(
16
- self,
22
+ event_handlers: List[EventHandlerMapping] = [
23
+ EventHandlerMapping(
24
+ name="start_mission_event",
25
+ event=events.api_requests.start_mission.input,
26
+ handler=lambda event: start_mission_event_handler(state_machine, event),
27
+ ),
28
+ EventHandlerMapping(
29
+ name="return_home_event",
30
+ event=events.api_requests.return_home.input,
31
+ handler=lambda event: return_home_event_handler(state_machine, event),
32
+ ),
33
+ EventHandlerMapping(
34
+ name="stop_mission_event",
35
+ event=events.api_requests.return_home.input,
36
+ handler=lambda event: stop_mission_event_handler(state_machine, event),
37
+ ),
38
+ EventHandlerMapping(
39
+ name="robot_status_event",
40
+ event=shared_state.robot_status,
41
+ handler=lambda event: robot_status_event_handler(
42
+ state_machine, RobotStatus.Home, event
43
+ ),
44
+ ),
45
+ ]
46
+ super().__init__(
47
+ state_name="home",
17
48
  state_machine=state_machine,
18
- state=IdleStates.Home,
49
+ event_handler_mappings=event_handlers,
19
50
  )
@@ -1,22 +1,93 @@
1
- from typing import TYPE_CHECKING
1
+ import logging
2
+ from copy import deepcopy
3
+ from typing import TYPE_CHECKING, Callable, List, Optional
2
4
 
3
- from transitions import State
4
-
5
- from isar.state_machine.generic_states.ongoing_mission import (
6
- OngoingMission,
7
- OngoingMissionStates,
5
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
6
+ from isar.models.events import Event
7
+ from isar.services.utilities.threaded_request import ThreadedRequest
8
+ from isar.state_machine.utils.common_event_handlers import (
9
+ mission_failed_event_handler,
10
+ mission_started_event_handler,
11
+ stop_mission_event_handler,
12
+ task_status_event_handler,
13
+ task_status_failed_event_handler,
8
14
  )
15
+ from robot_interface.models.mission.status import TaskStatus
9
16
 
10
17
  if TYPE_CHECKING:
11
18
  from isar.state_machine.state_machine import StateMachine
12
19
 
13
20
 
14
- class Monitor(State, OngoingMission):
15
- def __init__(self, state_machine: "StateMachine") -> None:
16
- State.__init__(self, name="monitor", on_enter=self.start, on_exit=self.stop)
21
+ class Monitor(EventHandlerBase):
22
+
23
+ def __init__(self, state_machine: "StateMachine"):
24
+ logger = logging.getLogger("state_machine")
25
+ events = state_machine.events
26
+
27
+ def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
28
+ if event.consume_event():
29
+ return state_machine.pause # type: ignore
30
+ return None
31
+
32
+ def _handle_task_completed(task_status: TaskStatus):
33
+ if state_machine.should_upload_inspections():
34
+ get_inspection_thread = ThreadedRequest(
35
+ state_machine.queue_inspections_for_upload
36
+ )
37
+ get_inspection_thread.start_thread(
38
+ deepcopy(state_machine.current_mission),
39
+ deepcopy(state_machine.current_task),
40
+ logger,
41
+ name="State Machine Get Inspections",
42
+ )
43
+
44
+ state_machine.iterate_current_task()
45
+ if state_machine.current_task is None:
46
+ return state_machine.mission_finished # type: ignore
47
+ return None
17
48
 
18
- OngoingMission.__init__(
19
- self,
49
+ event_handlers: List[EventHandlerMapping] = [
50
+ EventHandlerMapping(
51
+ name="stop_mission_event",
52
+ event=events.api_requests.stop_mission.input,
53
+ handler=lambda event: stop_mission_event_handler(state_machine, event),
54
+ ),
55
+ EventHandlerMapping(
56
+ name="pause_mission_event",
57
+ event=events.api_requests.pause_mission.input,
58
+ handler=_pause_mission_event_handler,
59
+ ),
60
+ EventHandlerMapping(
61
+ name="mission_started_event",
62
+ event=events.robot_service_events.mission_started,
63
+ handler=lambda event: mission_started_event_handler(
64
+ state_machine, event
65
+ ),
66
+ ),
67
+ EventHandlerMapping(
68
+ name="mission_failed_event",
69
+ event=events.robot_service_events.mission_failed,
70
+ handler=lambda event: mission_failed_event_handler(
71
+ state_machine, event
72
+ ),
73
+ ),
74
+ EventHandlerMapping(
75
+ name="task_status_failed_event",
76
+ event=events.robot_service_events.task_status_failed,
77
+ handler=lambda event: task_status_failed_event_handler(
78
+ state_machine, _handle_task_completed, event
79
+ ),
80
+ ),
81
+ EventHandlerMapping(
82
+ name="task_status_event",
83
+ event=events.robot_service_events.task_status_updated,
84
+ handler=lambda event: task_status_event_handler(
85
+ state_machine, _handle_task_completed, event
86
+ ),
87
+ ),
88
+ ]
89
+ super().__init__(
90
+ state_name="monitor",
20
91
  state_machine=state_machine,
21
- state=OngoingMissionStates.Monitor,
92
+ event_handler_mappings=event_handlers,
22
93
  )
@@ -1,22 +1,34 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, List
2
2
 
3
- from transitions import State
4
-
5
- from isar.state_machine.generic_states.robot_unavailable import (
6
- RobotUnavailable,
7
- RobotUnavailableStates,
8
- )
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
+ from isar.models.events import Event
5
+ from robot_interface.models.mission.status import RobotStatus
9
6
 
10
7
  if TYPE_CHECKING:
11
8
  from isar.state_machine.state_machine import StateMachine
12
9
 
13
10
 
14
- class Offline(State, RobotUnavailable):
15
- def __init__(self, state_machine: "StateMachine") -> None:
16
- State.__init__(self, name="offline", on_enter=self.start, on_exit=self.stop)
11
+ class Offline(EventHandlerBase):
12
+
13
+ def __init__(self, state_machine: "StateMachine"):
14
+
15
+ shared_state = state_machine.shared_state
16
+
17
+ def _robot_status_event_handler(event: Event[RobotStatus]):
18
+ robot_status: RobotStatus = event.check()
19
+ if robot_status != RobotStatus.Offline:
20
+ return state_machine.robot_status_changed # type: ignore
21
+ return None
17
22
 
18
- RobotUnavailable.__init__(
19
- self,
23
+ event_handlers: List[EventHandlerMapping] = [
24
+ EventHandlerMapping(
25
+ name="robot_status_event",
26
+ event=shared_state.robot_status,
27
+ handler=_robot_status_event_handler,
28
+ ),
29
+ ]
30
+ super().__init__(
31
+ state_name="offline",
20
32
  state_machine=state_machine,
21
- state=RobotUnavailableStates.Offline,
33
+ event_handler_mappings=event_handlers,
22
34
  )
@@ -1,44 +1,30 @@
1
- import logging
2
- import time
3
- from typing import TYPE_CHECKING, Callable
1
+ from typing import TYPE_CHECKING, List
4
2
 
5
- from transitions import State
6
-
7
- from isar.models.communication.queues.queue_utils import check_for_event
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
8
4
 
9
5
  if TYPE_CHECKING:
10
6
  from isar.state_machine.state_machine import StateMachine
11
7
 
12
8
 
13
- class Paused(State):
14
- def __init__(self, state_machine: "StateMachine") -> None:
15
- super().__init__(name="paused", on_enter=self.start)
16
- self.state_machine: "StateMachine" = state_machine
17
- self.logger = logging.getLogger("state_machine")
18
- self.events = self.state_machine.events
19
- self.signal_state_machine_to_stop = state_machine.signal_state_machine_to_stop
20
-
21
- def start(self) -> None:
22
- self.state_machine.update_state()
23
- self._run()
24
-
25
- def _run(self) -> None:
26
- transition: Callable
27
- while True:
28
- if self.signal_state_machine_to_stop.is_set():
29
- self.logger.info(
30
- "Stopping state machine from %s state", self.__class__.__name__
31
- )
32
- break
33
-
34
- if check_for_event(self.events.api_requests.stop_mission.input):
35
- transition = self.state_machine.stop # type: ignore
36
- break
37
-
38
- if check_for_event(self.events.api_requests.resume_mission.input):
39
- transition = self.state_machine.resume # type: ignore
40
- break
41
-
42
- time.sleep(self.state_machine.sleep_time)
43
-
44
- transition()
9
+ class Paused(EventHandlerBase):
10
+
11
+ def __init__(self, state_machine: "StateMachine"):
12
+ events = state_machine.events
13
+
14
+ event_handlers: List[EventHandlerMapping] = [
15
+ EventHandlerMapping(
16
+ name="stop_mission_event",
17
+ event=events.api_requests.stop_mission.input,
18
+ handler=lambda event: state_machine.stop if event.consume_event() else None, # type: ignore
19
+ ),
20
+ EventHandlerMapping(
21
+ name="resume_mission_event",
22
+ event=events.api_requests.resume_mission.input,
23
+ handler=lambda event: state_machine.resume if event.consume_event() else None, # type: ignore
24
+ ),
25
+ ]
26
+ super().__init__(
27
+ state_name="paused",
28
+ state_machine=state_machine,
29
+ event_handler_mappings=event_handlers,
30
+ )
@@ -1,24 +1,85 @@
1
- from typing import TYPE_CHECKING
1
+ from typing import TYPE_CHECKING, Callable, List, Optional
2
2
 
3
- from transitions import State
4
-
5
- from isar.state_machine.generic_states.ongoing_mission import (
6
- OngoingMission,
7
- OngoingMissionStates,
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
+ from isar.models.events import Event
5
+ from isar.state_machine.utils.common_event_handlers import (
6
+ mission_failed_event_handler,
7
+ mission_started_event_handler,
8
+ stop_mission_event_handler,
9
+ task_status_event_handler,
10
+ task_status_failed_event_handler,
8
11
  )
12
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage, ErrorReason
13
+ from robot_interface.models.mission.mission import Mission
14
+ from robot_interface.models.mission.status import TaskStatus
9
15
 
10
16
  if TYPE_CHECKING:
11
17
  from isar.state_machine.state_machine import StateMachine
12
18
 
13
19
 
14
- class ReturningHome(State, OngoingMission):
15
- def __init__(self, state_machine: "StateMachine") -> None:
16
- State.__init__(
17
- self, name="returning_home", on_enter=self.start, on_exit=self.stop
18
- )
20
+ class ReturningHome(EventHandlerBase):
21
+
22
+ def __init__(self, state_machine: "StateMachine"):
23
+ events = state_machine.events
24
+
25
+ def _handle_task_completed(status: TaskStatus):
26
+ if status != TaskStatus.Successful:
27
+ state_machine.current_mission.error_message = ErrorMessage(
28
+ error_reason=ErrorReason.RobotActionException,
29
+ error_description="Return home failed.",
30
+ )
31
+ return state_machine.return_home_failed # type: ignore
32
+ return state_machine.returned_home # type: ignore
33
+
34
+ def _start_mission_event_handler(
35
+ event: Event[Mission],
36
+ ) -> Optional[Callable]:
37
+ if event.has_event():
38
+ return state_machine.stop # type: ignore
39
+ return None
19
40
 
20
- OngoingMission.__init__(
21
- self,
41
+ event_handlers: List[EventHandlerMapping] = [
42
+ EventHandlerMapping(
43
+ name="stop_mission_event",
44
+ event=events.api_requests.stop_mission.input,
45
+ handler=lambda event: stop_mission_event_handler(state_machine, event),
46
+ ),
47
+ EventHandlerMapping(
48
+ name="mission_started_event",
49
+ event=events.robot_service_events.mission_started,
50
+ handler=lambda event: mission_started_event_handler(
51
+ state_machine, event
52
+ ),
53
+ ),
54
+ EventHandlerMapping(
55
+ name="mission_failed_event",
56
+ event=events.robot_service_events.mission_failed,
57
+ handler=lambda event: mission_failed_event_handler(
58
+ state_machine, event
59
+ ),
60
+ ),
61
+ EventHandlerMapping(
62
+ name="start_mission_event",
63
+ event=events.api_requests.start_mission.input,
64
+ handler=_start_mission_event_handler,
65
+ ),
66
+ EventHandlerMapping(
67
+ name="task_status_failed_event",
68
+ event=events.robot_service_events.task_status_failed,
69
+ handler=lambda event: task_status_failed_event_handler(
70
+ state_machine, _handle_task_completed, event
71
+ ),
72
+ ),
73
+ EventHandlerMapping(
74
+ name="task_status_event",
75
+ event=events.robot_service_events.task_status_updated,
76
+ handler=lambda event: task_status_event_handler(
77
+ state_machine, _handle_task_completed, event
78
+ ),
79
+ ),
80
+ ]
81
+ super().__init__(
82
+ state_name="returning_home",
22
83
  state_machine=state_machine,
23
- state=OngoingMissionStates.ReturningHome,
84
+ event_handler_mappings=event_handlers,
24
85
  )