isar 1.25.9__py3-none-any.whl → 1.26.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 (42) hide show
  1. isar/apis/models/start_mission_definition.py +55 -108
  2. isar/apis/robot_control/robot_controller.py +5 -4
  3. isar/apis/schedule/scheduling_controller.py +4 -32
  4. isar/apis/security/authentication.py +2 -2
  5. isar/config/settings.env +1 -3
  6. isar/config/settings.py +5 -5
  7. isar/models/communication/message.py +0 -4
  8. isar/models/communication/queues/queue_utils.py +27 -0
  9. isar/models/communication/queues/queues.py +23 -5
  10. isar/robot/robot.py +91 -0
  11. isar/robot/robot_start_mission.py +73 -0
  12. isar/robot/robot_status.py +46 -0
  13. isar/robot/robot_task_status.py +92 -0
  14. isar/script.py +7 -0
  15. isar/services/utilities/scheduling_utilities.py +15 -26
  16. isar/state_machine/state_machine.py +94 -183
  17. isar/state_machine/states/blocked_protective_stop.py +7 -19
  18. isar/state_machine/states/idle.py +12 -54
  19. isar/state_machine/states/monitor.py +43 -90
  20. isar/state_machine/states/offline.py +8 -33
  21. isar/state_machine/states/paused.py +1 -1
  22. isar/state_machine/states_enum.py +0 -2
  23. isar/state_machine/transitions/fail_mission.py +13 -0
  24. isar/state_machine/transitions/finish_mission.py +39 -0
  25. isar/state_machine/transitions/pause.py +24 -0
  26. isar/state_machine/transitions/resume.py +27 -0
  27. isar/state_machine/transitions/start_mission.py +73 -0
  28. isar/state_machine/transitions/stop.py +33 -0
  29. isar/state_machine/transitions/utils.py +10 -0
  30. isar/storage/slimm_storage.py +2 -2
  31. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/METADATA +2 -3
  32. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/RECORD +40 -30
  33. robot_interface/models/mission/task.py +1 -1
  34. robot_interface/telemetry/mqtt_client.py +0 -1
  35. robot_interface/telemetry/payloads.py +3 -3
  36. robot_interface/utilities/json_service.py +1 -1
  37. isar/state_machine/states/initialize.py +0 -70
  38. isar/state_machine/states/initiate.py +0 -111
  39. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/LICENSE +0 -0
  40. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/WHEEL +0 -0
  41. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/entry_points.txt +0 -0
  42. {isar-1.25.9.dist-info → isar-1.26.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,46 @@
1
+ import logging
2
+ import time
3
+ from threading import Event, Thread
4
+
5
+ from isar.config.settings import settings
6
+ from isar.models.communication.queues.queue_utils import update_shared_state
7
+ from isar.models.communication.queues.queues import Queues
8
+ from robot_interface.robot_interface import RobotInterface
9
+
10
+
11
+ class RobotStatusThread(Thread):
12
+
13
+ def __init__(
14
+ self, queues: Queues, robot: RobotInterface, signal_thread_quitting: Event
15
+ ):
16
+ self.logger = logging.getLogger("robot")
17
+ self.queues: Queues = queues
18
+ self.robot: RobotInterface = robot
19
+ self.signal_thread_quitting: Event = signal_thread_quitting
20
+ self.last_robot_status_poll_time: float = time.time()
21
+ Thread.__init__(self, name="Robot status thread", daemon=True)
22
+
23
+ def stop(self) -> None:
24
+ return
25
+
26
+ def _is_ready_to_poll_for_status(self) -> bool:
27
+ time_since_last_robot_status_poll = (
28
+ time.time() - self.last_robot_status_poll_time
29
+ )
30
+ return (
31
+ time_since_last_robot_status_poll > settings.ROBOT_API_STATUS_POLL_INTERVAL
32
+ )
33
+
34
+ def run(self):
35
+ while True:
36
+ if self.signal_thread_quitting.is_set():
37
+ break
38
+
39
+ if not self._is_ready_to_poll_for_status():
40
+ continue
41
+
42
+ robot_status = self.robot.robot_status()
43
+
44
+ update_shared_state(self.queues.robot_status, robot_status)
45
+ self.last_robot_status_poll_time = time.time()
46
+ self.logger.info("Exiting robot status thread")
@@ -0,0 +1,92 @@
1
+ import logging
2
+ import time
3
+ from threading import Event, Thread
4
+ from typing import Optional
5
+
6
+ from isar.config.settings import settings
7
+ from isar.models.communication.queues.queue_utils import trigger_event
8
+ from isar.models.communication.queues.queues import Queues
9
+ from isar.services.utilities.threaded_request import ThreadedRequest
10
+ from robot_interface.models.exceptions.robot_exceptions import (
11
+ ErrorMessage,
12
+ RobotCommunicationException,
13
+ RobotCommunicationTimeoutException,
14
+ RobotException,
15
+ RobotTaskStatusException,
16
+ )
17
+ from robot_interface.models.mission.status import TaskStatus
18
+ from robot_interface.robot_interface import RobotInterface
19
+
20
+
21
+ class RobotTaskStatusThread(Thread):
22
+
23
+ def __init__(
24
+ self,
25
+ queues: Queues,
26
+ robot: RobotInterface,
27
+ signal_thread_quitting: Event,
28
+ task_id: str,
29
+ ):
30
+ self.logger = logging.getLogger("robot")
31
+ self.queues: Queues = queues
32
+ self.robot: RobotInterface = robot
33
+ self.start_mission_thread: Optional[ThreadedRequest] = None
34
+ self.signal_thread_quitting: Event = signal_thread_quitting
35
+ self.task_id: str = task_id
36
+ Thread.__init__(self, name="Robot task status thread")
37
+
38
+ def run(self):
39
+ task_status: TaskStatus = TaskStatus.NotStarted
40
+ failed_task_error: Optional[ErrorMessage] = None
41
+ request_status_failure_counter: int = 0
42
+
43
+ while (
44
+ request_status_failure_counter
45
+ < settings.REQUEST_STATUS_FAILURE_COUNTER_LIMIT
46
+ ):
47
+ if self.signal_thread_quitting.wait(0):
48
+ return
49
+ if request_status_failure_counter > 0:
50
+ time.sleep(settings.REQUEST_STATUS_COMMUNICATION_RECONNECT_DELAY)
51
+
52
+ try:
53
+ task_status = self.robot.task_status(self.task_id)
54
+ self.request_status_failure_counter = 0
55
+ except (
56
+ RobotCommunicationTimeoutException,
57
+ RobotCommunicationException,
58
+ ) as e:
59
+ self.request_status_failure_counter += 1
60
+ self.logger.error(
61
+ f"Failed to get task status "
62
+ f"{self.request_status_failure_counter} times because: "
63
+ f"{e.error_description}"
64
+ )
65
+
66
+ failed_task_error = ErrorMessage(
67
+ error_reason=e.error_reason,
68
+ error_description=e.error_description,
69
+ )
70
+ continue
71
+
72
+ except RobotTaskStatusException as e:
73
+ failed_task_error = ErrorMessage(
74
+ error_reason=e.error_reason,
75
+ error_description=e.error_description,
76
+ )
77
+ break
78
+
79
+ except RobotException as e:
80
+ failed_task_error = ErrorMessage(
81
+ error_reason=e.error_reason,
82
+ error_description=e.error_description,
83
+ )
84
+ break
85
+
86
+ trigger_event(self.queues.robot_task_status, task_status)
87
+ return
88
+
89
+ trigger_event(
90
+ self.queues.robot_task_status_failed,
91
+ failed_task_error,
92
+ )
isar/script.py CHANGED
@@ -14,6 +14,7 @@ from isar.config.log import setup_loggers
14
14
  from isar.config.settings import robot_settings, settings
15
15
  from isar.models.communication.queues.queues import Queues
16
16
  from isar.modules import get_injector
17
+ from isar.robot.robot import Robot
17
18
  from isar.services.service_connections.mqtt.mqtt_client import MqttClient
18
19
  from isar.services.service_connections.mqtt.robot_heartbeat_publisher import (
19
20
  RobotHeartbeatPublisher,
@@ -94,6 +95,7 @@ def start() -> None:
94
95
  uploader: Uploader = injector.get(Uploader)
95
96
  robot: RobotInterface = injector.get(RobotInterface)
96
97
  queues: Queues = injector.get(Queues)
98
+ robot_service: Robot = injector.get(Robot)
97
99
 
98
100
  threads: List[Thread] = []
99
101
 
@@ -107,6 +109,11 @@ def start() -> None:
107
109
  )
108
110
  threads.append(uploader_thread)
109
111
 
112
+ robot_service_thread: Thread = Thread(
113
+ target=robot_service.run, name="Robot service", daemon=True
114
+ )
115
+ threads.append(robot_service_thread)
116
+
110
117
  if settings.UPLOAD_INSPECTIONS_ASYNC:
111
118
 
112
119
  def inspections_callback(inspection: Inspection, mission: Mission):
@@ -2,9 +2,8 @@ import logging
2
2
  from copy import deepcopy
3
3
  from http import HTTPStatus
4
4
  from queue import Empty
5
- from typing import Any, List, Optional, Set
5
+ from typing import Any, List
6
6
 
7
- from alitra import Pose
8
7
  from fastapi import HTTPException
9
8
  from injector import inject
10
9
  from requests import HTTPError
@@ -18,10 +17,8 @@ from isar.mission_planner.mission_planner_interface import (
18
17
  )
19
18
  from isar.models.communication.message import StartMissionMessage
20
19
  from isar.models.communication.queues.queue_io import QueueIO
20
+ from isar.models.communication.queues.queue_timeout_error import QueueTimeoutError
21
21
  from isar.models.communication.queues.queues import Queues
22
- from isar.models.communication.queues.queue_timeout_error import (
23
- QueueTimeoutError,
24
- )
25
22
  from isar.services.utilities.queue_utilities import QueueUtilities
26
23
  from isar.state_machine.states_enum import States
27
24
  from robot_interface.models.mission.mission import Mission
@@ -102,14 +99,11 @@ class SchedulingUtilities:
102
99
  HTTPException 400 Bad request
103
100
  If the robot is not capable of performing mission
104
101
  """
105
- is_capable: bool = True
106
- missing_capabilities: Set[str] = set()
107
- for task in mission.tasks:
108
- if task.type not in robot_capabilities:
109
- is_capable = False
110
- missing_capabilities.add(task.type)
111
-
112
- if not is_capable:
102
+ missing_capabilities = {
103
+ task.type for task in mission.tasks if task.type not in robot_capabilities
104
+ }
105
+
106
+ if missing_capabilities:
113
107
  error_message = (
114
108
  f"Bad Request - Robot is not capable of performing mission."
115
109
  f" Missing functionalities: {missing_capabilities}."
@@ -120,7 +114,7 @@ class SchedulingUtilities:
120
114
  detail=error_message,
121
115
  )
122
116
 
123
- return is_capable
117
+ return True
124
118
 
125
119
  def verify_state_machine_ready_to_receive_mission(self, state: States) -> bool:
126
120
  """Verify that the state machine is idle and ready to receive a mission
@@ -130,18 +124,16 @@ class SchedulingUtilities:
130
124
  HTTPException 409 Conflict
131
125
  If state machine is not idle and therefore can not start a new mission
132
126
  """
133
- is_state_machine_ready_to_receive_mission = state == States.Idle
134
- if not is_state_machine_ready_to_receive_mission:
127
+ if state != States.Idle:
135
128
  error_message = f"Conflict - Robot is not idle - State: {state}"
136
129
  self.logger.warning(error_message)
137
130
  raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
138
131
 
139
- return is_state_machine_ready_to_receive_mission
132
+ return True
140
133
 
141
134
  def start_mission(
142
135
  self,
143
136
  mission: Mission,
144
- initial_pose: Optional[Pose],
145
137
  ) -> None:
146
138
  """Start mission
147
139
 
@@ -152,11 +144,8 @@ class SchedulingUtilities:
152
144
  """
153
145
  try:
154
146
  self._send_command(
155
- StartMissionMessage(
156
- mission=deepcopy(mission),
157
- initial_pose=initial_pose,
158
- ),
159
- self.queues.start_mission,
147
+ StartMissionMessage(mission=deepcopy(mission)),
148
+ self.queues.api_start_mission,
160
149
  )
161
150
  except QueueTimeoutError:
162
151
  error_message = "Internal Server Error - Failed to start mission in ISAR"
@@ -175,7 +164,7 @@ class SchedulingUtilities:
175
164
  If there is a timeout while communicating with the state machine
176
165
  """
177
166
  try:
178
- return self._send_command(True, self.queues.pause_mission)
167
+ return self._send_command(True, self.queues.api_pause_mission)
179
168
  except QueueTimeoutError:
180
169
  error_message = "Internal Server Error - Failed to pause mission"
181
170
  self.logger.error(error_message)
@@ -194,7 +183,7 @@ class SchedulingUtilities:
194
183
  If there is a timeout while communicating with the state machine
195
184
  """
196
185
  try:
197
- return self._send_command(True, self.queues.resume_mission)
186
+ return self._send_command(True, self.queues.api_resume_mission)
198
187
  except QueueTimeoutError:
199
188
  error_message = "Internal Server Error - Failed to resume mission"
200
189
  self.logger.error(error_message)
@@ -214,7 +203,7 @@ class SchedulingUtilities:
214
203
  """
215
204
  try:
216
205
  stop_mission_response: ControlMissionResponse = self._send_command(
217
- True, self.queues.stop_mission
206
+ True, self.queues.api_stop_mission
218
207
  )
219
208
  except QueueTimeoutError:
220
209
  error_message = "Internal Server Error - Failed to stop mission"