isar 1.31.0__py3-none-any.whl → 1.32.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (58) hide show
  1. isar/apis/api.py +18 -0
  2. isar/apis/schedule/scheduling_controller.py +16 -0
  3. isar/config/log.py +1 -2
  4. isar/config/logging.conf +9 -30
  5. isar/config/open_telemetry.py +2 -8
  6. isar/config/settings.py +4 -0
  7. isar/eventhandlers/eventhandler.py +93 -0
  8. isar/models/events.py +119 -0
  9. isar/modules.py +1 -1
  10. isar/robot/robot.py +16 -18
  11. isar/robot/robot_start_mission.py +8 -14
  12. isar/robot/robot_status.py +2 -3
  13. isar/robot/robot_stop_mission.py +3 -9
  14. isar/robot/robot_task_status.py +3 -7
  15. isar/script.py +9 -17
  16. isar/services/utilities/scheduling_utilities.py +45 -23
  17. isar/state_machine/state_machine.py +104 -9
  18. isar/state_machine/states/await_next_mission.py +46 -11
  19. isar/state_machine/states/blocked_protective_stop.py +24 -15
  20. isar/state_machine/states/home.py +40 -9
  21. isar/state_machine/states/intervention_needed.py +43 -0
  22. isar/state_machine/states/monitor.py +83 -12
  23. isar/state_machine/states/offline.py +25 -13
  24. isar/state_machine/states/paused.py +24 -38
  25. isar/state_machine/states/returning_home.py +75 -14
  26. isar/state_machine/states/robot_standing_still.py +41 -11
  27. isar/state_machine/states/stopping.py +52 -67
  28. isar/state_machine/states/unknown_status.py +37 -64
  29. isar/state_machine/states_enum.py +1 -0
  30. isar/state_machine/transitions/functions/fail_mission.py +7 -0
  31. isar/state_machine/transitions/functions/robot_status.py +4 -5
  32. isar/state_machine/transitions/functions/stop.py +3 -12
  33. isar/state_machine/transitions/mission.py +0 -6
  34. isar/state_machine/transitions/return_home.py +14 -2
  35. isar/state_machine/utils/common_event_handlers.py +166 -0
  36. isar/storage/uploader.py +1 -1
  37. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/METADATA +1 -1
  38. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/RECORD +44 -54
  39. robot_interface/models/mission/status.py +1 -0
  40. robot_interface/telemetry/payloads.py +8 -0
  41. isar/models/communication/__init__.py +0 -0
  42. isar/models/communication/message.py +0 -8
  43. isar/models/communication/queues/__init__.py +0 -0
  44. isar/models/communication/queues/events.py +0 -58
  45. isar/models/communication/queues/queue_io.py +0 -12
  46. isar/models/communication/queues/queue_timeout_error.py +0 -2
  47. isar/models/communication/queues/queue_utils.py +0 -38
  48. isar/models/communication/queues/status_queue.py +0 -22
  49. isar/models/mission_metadata/__init__.py +0 -0
  50. isar/services/service_connections/stid/__init__.py +0 -0
  51. isar/services/utilities/queue_utilities.py +0 -39
  52. isar/state_machine/generic_states/idle.py +0 -133
  53. isar/state_machine/generic_states/ongoing_mission.py +0 -309
  54. isar/state_machine/generic_states/robot_unavailable.py +0 -61
  55. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/WHEEL +0 -0
  56. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/entry_points.txt +0 -0
  57. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/licenses/LICENSE +0 -0
  58. {isar-1.31.0.dist-info → isar-1.32.0.dist-info}/top_level.txt +0 -0
isar/apis/api.py CHANGED
@@ -245,6 +245,24 @@ class API:
245
245
  },
246
246
  },
247
247
  )
248
+ router.add_api_route(
249
+ path="/schedule/release-intervention-needed",
250
+ endpoint=self.scheduling_controller.release_intervention_needed,
251
+ methods=["POST"],
252
+ dependencies=[authentication_dependency],
253
+ summary="Release the intervention needed state",
254
+ responses={
255
+ HTTPStatus.OK.value: {
256
+ "description": "Robot released from intervention needed state"
257
+ },
258
+ HTTPStatus.CONFLICT.value: {
259
+ "description": "Conflict - Invalid command in the current state"
260
+ },
261
+ HTTPStatus.INTERNAL_SERVER_ERROR.value: {
262
+ "description": "Internal Server Error - Current state of state machine unknown"
263
+ },
264
+ },
265
+ )
248
266
  router.add_api_route(
249
267
  path="/schedule/move_arm/{arm_pose_literal}",
250
268
  endpoint=self.scheduling_controller.start_move_arm_mission,
@@ -256,6 +256,22 @@ class SchedulingController:
256
256
  self.scheduling_utilities.start_mission(mission=mission)
257
257
  return self._api_response(mission)
258
258
 
259
+ def release_intervention_needed(self) -> None:
260
+ self.logger.info("Received request to release intervention needed state")
261
+
262
+ state: States = self.scheduling_utilities.get_state()
263
+
264
+ if state != States.InterventionNeeded:
265
+ error_message = f"Conflict - Release intervention needed command received in invalid state - State: {state}"
266
+ self.logger.warning(error_message)
267
+ raise HTTPException(
268
+ status_code=HTTPStatus.CONFLICT,
269
+ detail=error_message,
270
+ )
271
+
272
+ self.scheduling_utilities.release_intervention_needed()
273
+ self.logger.info("Released intervention needed state successfully")
274
+
259
275
  def _api_response(self, mission: Mission) -> StartMissionResponse:
260
276
  return StartMissionResponse(
261
277
  id=mission.id,
isar/config/log.py CHANGED
@@ -5,11 +5,10 @@ from importlib.resources import as_file, files
5
5
  import yaml
6
6
  from uvicorn.logging import ColourizedFormatter
7
7
 
8
- from isar.config.keyvault.keyvault_service import Keyvault
9
8
  from isar.config.settings import settings
10
9
 
11
10
 
12
- def setup_loggers(keyvault: Keyvault) -> None:
11
+ def setup_loggers() -> None:
13
12
  log_levels: dict = settings.LOG_LEVELS
14
13
  log_config = load_log_config()
15
14
 
isar/config/logging.conf CHANGED
@@ -5,50 +5,29 @@ formatters:
5
5
  colourized:
6
6
  style: "{"
7
7
  format: "{asctime} - {levelprefix:<8} - {name} - {message}"
8
- handlers:
9
- api:
10
- class: logging.FileHandler
11
- formatter: simple
12
- filename: api.log
13
- main:
14
- class: logging.FileHandler
15
- formatter: simple
16
- filename: main.log
17
- mqtt:
18
- class: logging.FileHandler
19
- formatter: simple
20
- filename: mqtt.log
21
- state_machine:
22
- class: logging.FileHandler
23
- formatter: simple
24
- filename: state_machine.log
25
- uploader:
26
- class: logging.FileHandler
27
- formatter: simple
28
- filename: uploader.log
29
8
  loggers:
30
9
  console:
31
10
  handlers: []
32
11
  propagate: no
33
12
  main:
34
- handlers: [main]
13
+ handlers: []
35
14
  propagate: no
36
15
  api:
37
- handlers: [api]
16
+ handlers: []
38
17
  propagate: no
39
18
  mqtt:
40
- handlers: [mqtt]
41
- propagate: False
19
+ handlers: []
20
+ propagate: no
42
21
  state_machine:
43
- handlers: [state_machine]
44
- propagate: False
22
+ handlers: []
23
+ propagate: no
45
24
  uploader:
46
- handlers: [uploader]
47
- propagate: False
25
+ handlers: []
26
+ propagate: no
48
27
  urllib3:
49
28
  handlers: []
50
29
  uvicorn:
51
- handlers: [api]
30
+ handlers: []
52
31
  propagate: no
53
32
  azure:
54
33
  handlers: []
@@ -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
isar/config/settings.py CHANGED
@@ -224,6 +224,9 @@ class Settings(BaseSettings):
224
224
  default="robot_heartbeat", validate_default=True
225
225
  )
226
226
  TOPIC_ISAR_STARTUP: str = Field(default="startup", validate_default=True)
227
+ TOPIC_ISAR_INTERVENTION_NEEDED: str = Field(
228
+ default="intervention_needed", validate_default=True
229
+ )
227
230
 
228
231
  # Logging
229
232
 
@@ -275,6 +278,7 @@ class Settings(BaseSettings):
275
278
  "TOPIC_ISAR_INSPECTION_RESULT",
276
279
  "TOPIC_ISAR_INSPECTION_VALUE",
277
280
  "TOPIC_ISAR_STARTUP",
281
+ "TOPIC_ISAR_INTERVENTION_NEEDED",
278
282
  )
279
283
  @classmethod
280
284
  def prefix_isar_topics(cls, v: Any, info: ValidationInfo):
@@ -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,119 @@
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
+ self.release_intervention_needed: APIEvent[bool, bool] = APIEvent()
90
+
91
+
92
+ class StateMachineEvents:
93
+ def __init__(self) -> None:
94
+ self.start_mission: Event[Mission] = Event()
95
+ self.stop_mission: Event[bool] = Event()
96
+ self.pause_mission: Event[bool] = Event()
97
+ self.task_status_request: Event[str] = Event()
98
+
99
+
100
+ class RobotServiceEvents:
101
+ def __init__(self) -> None:
102
+ self.task_status_updated: Event[TaskStatus] = Event()
103
+ self.task_status_failed: Event[ErrorMessage] = Event()
104
+ self.mission_started: Event[bool] = Event()
105
+ self.mission_failed: Event[ErrorMessage] = Event()
106
+ self.robot_status_changed: Event[bool] = Event()
107
+ self.mission_failed_to_stop: Event[ErrorMessage] = Event()
108
+ self.mission_successfully_stopped: Event[bool] = Event()
109
+
110
+
111
+ class SharedState:
112
+ def __init__(self) -> None:
113
+ self.state: Event[State] = Event()
114
+ self.robot_status: Event[RobotStatus] = Event()
115
+ self.state_machine_current_task: Event[TASKS] = Event()
116
+
117
+
118
+ class EventTimeoutError(Exception):
119
+ 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
@@ -36,7 +36,8 @@ def print_setting(
36
36
 
37
37
 
38
38
  def print_startup_info():
39
- print(
39
+ logger: Logger = logging.getLogger("main")
40
+ logger.info(
40
41
  """
41
42
  __ ________ ___ ________
42
43
  / / / ______/ / | / ____ /
@@ -48,42 +49,33 @@ def print_startup_info():
48
49
  """
49
50
  )
50
51
 
51
- WIDTH = 48
52
-
53
52
  def print_setting(setting: str = "", value: Any = "", fillchar: str = " "):
54
53
  separator = ": " if value != "" else ""
55
- text = setting.ljust(22, fillchar) + separator + str(value)
56
- print("*", text.ljust(WIDTH - 4, fillchar), "*")
54
+ logger.info(setting + separator + str(value))
57
55
 
58
- print("Integration and Supervisory control".center(WIDTH, " "))
59
- print("of Autonomous Robots".center(WIDTH, " "))
60
- print()
61
- print(f"Version: {isar.__version__}\n".center(WIDTH, " "))
56
+ logger.info(
57
+ f"Integration and Supervisory control of Autonomous Robots - Version: {isar.__version__}\n"
58
+ )
62
59
 
63
- print_setting(fillchar="*")
64
60
  print_setting("ISAR settings")
65
- print_setting(fillchar="-")
66
61
  print_setting("Robot package", settings.ROBOT_PACKAGE)
67
62
  print_setting("Robot name", settings.ROBOT_NAME)
68
63
  print_setting("Running on port", settings.API_PORT)
69
64
  print_setting("Mission planner", settings.MISSION_PLANNER)
70
65
  print_setting("Using local storage", settings.STORAGE_LOCAL_ENABLED)
71
66
  print_setting("Using blob storage", settings.STORAGE_BLOB_ENABLED)
67
+ print_setting("Blob storage account", settings.BLOB_STORAGE_ACCOUNT)
72
68
  print_setting("Using async inspection uploading", settings.UPLOAD_INSPECTIONS_ASYNC)
73
69
  print_setting("Plant code", settings.PLANT_CODE)
74
70
  print_setting("Plant name", settings.PLANT_NAME)
75
71
  print_setting("Plant shortname", settings.PLANT_SHORT_NAME)
76
- print_setting(fillchar="-")
77
72
  print_setting("Robot capabilities", robot_settings.CAPABILITIES)
78
- print_setting(fillchar="*")
79
- print()
80
73
 
81
74
 
82
75
  def start() -> None:
83
76
  injector: ApplicationContainer = get_injector()
84
77
 
85
- keyvault = injector.keyvault()
86
- setup_loggers(keyvault=keyvault)
78
+ setup_loggers()
87
79
  setup_open_telemetry(app=injector.api().app)
88
80
  logger: Logger = logging.getLogger("main")
89
81