isar 1.34.2__py3-none-any.whl → 1.34.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of isar might be problematic. Click here for more details.

isar/models/status.py CHANGED
@@ -14,3 +14,4 @@ class IsarStatus(Enum):
14
14
  Recharging = "recharging"
15
15
  Lockdown = "lockdown"
16
16
  GoingToLockdown = "goingtolockdown"
17
+ GoingToRecharging = "goingtorecharging"
isar/robot/robot.py CHANGED
@@ -70,6 +70,10 @@ class Robot(object):
70
70
  self.start_mission_thread.join()
71
71
  if self.stop_mission_thread is not None and self.stop_mission_thread.is_alive():
72
72
  self.stop_mission_thread.join()
73
+ for thread in self.upload_inspection_threads:
74
+ if thread.is_alive():
75
+ thread.join()
76
+ self.upload_inspection_threads = []
73
77
  self.robot_status_thread = None
74
78
  self.robot_battery_thread = None
75
79
  self.start_mission_thread = None
@@ -82,8 +86,14 @@ class Robot(object):
82
86
  ):
83
87
  self.start_mission_thread.join()
84
88
  mission = self.start_mission_thread.mission
89
+ error_message = self.start_mission_thread.error_message
85
90
  self.start_mission_thread = None
86
91
 
92
+ if error_message:
93
+ self.robot_service_events.mission_failed.trigger_event(error_message)
94
+ else:
95
+ self.robot_service_events.mission_started.trigger_event(True)
96
+
87
97
  if (
88
98
  self.monitor_mission_thread is not None
89
99
  and self.monitor_mission_thread.is_alive()
@@ -131,6 +141,24 @@ class Robot(object):
131
141
  True
132
142
  )
133
143
 
144
+ def _pause_mission_done_handler(self) -> None:
145
+ if (
146
+ self.pause_mission_thread is not None
147
+ and not self.pause_mission_thread.is_alive()
148
+ ):
149
+ self.pause_mission_thread.join()
150
+ error_message = self.pause_mission_thread.error_message
151
+ self.pause_mission_thread = None
152
+
153
+ if error_message:
154
+ self.robot_service_events.mission_failed_to_pause.trigger_event(
155
+ error_message
156
+ )
157
+ else:
158
+ self.robot_service_events.mission_successfully_paused.trigger_event(
159
+ True
160
+ )
161
+
134
162
  def _start_mission_event_handler(self, event: Event[Mission]) -> None:
135
163
  start_mission = event.consume_event()
136
164
  if start_mission is not None:
@@ -144,7 +172,6 @@ class Robot(object):
144
172
  self.start_mission_thread.join()
145
173
 
146
174
  self.start_mission_thread = RobotStartMissionThread(
147
- self.robot_service_events,
148
175
  self.robot,
149
176
  self.signal_thread_quitting,
150
177
  start_mission,
@@ -204,7 +231,7 @@ class Robot(object):
204
231
  )
205
232
  return
206
233
  self.pause_mission_thread = RobotPauseMissionThread(
207
- self.robot_service_events, self.robot, self.signal_thread_quitting
234
+ self.robot, self.signal_thread_quitting
208
235
  )
209
236
  self.pause_mission_thread.start()
210
237
 
@@ -220,15 +247,20 @@ class Robot(object):
220
247
  self.upload_inspection_threads.append(upload_inspection_thread)
221
248
  upload_inspection_thread.start()
222
249
 
223
- def _join_threads(thread: RobotUploadInspectionThread) -> bool:
224
- if thread.is_done():
225
- thread.join()
226
- return True
227
- return False
250
+ def _upload_inspection_done_handler(self):
251
+ if len(self.upload_inspection_threads) > 0:
252
+
253
+ def _join_threads(thread: RobotUploadInspectionThread) -> bool:
254
+ if not thread.is_alive():
255
+ thread.join()
256
+ return True
257
+ return False
228
258
 
229
- self.upload_inspection_threads[:] = [
230
- thread for thread in self.upload_inspection_threads if _join_threads(thread)
231
- ]
259
+ self.upload_inspection_threads[:] = [
260
+ thread
261
+ for thread in self.upload_inspection_threads
262
+ if _join_threads(thread)
263
+ ]
232
264
 
233
265
  def run(self) -> None:
234
266
  self.robot_status_thread = RobotStatusThread(
@@ -260,4 +292,8 @@ class Robot(object):
260
292
 
261
293
  self._stop_mission_done_handler()
262
294
 
295
+ self._pause_mission_done_handler()
296
+
297
+ self._upload_inspection_done_handler()
298
+
263
299
  self.logger.info("Exiting robot service main thread")
@@ -86,6 +86,10 @@ class RobotMonitorMissionThread(Thread):
86
86
  if self.signal_thread_quitting.wait(0) or self.signal_mission_stopped.wait(
87
87
  0
88
88
  ):
89
+ failed_task_error = ErrorMessage(
90
+ error_reason=ErrorReason.RobotTaskStatusException,
91
+ error_description="Task status collection was cancelled by monitor thread exit",
92
+ )
89
93
  break
90
94
  if request_status_failure_counter > 0:
91
95
  time.sleep(settings.REQUEST_STATUS_COMMUNICATION_RECONNECT_DELAY)
@@ -320,10 +324,12 @@ class RobotMonitorMissionThread(Thread):
320
324
  new_task_status = self._get_task_status(current_task.id)
321
325
  except RobotTaskStatusException as e:
322
326
  self.logger.error(
323
- "Failed to collect task status", e.error_description
327
+ "Failed to collect task status. Error description: %s",
328
+ e.error_description,
324
329
  )
325
330
  break
326
331
  except Exception:
332
+ self.logger.exception("Failed to collect task status")
327
333
  break
328
334
 
329
335
  if current_task.status != new_task_status:
@@ -350,8 +356,14 @@ class RobotMonitorMissionThread(Thread):
350
356
 
351
357
  try:
352
358
  new_mission_status = self._get_mission_status(self.current_mission.id)
353
- except RobotMissionStatusException:
359
+ except RobotMissionStatusException as e:
354
360
  self.logger.exception("Failed to collect mission status")
361
+ self.robot_service_events.mission_failed.trigger_event(
362
+ ErrorMessage(
363
+ error_reason=e.error_reason,
364
+ error_description=e.error_description,
365
+ )
366
+ )
355
367
  break
356
368
  if new_mission_status != last_mission_status:
357
369
  self.current_mission.status = new_mission_status
@@ -4,11 +4,11 @@ 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.events import RobotServiceEvents
8
7
  from robot_interface.models.exceptions.robot_exceptions import (
9
8
  ErrorMessage,
10
9
  RobotActionException,
11
10
  RobotException,
11
+ RobotNoMissionRunningException,
12
12
  )
13
13
  from robot_interface.robot_interface import RobotInterface
14
14
 
@@ -16,14 +16,13 @@ from robot_interface.robot_interface import RobotInterface
16
16
  class RobotPauseMissionThread(Thread):
17
17
  def __init__(
18
18
  self,
19
- robot_service_events: RobotServiceEvents,
20
19
  robot: RobotInterface,
21
20
  signal_thread_quitting: Event,
22
21
  ):
23
22
  self.logger = logging.getLogger("robot")
24
- self.robot_service_events: RobotServiceEvents = robot_service_events
25
23
  self.robot: RobotInterface = robot
26
24
  self.signal_thread_quitting: Event = signal_thread_quitting
25
+ self.error_message: Optional[ErrorMessage] = None
27
26
  Thread.__init__(self, name="Robot pause mission thread")
28
27
 
29
28
  def run(self) -> None:
@@ -35,6 +34,11 @@ class RobotPauseMissionThread(Thread):
35
34
 
36
35
  try:
37
36
  self.robot.pause()
37
+ except RobotNoMissionRunningException as e:
38
+ error = ErrorMessage(
39
+ error_reason=e.error_reason, error_description=e.error_description
40
+ )
41
+ break
38
42
  except (RobotActionException, RobotException) as e:
39
43
  self.logger.warning(
40
44
  f"\nFailed to pause robot because: {e.error_description}"
@@ -46,7 +50,6 @@ class RobotPauseMissionThread(Thread):
46
50
  )
47
51
  time.sleep(settings.FSM_SLEEP_TIME)
48
52
  continue
49
- self.robot_service_events.mission_successfully_paused.trigger_event(True)
50
53
  return
51
54
 
52
55
  error_description = (
@@ -56,8 +59,7 @@ class RobotPauseMissionThread(Thread):
56
59
  "been attempted"
57
60
  )
58
61
 
59
- error_message = ErrorMessage(
62
+ self.error_message = ErrorMessage(
60
63
  error_reason=error.error_reason,
61
64
  error_description=error_description,
62
65
  )
63
- self.robot_service_events.mission_failed_to_pause.trigger_event(error_message)
@@ -1,8 +1,8 @@
1
1
  import logging
2
2
  from threading import Event, Thread
3
+ from typing import Optional
3
4
 
4
5
  from isar.config.settings import settings
5
- from isar.models.events import RobotServiceEvents
6
6
  from robot_interface.models.exceptions.robot_exceptions import (
7
7
  ErrorMessage,
8
8
  RobotException,
@@ -15,23 +15,21 @@ from robot_interface.robot_interface import RobotInterface
15
15
  class RobotStartMissionThread(Thread):
16
16
  def __init__(
17
17
  self,
18
- robot_service_events: RobotServiceEvents,
19
18
  robot: RobotInterface,
20
19
  signal_thread_quitting: Event,
21
20
  mission: Mission,
22
21
  ):
23
22
  self.logger = logging.getLogger("robot")
24
- self.robot_service_events: RobotServiceEvents = robot_service_events
25
23
  self.robot: RobotInterface = robot
26
24
  self.signal_thread_quitting: Event = signal_thread_quitting
27
25
  self.mission = mission
26
+ self.error_message: Optional[ErrorMessage] = None
28
27
  Thread.__init__(self, name="Robot start mission thread")
29
28
 
30
29
  def run(self):
31
30
  retries = 0
32
- started_mission = False
33
31
  try:
34
- while not started_mission:
32
+ while True:
35
33
  if self.signal_thread_quitting.wait(0):
36
34
  return
37
35
  try:
@@ -40,11 +38,9 @@ class RobotStartMissionThread(Thread):
40
38
  self.logger.error(
41
39
  f"Mission is infeasible and cannot be scheduled because: {e.error_description}"
42
40
  )
43
- self.robot_service_events.mission_failed.trigger_event(
44
- ErrorMessage(
45
- error_reason=e.error_reason,
46
- error_description=e.error_description,
47
- )
41
+ self.error_message = ErrorMessage(
42
+ error_reason=e.error_reason,
43
+ error_description=e.error_description,
48
44
  )
49
45
 
50
46
  break
@@ -62,23 +58,15 @@ class RobotStartMissionThread(Thread):
62
58
  f"{e.error_description}"
63
59
  )
64
60
 
65
- self.robot_service_events.mission_failed.trigger_event(
66
- ErrorMessage(
67
- error_reason=e.error_reason,
68
- error_description=e.error_description,
69
- )
61
+ self.error_message = ErrorMessage(
62
+ error_reason=e.error_reason,
63
+ error_description=e.error_description,
70
64
  )
71
65
  break
72
66
 
73
67
  continue
74
-
75
- started_mission = True
68
+ break
76
69
  except RobotInfeasibleMissionException as e:
77
- self.robot_service_events.mission_failed.trigger_event(
78
- ErrorMessage(
79
- error_reason=e.error_reason, error_description=e.error_description
80
- ),
70
+ self.error_message = ErrorMessage(
71
+ error_reason=e.error_reason, error_description=e.error_description
81
72
  )
82
-
83
- if started_mission:
84
- self.robot_service_events.mission_started.trigger_event(True)
@@ -9,6 +9,7 @@ from robot_interface.models.exceptions.robot_exceptions import (
9
9
  ErrorReason,
10
10
  RobotActionException,
11
11
  RobotException,
12
+ RobotNoMissionRunningException,
12
13
  )
13
14
  from robot_interface.robot_interface import RobotInterface
14
15
 
@@ -38,6 +39,8 @@ class RobotStopMissionThread(Thread):
38
39
 
39
40
  try:
40
41
  self.robot.stop()
42
+ except RobotNoMissionRunningException:
43
+ return
41
44
  except (RobotActionException, RobotException) as e:
42
45
  self.logger.warning(
43
46
  f"\nFailed to stop robot because: {e.error_description}"
@@ -27,15 +27,11 @@ class RobotUploadInspectionThread(Thread):
27
27
  self.task: TASKS = task
28
28
  self.upload_queue = upload_queue
29
29
  self.mission: Mission = mission
30
- self._is_done = False
31
30
  Thread.__init__(self, name=f"Robot inspection upload thread - {task.id}")
32
31
 
33
32
  def stop(self) -> None:
34
33
  return
35
34
 
36
- def is_done(self) -> bool:
37
- return self._is_done
38
-
39
35
  def run(self):
40
36
  try:
41
37
  inspection: Inspection = self.robot.get_inspection(task=self.task)
@@ -77,4 +73,3 @@ class RobotUploadInspectionThread(Thread):
77
73
  self.logger.info(
78
74
  f"Inspection result: {str(inspection.id)[:8]} queued for upload"
79
75
  )
80
- self._is_done = True
@@ -14,6 +14,7 @@ from isar.models.status import IsarStatus
14
14
  from isar.state_machine.states.await_next_mission import AwaitNextMission
15
15
  from isar.state_machine.states.blocked_protective_stop import BlockedProtectiveStop
16
16
  from isar.state_machine.states.going_to_lockdown import GoingToLockdown
17
+ from isar.state_machine.states.going_to_recharging import GoingToRecharging
17
18
  from isar.state_machine.states.home import Home
18
19
  from isar.state_machine.states.intervention_needed import InterventionNeeded
19
20
  from isar.state_machine.states.lockdown import Lockdown
@@ -27,6 +28,7 @@ from isar.state_machine.states.return_home_paused import ReturnHomePaused
27
28
  from isar.state_machine.states.returning_home import ReturningHome
28
29
  from isar.state_machine.states.stopping import Stopping
29
30
  from isar.state_machine.states.stopping_go_to_lockdown import StoppingGoToLockdown
31
+ from isar.state_machine.states.stopping_go_to_recharge import StoppingGoToRecharge
30
32
  from isar.state_machine.states.stopping_return_home import StoppingReturnHome
31
33
  from isar.state_machine.states.unknown_status import UnknownStatus
32
34
  from isar.state_machine.states_enum import States
@@ -95,7 +97,9 @@ class StateMachine(object):
95
97
  self.stopping_return_home_state: State = StoppingReturnHome(self)
96
98
  self.pausing_return_home_state: State = PausingReturnHome(self)
97
99
  self.stopping_go_to_lockdown_state: State = StoppingGoToLockdown(self)
100
+ self.stopping_go_to_recharge_state: State = StoppingGoToRecharge(self)
98
101
  self.going_to_lockdown_state: State = GoingToLockdown(self)
102
+ self.going_to_recharging_state: State = GoingToRecharging(self)
99
103
 
100
104
  # States Waiting for mission
101
105
  self.await_next_mission_state: State = AwaitNextMission(self)
@@ -130,6 +134,8 @@ class StateMachine(object):
130
134
  self.stopping_go_to_lockdown_state,
131
135
  self.going_to_lockdown_state,
132
136
  self.lockdown_state,
137
+ self.going_to_recharging_state,
138
+ self.stopping_go_to_recharge_state,
133
139
  ]
134
140
 
135
141
  self.machine = Machine(
@@ -279,6 +285,8 @@ class StateMachine(object):
279
285
  return IsarStatus.Lockdown
280
286
  elif self.current_state == States.GoingToLockdown:
281
287
  return IsarStatus.GoingToLockdown
288
+ elif self.current_state == States.GoingToRecharging:
289
+ return IsarStatus.GoingToRecharging
282
290
  else:
283
291
  return IsarStatus.Busy
284
292
 
@@ -22,6 +22,7 @@ class AwaitNextMission(EventHandlerBase):
22
22
 
23
23
  def __init__(self, state_machine: "StateMachine"):
24
24
  events = state_machine.events
25
+ shared_state = state_machine.shared_state
25
26
 
26
27
  def _send_to_lockdown_event_handler(
27
28
  event: Event[bool],
@@ -35,6 +36,18 @@ class AwaitNextMission(EventHandlerBase):
35
36
  )
36
37
  return state_machine.request_lockdown_mission # type: ignore
37
38
 
39
+ def _robot_battery_level_updated_handler(
40
+ event: Event[float],
41
+ ) -> Optional[Callable]:
42
+ battery_level: float = event.check()
43
+ if (
44
+ battery_level is None
45
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
46
+ ):
47
+ return None
48
+
49
+ return state_machine.request_recharging_mission # type: ignore
50
+
38
51
  event_handlers: List[EventHandlerMapping] = [
39
52
  EventHandlerMapping(
40
53
  name="start_mission_event",
@@ -58,6 +71,11 @@ class AwaitNextMission(EventHandlerBase):
58
71
  event=events.api_requests.send_to_lockdown.request,
59
72
  handler=_send_to_lockdown_event_handler,
60
73
  ),
74
+ EventHandlerMapping(
75
+ name="robot_battery_update_event",
76
+ event=shared_state.robot_battery_level,
77
+ handler=_robot_battery_level_updated_handler,
78
+ ),
61
79
  ]
62
80
 
63
81
  timers: List[TimeoutHandlerMapping] = [
@@ -0,0 +1,81 @@
1
+ from typing import TYPE_CHECKING, Callable, List, Optional
2
+
3
+ from isar.apis.models.models import LockdownResponse
4
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
5
+ from isar.models.events import Event
6
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
7
+ from robot_interface.models.mission.status import MissionStatus
8
+
9
+ if TYPE_CHECKING:
10
+ from isar.state_machine.state_machine import StateMachine
11
+
12
+
13
+ class GoingToRecharging(EventHandlerBase):
14
+
15
+ def __init__(self, state_machine: "StateMachine"):
16
+ events = state_machine.events
17
+
18
+ def _mission_failed_event_handler(
19
+ event: Event[Optional[ErrorMessage]],
20
+ ) -> Optional[Callable]:
21
+ mission_failed: Optional[ErrorMessage] = event.consume_event()
22
+ if mission_failed is None:
23
+ return None
24
+
25
+ state_machine.logger.warning(
26
+ f"Failed to go to recharging because: "
27
+ f"{mission_failed.error_description}"
28
+ )
29
+ return state_machine.return_home_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 not mission_status or mission_status in [
37
+ MissionStatus.InProgress,
38
+ MissionStatus.NotStarted,
39
+ MissionStatus.Paused,
40
+ ]:
41
+ return None
42
+
43
+ if mission_status != MissionStatus.Successful:
44
+ return state_machine.return_home_failed # type: ignore
45
+
46
+ return state_machine.starting_recharging # type: ignore
47
+
48
+ def _send_to_lockdown_event_handler(
49
+ event: Event[bool],
50
+ ) -> Optional[Callable]:
51
+ should_lockdown: bool = event.consume_event()
52
+ if not should_lockdown:
53
+ return None
54
+
55
+ events.api_requests.send_to_lockdown.response.trigger_event(
56
+ LockdownResponse(lockdown_started=True)
57
+ )
58
+ return state_machine.go_to_lockdown # type: ignore
59
+
60
+ event_handlers: List[EventHandlerMapping] = [
61
+ EventHandlerMapping(
62
+ name="mission_failed_event",
63
+ event=events.robot_service_events.mission_failed,
64
+ handler=_mission_failed_event_handler,
65
+ ),
66
+ EventHandlerMapping(
67
+ name="mission_status_event",
68
+ event=events.robot_service_events.mission_status_updated,
69
+ handler=_mission_status_event_handler,
70
+ ),
71
+ EventHandlerMapping(
72
+ name="send_to_lockdown_event",
73
+ event=events.api_requests.send_to_lockdown.request,
74
+ handler=_send_to_lockdown_event_handler,
75
+ ),
76
+ ]
77
+ super().__init__(
78
+ state_name="going_to_recharging",
79
+ state_machine=state_machine,
80
+ event_handler_mappings=event_handlers,
81
+ )
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING, Callable, List, Optional
2
2
 
3
3
  from isar.apis.models.models import LockdownResponse
4
+ from isar.config.settings import settings
4
5
  from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
5
6
  from isar.models.events import Event
6
7
  from isar.state_machine.utils.common_event_handlers import (
@@ -44,6 +45,18 @@ class Home(EventHandlerBase):
44
45
  return state_machine.robot_status_changed # type: ignore
45
46
  return None
46
47
 
48
+ def _robot_battery_level_updated_handler(
49
+ event: Event[float],
50
+ ) -> Optional[Callable]:
51
+ battery_level: float = event.check()
52
+ if (
53
+ battery_level is None
54
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
55
+ ):
56
+ return None
57
+
58
+ return state_machine.starting_recharging # type: ignore
59
+
47
60
  event_handlers: List[EventHandlerMapping] = [
48
61
  EventHandlerMapping(
49
62
  name="start_mission_event",
@@ -72,6 +85,11 @@ class Home(EventHandlerBase):
72
85
  event=events.api_requests.send_to_lockdown.request,
73
86
  handler=_send_to_lockdown_event_handler,
74
87
  ),
88
+ EventHandlerMapping(
89
+ name="robot_battery_update_event",
90
+ event=shared_state.robot_battery_level,
91
+ handler=_robot_battery_level_updated_handler,
92
+ ),
75
93
  ]
76
94
  super().__init__(
77
95
  state_name="home",
@@ -30,16 +30,16 @@ class Monitor(EventHandlerBase):
30
30
  event: Event[float],
31
31
  ) -> Optional[Callable]:
32
32
  battery_level: float = event.check()
33
- if battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD:
33
+ if (
34
+ battery_level is None
35
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
36
+ ):
34
37
  return None
35
38
 
36
- state_machine.publish_mission_aborted(
37
- "Robot battery too low to continue mission", True
38
- )
39
39
  state_machine.logger.warning(
40
40
  "Cancelling current mission due to low battery"
41
41
  )
42
- return state_machine.stop # type: ignore
42
+ return state_machine.stop_go_to_recharge # type: ignore
43
43
 
44
44
  def _send_to_lockdown_event_handler(
45
45
  event: Event[bool],
@@ -19,7 +19,10 @@ class Paused(EventHandlerBase):
19
19
  event: Event[float],
20
20
  ) -> Optional[Callable]:
21
21
  battery_level: float = event.check()
22
- if battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD:
22
+ if (
23
+ battery_level is None
24
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
25
+ ):
23
26
  return None
24
27
 
25
28
  state_machine.publish_mission_aborted(
@@ -21,7 +21,10 @@ class ReturnHomePaused(EventHandlerBase):
21
21
  ) -> Optional[Callable]:
22
22
  battery_level: float = event.check()
23
23
 
24
- if battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD:
24
+ if (
25
+ battery_level is None
26
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
27
+ ):
25
28
  return None
26
29
 
27
30
  return state_machine.resume # type: ignore
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING, Callable, List, Optional
2
2
 
3
3
  from isar.apis.models.models import LockdownResponse, MissionStartResponse
4
+ from isar.config.settings import settings
4
5
  from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
5
6
  from isar.models.events import Event
6
7
  from isar.state_machine.utils.common_event_handlers import mission_started_event_handler
@@ -17,6 +18,7 @@ class ReturningHome(EventHandlerBase):
17
18
  def __init__(self, state_machine: "StateMachine"):
18
19
  self.failed_return_home_attemps: int = 0
19
20
  events = state_machine.events
21
+ shared_state = state_machine.shared_state
20
22
 
21
23
  def _pause_mission_event_handler(event: Event[bool]) -> Optional[Callable]:
22
24
  if not event.consume_event():
@@ -59,10 +61,7 @@ class ReturningHome(EventHandlerBase):
59
61
  return state_machine.return_home_failed # type: ignore
60
62
 
61
63
  self.failed_return_home_attemps = 0
62
- if not state_machine.battery_level_is_above_mission_start_threshold():
63
- return state_machine.starting_recharging # type: ignore
64
- else:
65
- return state_machine.returned_home # type: ignore
64
+ return state_machine.returned_home # type: ignore
66
65
  return None
67
66
 
68
67
  def _send_to_lockdown_event_handler(
@@ -89,6 +88,18 @@ class ReturningHome(EventHandlerBase):
89
88
  return state_machine.return_home_failed # type: ignore
90
89
  return None
91
90
 
91
+ def _robot_battery_level_updated_handler(
92
+ event: Event[float],
93
+ ) -> Optional[Callable]:
94
+ battery_level: float = event.check()
95
+ if (
96
+ battery_level is None
97
+ or battery_level >= settings.ROBOT_MISSION_BATTERY_START_THRESHOLD
98
+ ):
99
+ return None
100
+
101
+ return state_machine.go_to_recharging # type: ignore
102
+
92
103
  event_handlers: List[EventHandlerMapping] = [
93
104
  EventHandlerMapping(
94
105
  name="pause_mission_event",
@@ -117,6 +128,11 @@ class ReturningHome(EventHandlerBase):
117
128
  event=events.robot_service_events.mission_status_updated,
118
129
  handler=_mission_status_event_handler,
119
130
  ),
131
+ EventHandlerMapping(
132
+ name="robot_battery_update_event",
133
+ event=shared_state.robot_battery_level,
134
+ handler=_robot_battery_level_updated_handler,
135
+ ),
120
136
  EventHandlerMapping(
121
137
  name="send_to_lockdown_event",
122
138
  event=events.api_requests.send_to_lockdown.request,
@@ -0,0 +1,51 @@
1
+ from typing import TYPE_CHECKING, Callable, List, Optional
2
+
3
+ from isar.eventhandlers.eventhandler import EventHandlerBase, EventHandlerMapping
4
+ from isar.models.events import Event
5
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
6
+
7
+ if TYPE_CHECKING:
8
+ from isar.state_machine.state_machine import StateMachine
9
+
10
+
11
+ class StoppingGoToRecharge(EventHandlerBase):
12
+
13
+ def __init__(self, state_machine: "StateMachine"):
14
+ events = state_machine.events
15
+
16
+ def _failed_stop_event_handler(
17
+ event: Event[ErrorMessage],
18
+ ) -> Optional[Callable]:
19
+ error_message: Optional[ErrorMessage] = event.consume_event()
20
+ if error_message is None:
21
+ return None
22
+
23
+ return state_machine.mission_stopping_failed # type: ignore
24
+
25
+ def _successful_stop_event_handler(event: Event[bool]) -> Optional[Callable]:
26
+ if not event.consume_event():
27
+ return None
28
+
29
+ state_machine.publish_mission_aborted(
30
+ "Robot battery too low to continue mission", True
31
+ )
32
+
33
+ return state_machine.request_recharging_mission # type: ignore
34
+
35
+ event_handlers: List[EventHandlerMapping] = [
36
+ EventHandlerMapping(
37
+ name="failed_stop_event",
38
+ event=events.robot_service_events.mission_failed_to_stop,
39
+ handler=_failed_stop_event_handler,
40
+ ),
41
+ EventHandlerMapping(
42
+ name="successful_stop_event",
43
+ event=events.robot_service_events.mission_successfully_stopped,
44
+ handler=_successful_stop_event_handler,
45
+ ),
46
+ ]
47
+ super().__init__(
48
+ state_name="stopping_go_to_recharge",
49
+ state_machine=state_machine,
50
+ event_handler_mappings=event_handlers,
51
+ )
@@ -20,6 +20,8 @@ class States(str, Enum):
20
20
  StoppingGoToLockdown = "stopping_go_to_lockdown"
21
21
  GoingToLockdown = "going_to_lockdown"
22
22
  Lockdown = "lockdown"
23
+ GoingToRecharging = "going_to_recharging"
24
+ StoppingGoToRecharge = "stopping_go_to_recharge"
23
25
 
24
26
  def __repr__(self):
25
27
  return self.value
@@ -10,6 +10,7 @@ from isar.config.settings import settings
10
10
  from robot_interface.models.exceptions.robot_exceptions import (
11
11
  RobotActionException,
12
12
  RobotException,
13
+ RobotNoMissionRunningException,
13
14
  )
14
15
 
15
16
 
@@ -32,6 +33,12 @@ def resume_mission(state_machine: "StateMachine") -> bool:
32
33
 
33
34
  state_machine.logger.info("Mission resumed successfully.")
34
35
  return True
36
+ except RobotNoMissionRunningException as e:
37
+ state_machine.logger.error(
38
+ f"Failed to resume mission: {e.error_reason}. {e.error_description}"
39
+ )
40
+ # TODO: this will make it go to paused, instead of awaitnextmission
41
+ return False
35
42
  except RobotActionException as e:
36
43
  state_machine.logger.warning(
37
44
  f"Attempt {attempt + 1} to resume mission failed: {e.error_description}"
@@ -110,6 +110,12 @@ def get_mission_transitions(state_machine: "StateMachine") -> List[dict]:
110
110
  "dest": state_machine.stopping_go_to_lockdown_state,
111
111
  "before": def_transition(state_machine, trigger_stop_mission_event),
112
112
  },
113
+ {
114
+ "trigger": "stop_go_to_recharge",
115
+ "source": state_machine.monitor_state,
116
+ "dest": state_machine.stopping_go_to_recharge_state,
117
+ "before": def_transition(state_machine, trigger_stop_mission_event),
118
+ },
113
119
  {
114
120
  "trigger": "stop_return_home",
115
121
  "source": [
@@ -140,7 +146,10 @@ def get_mission_transitions(state_machine: "StateMachine") -> List[dict]:
140
146
  },
141
147
  {
142
148
  "trigger": "mission_stopping_failed",
143
- "source": state_machine.stopping_go_to_lockdown_state,
149
+ "source": [
150
+ state_machine.stopping_go_to_lockdown_state,
151
+ state_machine.stopping_go_to_recharge_state,
152
+ ],
144
153
  "dest": state_machine.monitor_state,
145
154
  },
146
155
  {
@@ -61,7 +61,7 @@ def get_return_home_transitions(state_machine: "StateMachine") -> List[dict]:
61
61
  },
62
62
  {
63
63
  "trigger": "starting_recharging",
64
- "source": state_machine.returning_home_state,
64
+ "source": state_machine.going_to_recharging_state,
65
65
  "dest": state_machine.recharging_state,
66
66
  "before": [
67
67
  def_transition(state_machine, reset_return_home_failure_counter),
@@ -69,7 +69,7 @@ def get_return_home_transitions(state_machine: "StateMachine") -> List[dict]:
69
69
  },
70
70
  {
71
71
  "trigger": "starting_recharging",
72
- "source": state_machine.lockdown_state,
72
+ "source": [state_machine.lockdown_state, state_machine.home_state],
73
73
  "dest": state_machine.recharging_state,
74
74
  },
75
75
  {
@@ -86,7 +86,10 @@ def get_return_home_transitions(state_machine: "StateMachine") -> List[dict]:
86
86
  },
87
87
  {
88
88
  "trigger": "return_home_failed",
89
- "source": state_machine.returning_home_state,
89
+ "source": [
90
+ state_machine.returning_home_state,
91
+ state_machine.going_to_recharging_state,
92
+ ],
90
93
  "dest": state_machine.intervention_needed_state,
91
94
  "before": [
92
95
  def_transition(
@@ -112,14 +115,36 @@ def get_return_home_transitions(state_machine: "StateMachine") -> List[dict]:
112
115
  def_transition(state_machine, initialize_robot),
113
116
  ],
114
117
  },
118
+ {
119
+ "trigger": "request_recharging_mission",
120
+ "source": [
121
+ state_machine.stopping_go_to_recharge_state,
122
+ state_machine.await_next_mission_state,
123
+ ],
124
+ "dest": state_machine.going_to_recharging_state,
125
+ "conditions": [
126
+ def_transition(state_machine, start_return_home_mission),
127
+ def_transition(state_machine, initialize_robot),
128
+ ],
129
+ },
115
130
  {
116
131
  "trigger": "go_to_lockdown",
117
132
  "source": [
118
133
  state_machine.returning_home_state,
119
134
  state_machine.return_home_paused_state,
135
+ state_machine.going_to_recharging_state,
120
136
  ],
121
137
  "dest": state_machine.going_to_lockdown_state,
122
138
  },
139
+ {
140
+ "trigger": "go_to_recharging",
141
+ "source": [
142
+ state_machine.returning_home_state,
143
+ state_machine.return_home_paused_state,
144
+ state_machine.home_state,
145
+ ],
146
+ "dest": state_machine.going_to_recharging_state,
147
+ },
123
148
  {
124
149
  "trigger": "reached_lockdown",
125
150
  "source": [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: isar
3
- Version: 1.34.2
3
+ Version: 1.34.4
4
4
  Summary: Integration and Supervisory control of Autonomous Robots
5
5
  Author-email: Equinor ASA <fg_robots_dev@equinor.com>
6
6
  License: Eclipse Public License version 2.0
@@ -41,15 +41,15 @@ isar/mission_planner/local_planner.py,sha256=Mkg3vvUBF1jImfQnaFvXLNpKVadR21X4mwD
41
41
  isar/mission_planner/mission_planner_interface.py,sha256=UgpPIM4FbrWOD7fGY3Ul64k3uYb8wo0FwSWGewYoVbc,485
42
42
  isar/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  isar/models/events.py,sha256=lwjq745VWJLRLdqNke58sWMRt8zz75hUt7t0QkbLsFU,5602
44
- isar/models/status.py,sha256=RCsf0p6FEsOFr4FGA6wekRdJIPrCOHMYuteXs2Mwwhk,430
45
- isar/robot/robot.py,sha256=gppNNm_4yjjU3fqWrgBP1VaMcQqHXw8eJk2H41mhLco,10703
44
+ isar/models/status.py,sha256=qPQdeE_6yB-twWtTHdeUMmSLMjbtWNGLHJOF_aucqx4,474
45
+ isar/robot/robot.py,sha256=bVzD4psVOCc7La1DjwTeRUmxFWwWp-3TwMdmcyoFOwU,11996
46
46
  isar/robot/robot_battery.py,sha256=goLdgmn61QCgE2Ja3YuiwE_sqJzIhCkS3u90sz1kdug,2089
47
- isar/robot/robot_monitor_mission.py,sha256=OIb7bLbld68pgnKkzgmkoUDINxSEvRtcaEXyQLioScc,15308
48
- isar/robot/robot_pause_mission.py,sha256=BFTLVFOnCeuLlyz1Lu12_6EgYBhk8frsviCs-kGq7AA,2277
49
- isar/robot/robot_start_mission.py,sha256=RPYH9VtXDFdPqhOpt6kSbT17RHkJQPKkQ6d4784_pFE,3210
47
+ isar/robot/robot_monitor_mission.py,sha256=mdnTZlhBgr-lWsAQRyP_vXiu3DYl-4joC0myZmgcw1o,15938
48
+ isar/robot/robot_pause_mission.py,sha256=MpK3YEux7k0Ks8Orv7Iz7ADhiEC8X_9LQZ5mK7e1a90,2247
49
+ isar/robot/robot_start_mission.py,sha256=KO_gUNEYvjNJ3eSkH-umUOEezwpLKIYBEuF_eot4rIM,2683
50
50
  isar/robot/robot_status.py,sha256=dfAls3s8_Vha7ZMLSYngELqsdpaEpcweAWRHanQj8u8,2361
51
- isar/robot/robot_stop_mission.py,sha256=noLqg7zdd0TMOOIqj-60tOQ_1wLOKOuUSkPIxZ3j1Ks,2211
52
- isar/robot/robot_upload_inspection.py,sha256=CFPz6JNinLO5M2MQG6AzYK0jl1hhwi8zaqITeB5A9EU,2659
51
+ isar/robot/robot_stop_mission.py,sha256=CaXLsZjjazHmydU9iR7uOWUHtJ2zvKh4ItUbkWY5sIk,2321
52
+ isar/robot/robot_upload_inspection.py,sha256=uCc9nuH1Am5uxD2Tgnm4ZTOm0tQnud0F6eHs0rSUfvY,2539
53
53
  isar/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  isar/services/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  isar/services/auth/azure_credentials.py,sha256=9PlwGe5FrPRbW2dp0go7LMp8_l_FRvL8xOXotXwzRDo,364
@@ -64,34 +64,36 @@ isar/services/utilities/robot_utilities.py,sha256=4zCigsLXfqDC8POHchktSq81zr1_pT
64
64
  isar/services/utilities/scheduling_utilities.py,sha256=AcoMWpNfNJ-IRu5ulLNtriLTKIVgog47im0yQ_ECanQ,20442
65
65
  isar/services/utilities/threaded_request.py,sha256=py4G-_RjnIdHljmKFAcQ6ddqMmp-ZYV39Ece-dqRqjs,1874
66
66
  isar/state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
- isar/state_machine/state_machine.py,sha256=lUT-lvtH2r0_ZKLs9AaXx80RWRDJv209w443LpF0G8o,11527
68
- isar/state_machine/states_enum.py,sha256=EzqLHsNbR7KBMIY5Fa_CDaHm9v6g8UFzq9DJs4x0OU8,746
67
+ isar/state_machine/state_machine.py,sha256=xd9GISbN-N73k8fme2zctg7H6Ur5Zl27PAg--xmj34k,12038
68
+ isar/state_machine/states_enum.py,sha256=XaFJ1GJMVACE5aw3oYxP-GPT6ucLXhnIMGOu5nTLVnI,845
69
69
  isar/state_machine/states/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- isar/state_machine/states/await_next_mission.py,sha256=H1QN62S6iUYzLgyzK8UVG-0PEWGiCDdsIOjWC7BWD_Q,2742
70
+ isar/state_machine/states/await_next_mission.py,sha256=MlIyFbiu6odQuX0H7Y1vg9_ay-ZzsTFtraX7yyIOxkY,3433
71
71
  isar/state_machine/states/blocked_protective_stop.py,sha256=HJr_jH8OdHMuX8zhjlF5jtNAfMCxkA1X7nOGmkAz-RY,1263
72
72
  isar/state_machine/states/going_to_lockdown.py,sha256=qOmJt5V51_fhJobIzzFyFW-sxfoktG8_Vt9a4N-EmWs,2725
73
- isar/state_machine/states/home.py,sha256=nIuHVDEYkMuoGTdf0nTUlOaNblYVDoZdl_njeE2sFNQ,3161
73
+ isar/state_machine/states/going_to_recharging.py,sha256=Ubn7YvKs4gr6rlII9BLt2aamIBYIIY9QfcQN-yn1HtY,3051
74
+ isar/state_machine/states/home.py,sha256=BNIuKYjRPGLjcVXfrJLyNM7sab6HB95hFr_n2MWoZpM,3837
74
75
  isar/state_machine/states/intervention_needed.py,sha256=nv7054e9gBJQ4-Mshs8cczqnD0IN-N-_5qvUR-cNcUM,1633
75
76
  isar/state_machine/states/lockdown.py,sha256=ZFw9XMDENECuH3lh89RKeU_3CTzW0ZBvqCKptFJG5xo,1364
76
- isar/state_machine/states/monitor.py,sha256=UIvqU2uNashm-ZCYuGr3GkpuwWbmuGC7ipG_XSHgO34,4514
77
+ isar/state_machine/states/monitor.py,sha256=o68TMYMoYccnbyXsVsIehuYdvMv3FqZYNzFI3oIdl44,4471
77
78
  isar/state_machine/states/offline.py,sha256=k1UdcRWLd6lJiL5cnpHKBkvMY4xZMt_x7XIauy-Rgww,1219
78
- isar/state_machine/states/paused.py,sha256=F5XIzIcnR7_BvIDyMu8CWEdsmXRukUFTYy0-H1ldWeo,2849
79
+ isar/state_machine/states/paused.py,sha256=pR8ZuETs-OAl6oUEZGRCfXAZgQyeDeA8KcOKA1b8c1Y,2922
79
80
  isar/state_machine/states/pausing.py,sha256=CJf_wqpajizv-UY3gL8ctHbPbVP-jXkLsaQiCgfCod4,2138
80
81
  isar/state_machine/states/pausing_return_home.py,sha256=bQjNp8cC90x7yoTLccrx3oIdaj9at-34p9ez1OqKrmE,2184
81
82
  isar/state_machine/states/recharging.py,sha256=TSCZuWhDKBW47peeP73k7aBD4_SEMOOdD1GgOdAq6lk,2447
82
- isar/state_machine/states/return_home_paused.py,sha256=rr5iy8u6zaTMZAGtlf1HlroVShjytxKjE6t-wGym8nU,3355
83
- isar/state_machine/states/returning_home.py,sha256=7bWLE2HU0rObPzM2evY8Wzl9hmerJMS0xaRt4vs3tFQ,5318
83
+ isar/state_machine/states/return_home_paused.py,sha256=cm2svV5l1dkDzFkGN0v3ZBEk7x8knU8E2u7N-jmFM7A,3428
84
+ isar/state_machine/states/returning_home.py,sha256=d_VSxLL5qKl6UYGWs-rTv6mhTbw2cvuECrAEyrntugU,5851
84
85
  isar/state_machine/states/stopping.py,sha256=MhIU1kZCjOQpZBZ5EQ299zdvBWNaDNVdksh7Zw4Hk68,2208
85
86
  isar/state_machine/states/stopping_go_to_lockdown.py,sha256=yl0HFxIBKFCWGQixTULuYb_Z-dwrPeI8TioZ9AQf1Lk,2264
87
+ isar/state_machine/states/stopping_go_to_recharge.py,sha256=eu2rPxeBJbQCNKlIxn9HJ7qZ21Fz81W-HKg8Le8gZtE,1861
86
88
  isar/state_machine/states/stopping_return_home.py,sha256=HtoZfSXWLGCRChITZCKjOufowwZzQaJLCZGaYsjP1mE,2854
87
89
  isar/state_machine/states/unknown_status.py,sha256=Vu07Sk90zFVInf3XdzFl5IaYrdrYaGO7Na7qAOGwjIk,1871
88
- isar/state_machine/transitions/mission.py,sha256=o3UG9klPqZnWTYVjTFOXYiMFxfbrWIxRvcaTfcqYW_0,7396
89
- isar/state_machine/transitions/return_home.py,sha256=aLOK7t7Us_TrDhtxhwW4XaIGxB_gU3Drg2uJFuOjS54,5558
90
+ isar/state_machine/transitions/mission.py,sha256=5H4ZvmsNn3kcaVSC7dN0C2JumzRlqq-daARtexxPgHk,7754
91
+ isar/state_machine/transitions/return_home.py,sha256=42o3etvJeBaWK4bJfqwtFLpSzeJFhsu_UalzLW4CzPA,6525
90
92
  isar/state_machine/transitions/robot_status.py,sha256=7fEXHBBo8A6z8eUeNXs01xQ87l26siPB3kyTSbcnPcc,2738
91
93
  isar/state_machine/transitions/functions/fail_mission.py,sha256=NFPGIPE0EtefUETbf8Z8plBWghcRQBrbup5xaxF3h6Y,604
92
94
  isar/state_machine/transitions/functions/finish_mission.py,sha256=K9paNVz0wbWSkCWvVBaKTM54vdHBH-TNuqG1EstEI8w,229
93
95
  isar/state_machine/transitions/functions/pause.py,sha256=8cQIvXDb1hKS-K6-a0fmcqJHde0FRzS-n4N8_EBSA3w,1089
94
- isar/state_machine/transitions/functions/resume.py,sha256=HWmM1T4jvMPq3by4rns9G0lKUtWMegkFkGtbILvzfKE,1600
96
+ isar/state_machine/transitions/functions/resume.py,sha256=pO2PSLQsXDXGJuFDPb6TqUJ87515OcyZBo3iGSYHZ3g,1932
95
97
  isar/state_machine/transitions/functions/return_home.py,sha256=UBXOfYKlvsmpnmUeKFkc2483fdY9pX70F4pmndjCs8Y,1148
96
98
  isar/state_machine/transitions/functions/robot_status.py,sha256=dVh0cY6KLt0xcZ2jBQWt4GbR_CPJvrQZWhP3GEpDCZY,1105
97
99
  isar/state_machine/transitions/functions/start_mission.py,sha256=1Op-Dutfmbc6kH1bHHu7obQ5I1G5jnYoH4wx5qSXoI0,1254
@@ -104,13 +106,13 @@ isar/storage/local_storage.py,sha256=Rn-iiiz9DI7PzIhevOMshPIaqzJaqBXeVJMQRhVSl2M
104
106
  isar/storage/storage_interface.py,sha256=x-imVeQTdL6dCaTaPTHpXwCR6N4e27WxK_Vpumg0x-Y,1230
105
107
  isar/storage/uploader.py,sha256=0BBrxyZGGRkNxGeZeoREucegs4yKUow2523oLEie07o,10841
106
108
  isar/storage/utilities.py,sha256=oLH0Rp7UtrQQdilfITnmXO1Z0ExdeDhBImYHid55vBA,3449
107
- isar-1.34.2.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
109
+ isar-1.34.4.dist-info/licenses/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
108
110
  robot_interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
111
  robot_interface/robot_interface.py,sha256=3JAVCzg4WV4gYgDxGfdmXPPlVETmwyoqLh7QZJMerKU,9886
110
112
  robot_interface/test_robot_interface.py,sha256=FV1urn7SbsMyWBIcTKjsBwAG4IsXeZ6pLHE0mA9EGGs,692
111
113
  robot_interface/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
114
  robot_interface/models/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
- robot_interface/models/exceptions/robot_exceptions.py,sha256=UFya2u-slcf2ebc3ZVCs-Kp1SS9PQvKdWInYMBEQ8BY,10404
115
+ robot_interface/models/exceptions/robot_exceptions.py,sha256=B08Om0poV2XWfI3b7nG0dkwU0CoWUUHLuyQnPpPNQec,10885
114
116
  robot_interface/models/initialize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
117
  robot_interface/models/inspection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
118
  robot_interface/models/inspection/inspection.py,sha256=cjAvekL8r82s7bgukWeXpYylHvJG_oRSCJNISPVCvZg,2238
@@ -128,8 +130,8 @@ robot_interface/telemetry/payloads.py,sha256=A0SWiG609k6o6-Y3vhDWE6an2-_m7D_ND85
128
130
  robot_interface/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
131
  robot_interface/utilities/json_service.py,sha256=9N1zijW7K4d3WFR2autpaS8U9o1ibymiOX-6stTKCyk,1243
130
132
  robot_interface/utilities/uuid_string_factory.py,sha256=_NQIbBQ56w0qqO0MUDP6aPpHbxW7ATRhK8HnQiBSLkc,76
131
- isar-1.34.2.dist-info/METADATA,sha256=ArvN55GfJYYtkePQzFkV0d3ebH4AvfAsvDOTGMXFq7Y,28866
132
- isar-1.34.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
133
- isar-1.34.2.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
134
- isar-1.34.2.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
135
- isar-1.34.2.dist-info/RECORD,,
133
+ isar-1.34.4.dist-info/METADATA,sha256=k6ub1kgGNic9LAtm8-lgZNtNUk3-LDnz0mU0fgq7KJI,28866
134
+ isar-1.34.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
135
+ isar-1.34.4.dist-info/entry_points.txt,sha256=TFam7uNNw7J0iiDYzsH2gfG0u1eV1wh3JTw_HkhgKLk,49
136
+ isar-1.34.4.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
137
+ isar-1.34.4.dist-info/RECORD,,
@@ -24,6 +24,7 @@ class ErrorReason(str, Enum):
24
24
  RobotTransformException = "robot_transform_exception"
25
25
  RobotUnknownErrorException = "robot_unknown_error_exception"
26
26
  RobotDisconnectedException = "robot_disconnected_exception"
27
+ RobotNoMissionRunningException = "robot_no_mission_running_exception"
27
28
 
28
29
 
29
30
  @dataclass
@@ -53,6 +54,18 @@ class RobotCommunicationException(RobotException):
53
54
  pass
54
55
 
55
56
 
57
+ # An exception which should be thrown by the robot package if it is unable to
58
+ # complete a request as there is no ongoing mission.
59
+ class RobotNoMissionRunningException(RobotException):
60
+ def __init__(self, error_description: str) -> None:
61
+ super().__init__(
62
+ error_reason=ErrorReason.RobotNoMissionRunningException,
63
+ error_description=error_description,
64
+ )
65
+
66
+ pass
67
+
68
+
56
69
  # An exception which should be thrown by the robot package if the communication has timed
57
70
  # out and ISAR should retry the request.
58
71
  class RobotCommunicationTimeoutException(RobotException):
File without changes