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
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
 
3
- from azure.identity import ManagedIdentityCredential
4
3
  from azure.monitor.opentelemetry.exporter import (
5
4
  AzureMonitorLogExporter,
6
5
  AzureMonitorTraceExporter,
@@ -57,12 +56,7 @@ def get_azure_monitor_exporters() -> (
57
56
  Else use Azure Managed Identity to create Azure Monitor Exporters.
58
57
  """
59
58
  connection_string = settings.APPLICATIONINSIGHTS_CONNECTION_STRING
60
- if connection_string:
61
- trace_exporter = AzureMonitorTraceExporter(connection_string=connection_string)
62
- log_exporter = AzureMonitorLogExporter(connection_string=connection_string)
63
- else:
64
- credential = ManagedIdentityCredential()
65
- trace_exporter = AzureMonitorTraceExporter(credential=credential)
66
- log_exporter = AzureMonitorLogExporter(credential=credential)
59
+ trace_exporter = AzureMonitorTraceExporter(connection_string=connection_string)
60
+ log_exporter = AzureMonitorLogExporter(connection_string=connection_string)
67
61
 
68
62
  return trace_exporter, log_exporter
@@ -0,0 +1,93 @@
1
+ import logging
2
+ import time
3
+ from copy import deepcopy
4
+ from dataclasses import dataclass
5
+ from threading import Event as ThreadEvent
6
+ from typing import TYPE_CHECKING, Callable, Generic, List, Optional, TypeVar
7
+
8
+ from transitions import State
9
+
10
+ from isar.config.settings import settings
11
+ from isar.models.events import Event
12
+
13
+ T = TypeVar("T")
14
+
15
+
16
+ @dataclass
17
+ class EventHandlerMapping(Generic[T]):
18
+ name: str
19
+ event: Event[T]
20
+ handler: Callable[[Event[T]], Optional[Callable]]
21
+
22
+
23
+ @dataclass
24
+ class TimeoutHandlerMapping:
25
+ name: str
26
+ timeout_in_seconds: float
27
+ handler: Callable[[], Optional[Callable]]
28
+
29
+
30
+ if TYPE_CHECKING:
31
+ from isar.state_machine.state_machine import StateMachine
32
+
33
+
34
+ class EventHandlerBase(State):
35
+ def __init__(
36
+ self,
37
+ state_machine: "StateMachine",
38
+ state_name: str,
39
+ event_handler_mappings: List[EventHandlerMapping],
40
+ timers: List[TimeoutHandlerMapping] = [],
41
+ ) -> None:
42
+
43
+ super().__init__(name=state_name, on_enter=self.start)
44
+ self.state_machine: "StateMachine" = state_machine
45
+ self.logger = logging.getLogger("state_machine")
46
+ self.events = state_machine.events
47
+ self.signal_state_machine_to_stop: ThreadEvent = (
48
+ state_machine.signal_state_machine_to_stop
49
+ )
50
+ self.event_handler_mappings = event_handler_mappings
51
+ self.state_name: str = state_name
52
+ self.timers = timers
53
+
54
+ def start(self) -> None:
55
+ self.state_machine.update_state()
56
+ self._run()
57
+
58
+ def stop(self) -> None:
59
+ return
60
+
61
+ def _run(self) -> None:
62
+ should_exit_state: bool = False
63
+ timers = deepcopy(self.timers)
64
+ entered_time = time.time()
65
+ while True:
66
+ if self.signal_state_machine_to_stop.is_set():
67
+ self.logger.info(
68
+ "Stopping state machine from %s state", self.state_name
69
+ )
70
+ break
71
+
72
+ for timer in timers:
73
+ if time.time() - entered_time > timer.timeout_in_seconds:
74
+ transition_func = timer.handler()
75
+ timers.remove(timer)
76
+ if transition_func is not None:
77
+ transition_func()
78
+ should_exit_state = True
79
+ break
80
+
81
+ if should_exit_state:
82
+ break
83
+
84
+ for handler_mapping in self.event_handler_mappings:
85
+ transition_func = handler_mapping.handler(handler_mapping.event)
86
+ if transition_func is not None:
87
+ transition_func()
88
+ should_exit_state = True
89
+ break
90
+
91
+ if should_exit_state:
92
+ break
93
+ time.sleep(settings.FSM_SLEEP_TIME)
isar/models/events.py ADDED
@@ -0,0 +1,118 @@
1
+ from collections import deque
2
+ from queue import Empty, Queue
3
+ from typing import Generic, Optional, TypeVar
4
+
5
+ from transitions import State
6
+
7
+ from isar.apis.models.models import ControlMissionResponse
8
+ from isar.config.settings import settings
9
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
10
+ from robot_interface.models.mission.mission import Mission
11
+ from robot_interface.models.mission.status import RobotStatus, TaskStatus
12
+ from robot_interface.models.mission.task import TASKS
13
+
14
+ T = TypeVar("T")
15
+ T1 = TypeVar("T1")
16
+ T2 = TypeVar("T2")
17
+
18
+
19
+ class Event(Queue[T]):
20
+ def __init__(self) -> None:
21
+ super().__init__(maxsize=1)
22
+
23
+ def trigger_event(self, data: T) -> None:
24
+ self.put(data)
25
+
26
+ def consume_event(self, timeout: int = None) -> Optional[T]:
27
+ try:
28
+ return self.get(block=timeout is not None, timeout=timeout)
29
+ except Empty:
30
+ if timeout is not None:
31
+ raise EventTimeoutError
32
+ return None
33
+
34
+ def clear_event(self) -> None:
35
+ while True:
36
+ try:
37
+ self.get(block=False)
38
+ except Empty:
39
+ break
40
+
41
+ def has_event(self) -> bool:
42
+ return (
43
+ self.qsize() != 0
44
+ ) # Queue size is not reliable, but should be sufficient for this case
45
+
46
+ def check(self) -> Optional[T]:
47
+ if not self._qsize():
48
+ return None
49
+ with self.mutex:
50
+ queueList = list(self.queue)
51
+ return queueList.pop()
52
+
53
+ def update(self, item: T):
54
+ with self.mutex:
55
+ self.queue: deque[T] = deque()
56
+ self.queue.append(item)
57
+
58
+
59
+ class Events:
60
+ def __init__(self) -> None:
61
+ self.api_requests: APIRequests = APIRequests()
62
+ self.state_machine_events: StateMachineEvents = StateMachineEvents()
63
+ self.robot_service_events: RobotServiceEvents = RobotServiceEvents()
64
+
65
+ self.upload_queue: Queue = Queue(maxsize=10)
66
+
67
+ if settings.MQTT_ENABLED:
68
+ self.mqtt_queue: Queue = Queue()
69
+
70
+
71
+ class APIEvent(Generic[T1, T2]):
72
+ """
73
+ Creates input and output event. The events are defined such that the input is from
74
+ api to state machine while the output is from state machine to api.
75
+ """
76
+
77
+ def __init__(self):
78
+ self.input: Event[T1] = Event()
79
+ self.output: Event[T2] = Event()
80
+
81
+
82
+ class APIRequests:
83
+ def __init__(self) -> None:
84
+ self.start_mission: APIEvent[Mission, bool] = APIEvent()
85
+ self.stop_mission: APIEvent[str, ControlMissionResponse] = APIEvent()
86
+ self.pause_mission: APIEvent[bool, ControlMissionResponse] = APIEvent()
87
+ self.resume_mission: APIEvent[bool, ControlMissionResponse] = APIEvent()
88
+ self.return_home: APIEvent[bool, bool] = APIEvent()
89
+
90
+
91
+ class StateMachineEvents:
92
+ def __init__(self) -> None:
93
+ self.start_mission: Event[Mission] = Event()
94
+ self.stop_mission: Event[bool] = Event()
95
+ self.pause_mission: Event[bool] = Event()
96
+ self.task_status_request: Event[str] = Event()
97
+
98
+
99
+ class RobotServiceEvents:
100
+ def __init__(self) -> None:
101
+ self.task_status_updated: Event[TaskStatus] = Event()
102
+ self.task_status_failed: Event[ErrorMessage] = Event()
103
+ self.mission_started: Event[bool] = Event()
104
+ self.mission_failed: Event[ErrorMessage] = Event()
105
+ self.robot_status_changed: Event[bool] = Event()
106
+ self.mission_failed_to_stop: Event[ErrorMessage] = Event()
107
+ self.mission_successfully_stopped: Event[bool] = Event()
108
+
109
+
110
+ class SharedState:
111
+ def __init__(self) -> None:
112
+ self.state: Event[State] = Event()
113
+ self.robot_status: Event[RobotStatus] = Event()
114
+ self.state_machine_current_task: Event[TASKS] = Event()
115
+
116
+
117
+ class EventTimeoutError(Exception):
118
+ pass
isar/modules.py CHANGED
@@ -12,7 +12,7 @@ from isar.config.settings import settings
12
12
  from isar.mission_planner.local_planner import LocalPlanner
13
13
  from isar.mission_planner.sequential_task_selector import SequentialTaskSelector
14
14
  from isar.mission_planner.task_selector_interface import TaskSelectorInterface
15
- from isar.models.communication.queues.events import Events, SharedState
15
+ from isar.models.events import Events, SharedState
16
16
  from isar.robot.robot import Robot
17
17
  from isar.services.utilities.robot_utilities import RobotUtilities
18
18
  from isar.services.utilities.scheduling_utilities import SchedulingUtilities
isar/robot/robot.py CHANGED
@@ -1,20 +1,20 @@
1
1
  import logging
2
- from queue import Queue
3
- from threading import Event
2
+ from threading import Event as ThreadEvent
4
3
  from typing import Optional
5
4
 
6
- from isar.models.communication.queues.events import (
5
+ from isar.models.events import (
6
+ Event,
7
7
  Events,
8
8
  RobotServiceEvents,
9
9
  SharedState,
10
10
  StateMachineEvents,
11
11
  )
12
- from isar.models.communication.queues.queue_utils import check_for_event, trigger_event
13
12
  from isar.robot.robot_start_mission import RobotStartMissionThread
14
13
  from isar.robot.robot_status import RobotStatusThread
15
14
  from isar.robot.robot_stop_mission import RobotStopMissionThread
16
15
  from isar.robot.robot_task_status import RobotTaskStatusThread
17
16
  from robot_interface.models.exceptions.robot_exceptions import ErrorMessage, ErrorReason
17
+ from robot_interface.models.mission.mission import Mission
18
18
  from robot_interface.robot_interface import RobotInterface
19
19
 
20
20
 
@@ -31,7 +31,7 @@ class Robot(object):
31
31
  self.robot_status_thread: Optional[RobotStatusThread] = None
32
32
  self.robot_task_status_thread: Optional[RobotTaskStatusThread] = None
33
33
  self.stop_mission_thread: Optional[RobotStopMissionThread] = None
34
- self.signal_thread_quitting: Event = Event()
34
+ self.signal_thread_quitting: ThreadEvent = ThreadEvent()
35
35
 
36
36
  def stop(self) -> None:
37
37
  self.signal_thread_quitting.set()
@@ -53,8 +53,8 @@ class Robot(object):
53
53
  self.robot_task_status_thread = None
54
54
  self.start_mission_thread = None
55
55
 
56
- def _check_and_handle_start_mission(self, event: Queue) -> None:
57
- start_mission = check_for_event(event)
56
+ def _start_mission_event_handler(self, event: Event[Mission]) -> None:
57
+ start_mission = event.consume_event()
58
58
  if start_mission is not None:
59
59
  if (
60
60
  self.start_mission_thread is not None
@@ -72,8 +72,8 @@ class Robot(object):
72
72
  )
73
73
  self.start_mission_thread.start()
74
74
 
75
- def _check_and_handle_task_status_request(self, event: Queue[str]) -> None:
76
- task_id: str = check_for_event(event)
75
+ def _task_status_request_handler(self, event: Event[str]) -> None:
76
+ task_id: str = event.consume_event()
77
77
  if task_id:
78
78
  self.robot_task_status_thread = RobotTaskStatusThread(
79
79
  self.robot_service_events,
@@ -83,8 +83,8 @@ class Robot(object):
83
83
  )
84
84
  self.robot_task_status_thread.start()
85
85
 
86
- def _check_and_handle_stop_mission(self, event: Queue) -> None:
87
- if check_for_event(event):
86
+ def _stop_mission_request_handler(self, event: Event[bool]) -> None:
87
+ if event.consume_event():
88
88
  if (
89
89
  self.stop_mission_thread is not None
90
90
  and self.stop_mission_thread.is_alive()
@@ -102,8 +102,8 @@ class Robot(object):
102
102
  error_reason=ErrorReason.RobotStillStartingMissionException,
103
103
  error_description=error_description,
104
104
  )
105
- trigger_event(
106
- self.robot_service_events.mission_failed_to_stop, error_message
105
+ self.robot_service_events.mission_failed_to_stop.trigger_event(
106
+ error_message
107
107
  )
108
108
  return
109
109
  self.stop_mission_thread = RobotStopMissionThread(
@@ -118,14 +118,12 @@ class Robot(object):
118
118
  self.robot_status_thread.start()
119
119
 
120
120
  while not self.signal_thread_quitting.wait(0):
121
- self._check_and_handle_start_mission(
122
- self.state_machine_events.start_mission
123
- )
121
+ self._start_mission_event_handler(self.state_machine_events.start_mission)
124
122
 
125
- self._check_and_handle_task_status_request(
123
+ self._task_status_request_handler(
126
124
  self.state_machine_events.task_status_request
127
125
  )
128
126
 
129
- self._check_and_handle_stop_mission(self.state_machine_events.stop_mission)
127
+ self._stop_mission_request_handler(self.state_machine_events.stop_mission)
130
128
 
131
129
  self.logger.info("Exiting robot service main thread")
@@ -2,11 +2,7 @@ import logging
2
2
  from threading import Event, Thread
3
3
 
4
4
  from isar.config.settings import settings
5
- from isar.models.communication.queues.events import RobotServiceEvents
6
- from isar.models.communication.queues.queue_utils import (
7
- trigger_event,
8
- trigger_event_without_data,
9
- )
5
+ from isar.models.events import RobotServiceEvents
10
6
  from robot_interface.models.exceptions.robot_exceptions import (
11
7
  ErrorMessage,
12
8
  RobotException,
@@ -44,13 +40,13 @@ class RobotStartMissionThread(Thread):
44
40
  self.logger.error(
45
41
  f"Mission is infeasible and cannot be scheduled because: {e.error_description}"
46
42
  )
47
- trigger_event(
48
- self.robot_service_events.mission_failed,
43
+ self.robot_service_events.mission_failed.trigger_event(
49
44
  ErrorMessage(
50
45
  error_reason=e.error_reason,
51
46
  error_description=e.error_description,
52
- ),
47
+ )
53
48
  )
49
+
54
50
  break
55
51
  except RobotException as e:
56
52
  retries += 1
@@ -66,12 +62,11 @@ class RobotStartMissionThread(Thread):
66
62
  f"{e.error_description}"
67
63
  )
68
64
 
69
- trigger_event(
70
- self.robot_service_events.mission_failed,
65
+ self.robot_service_events.mission_failed.trigger_event(
71
66
  ErrorMessage(
72
67
  error_reason=e.error_reason,
73
68
  error_description=e.error_description,
74
- ),
69
+ )
75
70
  )
76
71
  break
77
72
 
@@ -79,12 +74,11 @@ class RobotStartMissionThread(Thread):
79
74
 
80
75
  started_mission = True
81
76
  except RobotInfeasibleMissionException as e:
82
- trigger_event(
83
- self.robot_service_events.mission_failed,
77
+ self.robot_service_events.mission_failed.trigger_event(
84
78
  ErrorMessage(
85
79
  error_reason=e.error_reason, error_description=e.error_description
86
80
  ),
87
81
  )
88
82
 
89
83
  if started_mission:
90
- trigger_event_without_data(self.robot_service_events.mission_started)
84
+ self.robot_service_events.mission_started.trigger_event(True)
@@ -3,8 +3,7 @@ import time
3
3
  from threading import Event, Thread
4
4
 
5
5
  from isar.config.settings import settings
6
- from isar.models.communication.queues.events import SharedState
7
- from isar.models.communication.queues.queue_utils import update_shared_state
6
+ from isar.models.events import SharedState
8
7
  from robot_interface.models.exceptions.robot_exceptions import RobotException
9
8
  from robot_interface.robot_interface import RobotInterface
10
9
 
@@ -49,7 +48,7 @@ class RobotStatusThread(Thread):
49
48
  self.last_robot_status_poll_time = time.time()
50
49
 
51
50
  robot_status = self.robot.robot_status()
52
- update_shared_state(self.shared_state.robot_status, robot_status)
51
+ self.shared_state.robot_status.update(robot_status)
53
52
  except RobotException as e:
54
53
  self.logger.error(f"Failed to retrieve robot status: {e}")
55
54
  continue
@@ -4,11 +4,7 @@ from threading import Event, Thread
4
4
  from typing import Optional
5
5
 
6
6
  from isar.config.settings import settings
7
- from isar.models.communication.queues.events import RobotServiceEvents
8
- from isar.models.communication.queues.queue_utils import (
9
- trigger_event,
10
- trigger_event_without_data,
11
- )
7
+ from isar.models.events import RobotServiceEvents
12
8
  from robot_interface.models.exceptions.robot_exceptions import (
13
9
  ErrorMessage,
14
10
  RobotActionException,
@@ -51,9 +47,7 @@ class RobotStopMissionThread(Thread):
51
47
  time.sleep(settings.FSM_SLEEP_TIME)
52
48
  continue
53
49
 
54
- trigger_event_without_data(
55
- self.robot_service_events.mission_successfully_stopped
56
- )
50
+ self.robot_service_events.mission_successfully_stopped.trigger_event(True)
57
51
  return
58
52
 
59
53
  error_description = (
@@ -68,4 +62,4 @@ class RobotStopMissionThread(Thread):
68
62
  error_description=error_description,
69
63
  )
70
64
 
71
- trigger_event(self.robot_service_events.mission_failed_to_stop, error_message)
65
+ self.robot_service_events.mission_failed_to_stop.trigger_event(error_message)
@@ -4,8 +4,7 @@ from threading import Event, Thread
4
4
  from typing import Optional
5
5
 
6
6
  from isar.config.settings import settings
7
- from isar.models.communication.queues.events import RobotServiceEvents
8
- from isar.models.communication.queues.queue_utils import trigger_event
7
+ from isar.models.events import RobotServiceEvents
9
8
  from isar.services.utilities.threaded_request import ThreadedRequest
10
9
  from robot_interface.models.exceptions.robot_exceptions import (
11
10
  ErrorMessage,
@@ -82,10 +81,7 @@ class RobotTaskStatusThread(Thread):
82
81
  )
83
82
  break
84
83
 
85
- trigger_event(self.robot_service_events.task_status_updated, task_status)
84
+ self.robot_service_events.task_status_updated.trigger_event(task_status)
86
85
  return
87
86
 
88
- trigger_event(
89
- self.robot_service_events.task_status_failed,
90
- failed_task_error,
91
- )
87
+ self.robot_service_events.task_status_failed.trigger_event(failed_task_error)
isar/script.py CHANGED
@@ -10,7 +10,7 @@ from isar.apis.api import API
10
10
  from isar.config.log import setup_loggers
11
11
  from isar.config.open_telemetry import setup_open_telemetry
12
12
  from isar.config.settings import robot_settings, settings
13
- from isar.models.communication.queues.events import Events
13
+ from isar.models.events import Events
14
14
  from isar.modules import ApplicationContainer, get_injector
15
15
  from isar.robot.robot import Robot
16
16
  from isar.services.service_connections.mqtt.mqtt_client import MqttClient
@@ -69,6 +69,7 @@ def print_startup_info():
69
69
  print_setting("Mission planner", settings.MISSION_PLANNER)
70
70
  print_setting("Using local storage", settings.STORAGE_LOCAL_ENABLED)
71
71
  print_setting("Using blob storage", settings.STORAGE_BLOB_ENABLED)
72
+ print_setting("Blob storage account", settings.BLOB_STORAGE_ACCOUNT)
72
73
  print_setting("Using async inspection uploading", settings.UPLOAD_INSPECTIONS_ASYNC)
73
74
  print_setting("Plant code", settings.PLANT_CODE)
74
75
  print_setting("Plant name", settings.PLANT_NAME)
@@ -1,8 +1,7 @@
1
1
  import logging
2
2
  from copy import deepcopy
3
3
  from http import HTTPStatus
4
- from queue import Empty
5
- from typing import Any, List
4
+ from typing import List, TypeVar
6
5
 
7
6
  from fastapi import HTTPException
8
7
  from requests import HTTPError
@@ -14,15 +13,20 @@ from isar.mission_planner.mission_planner_interface import (
14
13
  MissionPlannerError,
15
14
  MissionPlannerInterface,
16
15
  )
17
- from isar.models.communication.message import StartMissionMessage
18
- from isar.models.communication.queues.events import APIRequests, Events, SharedState
19
- from isar.models.communication.queues.queue_io import QueueIO
20
- from isar.models.communication.queues.queue_timeout_error import QueueTimeoutError
21
- from isar.services.utilities.queue_utilities import QueueUtilities
16
+ from isar.models.events import (
17
+ APIEvent,
18
+ APIRequests,
19
+ Events,
20
+ EventTimeoutError,
21
+ SharedState,
22
+ )
22
23
  from isar.state_machine.states_enum import States
23
24
  from robot_interface.models.mission.mission import Mission
24
25
  from robot_interface.models.mission.status import MissionStatus
25
26
 
27
+ T1 = TypeVar("T1")
28
+ T2 = TypeVar("T2")
29
+
26
30
 
27
31
  class SchedulingUtilities:
28
32
  """
@@ -51,9 +55,8 @@ class SchedulingUtilities:
51
55
  HTTPException 500 Internal Server Error
52
56
  If the current state is not available on the queue
53
57
  """
54
- try:
55
- return self.shared_state.state.check()
56
- except Empty:
58
+ current_state = self.shared_state.state.check()
59
+ if current_state is None:
57
60
  error_message: str = (
58
61
  "Internal Server Error - Current state of the state machine is unknown"
59
62
  )
@@ -61,6 +64,7 @@ class SchedulingUtilities:
61
64
  raise HTTPException(
62
65
  status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_message
63
66
  )
67
+ return current_state
64
68
 
65
69
  def get_mission(self, mission_id: str) -> Mission:
66
70
  """Get the mission with mission_id from the current mission planner
@@ -173,10 +177,10 @@ class SchedulingUtilities:
173
177
  """
174
178
  try:
175
179
  self._send_command(
176
- StartMissionMessage(mission=deepcopy(mission)),
180
+ deepcopy(mission),
177
181
  self.api_events.start_mission,
178
182
  )
179
- except QueueTimeoutError:
183
+ except EventTimeoutError:
180
184
  error_message = "Internal Server Error - Failed to start mission in ISAR"
181
185
  self.logger.error(error_message)
182
186
  raise HTTPException(
@@ -199,7 +203,7 @@ class SchedulingUtilities:
199
203
  True,
200
204
  self.api_events.return_home,
201
205
  )
202
- except QueueTimeoutError:
206
+ except EventTimeoutError:
203
207
  error_message = (
204
208
  "Internal Server Error - Failed to start return home mission in ISAR"
205
209
  )
@@ -221,7 +225,7 @@ class SchedulingUtilities:
221
225
  response = self._send_command(True, self.api_events.pause_mission)
222
226
  self.logger.info("OK - Mission successfully paused")
223
227
  return response
224
- except QueueTimeoutError:
228
+ except EventTimeoutError:
225
229
  error_message = "Internal Server Error - Failed to pause mission"
226
230
  self.logger.error(error_message)
227
231
  raise HTTPException(
@@ -240,7 +244,7 @@ class SchedulingUtilities:
240
244
  response = self._send_command(True, self.api_events.resume_mission)
241
245
  self.logger.info("OK - Mission successfully resumed")
242
246
  return response
243
- except QueueTimeoutError:
247
+ except EventTimeoutError:
244
248
  error_message = "Internal Server Error - Failed to resume mission"
245
249
  self.logger.error(error_message)
246
250
  raise HTTPException(
@@ -277,7 +281,7 @@ class SchedulingUtilities:
277
281
  raise HTTPException(
278
282
  status_code=HTTPStatus.CONFLICT, detail=error_message
279
283
  )
280
- except QueueTimeoutError:
284
+ except EventTimeoutError:
281
285
  error_message = "Internal Server Error - Failed to stop mission"
282
286
  self.logger.error(error_message)
283
287
  raise HTTPException(
@@ -286,14 +290,12 @@ class SchedulingUtilities:
286
290
  self.logger.info("OK - Mission successfully stopped")
287
291
  return stop_mission_response
288
292
 
289
- def _send_command(self, input: Any, queueio: QueueIO) -> Any:
290
- queueio.input.put(input)
293
+ def _send_command(self, input: T1, api_event: APIEvent[T1, T2]) -> T2:
294
+ api_event.input.trigger_event(input)
291
295
  try:
292
- return QueueUtilities.check_queue(
293
- queueio.output,
294
- self.queue_timeout,
295
- )
296
- except QueueTimeoutError as e:
297
- QueueUtilities.clear_queue(queueio.input)
296
+ return api_event.output.consume_event(timeout=self.queue_timeout)
297
+ except EventTimeoutError as e:
298
+ self.logger.error("Queue timed out")
299
+ api_event.input.clear_event()
298
300
  self.logger.error("No output received for command to state machine")
299
301
  raise e