isar 1.25.9__py3-none-any.whl → 1.26.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 (48) hide show
  1. isar/apis/models/start_mission_definition.py +55 -112
  2. isar/apis/robot_control/robot_controller.py +5 -4
  3. isar/apis/schedule/scheduling_controller.py +6 -57
  4. isar/apis/security/authentication.py +2 -2
  5. isar/config/settings.env +1 -3
  6. isar/config/settings.py +6 -6
  7. isar/models/communication/message.py +0 -4
  8. isar/models/communication/queues/events.py +57 -0
  9. isar/models/communication/queues/queue_utils.py +32 -0
  10. isar/models/communication/queues/status_queue.py +7 -5
  11. isar/modules.py +26 -13
  12. isar/robot/robot.py +124 -0
  13. isar/robot/robot_start_mission.py +73 -0
  14. isar/robot/robot_status.py +49 -0
  15. isar/robot/robot_stop_mission.py +72 -0
  16. isar/robot/robot_task_status.py +92 -0
  17. isar/script.py +14 -7
  18. isar/services/utilities/scheduling_utilities.py +21 -30
  19. isar/state_machine/state_machine.py +70 -212
  20. isar/state_machine/states/blocked_protective_stop.py +10 -30
  21. isar/state_machine/states/idle.py +45 -67
  22. isar/state_machine/states/monitor.py +129 -139
  23. isar/state_machine/states/offline.py +12 -33
  24. isar/state_machine/states/paused.py +6 -3
  25. isar/state_machine/states/stop.py +29 -58
  26. isar/state_machine/states_enum.py +0 -2
  27. isar/state_machine/transitions/fail_mission.py +13 -0
  28. isar/state_machine/transitions/finish_mission.py +39 -0
  29. isar/state_machine/transitions/pause.py +24 -0
  30. isar/state_machine/transitions/resume.py +27 -0
  31. isar/state_machine/transitions/start_mission.py +73 -0
  32. isar/state_machine/transitions/stop.py +40 -0
  33. isar/state_machine/transitions/utils.py +10 -0
  34. isar/storage/slimm_storage.py +2 -2
  35. isar/storage/uploader.py +5 -5
  36. {isar-1.25.9.dist-info → isar-1.26.1.dist-info}/METADATA +5 -19
  37. {isar-1.25.9.dist-info → isar-1.26.1.dist-info}/RECORD +45 -34
  38. {isar-1.25.9.dist-info → isar-1.26.1.dist-info}/WHEEL +1 -1
  39. robot_interface/models/mission/task.py +1 -1
  40. robot_interface/telemetry/mqtt_client.py +0 -1
  41. robot_interface/telemetry/payloads.py +3 -3
  42. robot_interface/utilities/json_service.py +1 -1
  43. isar/models/communication/queues/queues.py +0 -19
  44. isar/state_machine/states/initialize.py +0 -70
  45. isar/state_machine/states/initiate.py +0 -111
  46. {isar-1.25.9.dist-info → isar-1.26.1.dist-info}/entry_points.txt +0 -0
  47. {isar-1.25.9.dist-info → isar-1.26.1.dist-info/licenses}/LICENSE +0 -0
  48. {isar-1.25.9.dist-info → isar-1.26.1.dist-info}/top_level.txt +0 -0
@@ -12,13 +12,14 @@ from robot_interface.models.mission.task import (
12
12
  TASKS,
13
13
  RecordAudio,
14
14
  ReturnToHome,
15
+ TakeGasMeasurement,
15
16
  TakeImage,
16
17
  TakeThermalImage,
17
18
  TakeThermalVideo,
18
19
  TakeVideo,
19
- TakeGasMeasurement,
20
20
  ZoomDescription,
21
21
  )
22
+ from robot_interface.utilities.uuid_string_factory import uuid4_string
22
23
 
23
24
 
24
25
  class InspectionTypes(str, Enum):
@@ -60,7 +61,6 @@ class StartMissionDefinition(BaseModel):
60
61
 
61
62
  def to_isar_mission(
62
63
  start_mission_definition: StartMissionDefinition,
63
- return_pose: Optional[InputPose] = None,
64
64
  ) -> Mission:
65
65
  isar_tasks: List[TASKS] = []
66
66
 
@@ -68,17 +68,14 @@ def to_isar_mission(
68
68
  task: TASKS = to_isar_task(task_definition)
69
69
  isar_tasks.append(task)
70
70
 
71
- if return_pose:
72
- isar_tasks.append(ReturnToHome(pose=return_pose.to_alitra_pose()))
73
-
74
71
  if not isar_tasks:
75
72
  raise MissionPlannerError("Mission does not contain any valid tasks")
76
73
 
77
- isar_mission_name: str
78
- if start_mission_definition.name:
79
- isar_mission_name = start_mission_definition.name
80
- else:
81
- isar_mission_name = _build_mission_name()
74
+ isar_mission_name: str = (
75
+ start_mission_definition.name
76
+ if start_mission_definition.name
77
+ else _build_mission_name()
78
+ )
82
79
 
83
80
  start_pose = None
84
81
  if start_mission_definition.start_pose:
@@ -106,113 +103,59 @@ def to_inspection_task(task_definition: StartMissionTaskDefinition) -> TASKS:
106
103
  inspection_definition = task_definition.inspection
107
104
 
108
105
  if inspection_definition.type == InspectionTypes.image:
109
- if task_definition.id:
110
- return TakeImage(
111
- id=task_definition.id,
112
- robot_pose=task_definition.pose.to_alitra_pose(),
113
- tag_id=task_definition.tag,
114
- target=task_definition.inspection.inspection_target.to_alitra_position(),
115
- metadata=task_definition.inspection.metadata,
116
- zoom=task_definition.zoom,
117
- )
118
- else:
119
- return TakeImage(
120
- robot_pose=task_definition.pose.to_alitra_pose(),
121
- tag_id=task_definition.tag,
122
- target=task_definition.inspection.inspection_target.to_alitra_position(),
123
- metadata=task_definition.inspection.metadata,
124
- zoom=task_definition.zoom,
125
- )
106
+ return TakeImage(
107
+ id=task_definition.id if task_definition.id else uuid4_string(),
108
+ robot_pose=task_definition.pose.to_alitra_pose(),
109
+ tag_id=task_definition.tag,
110
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
111
+ metadata=task_definition.inspection.metadata,
112
+ zoom=task_definition.zoom,
113
+ )
126
114
  elif inspection_definition.type == InspectionTypes.video:
127
- if task_definition.id:
128
- return TakeVideo(
129
- id=task_definition.id,
130
- robot_pose=task_definition.pose.to_alitra_pose(),
131
- tag_id=task_definition.tag,
132
- target=task_definition.inspection.inspection_target.to_alitra_position(),
133
- duration=inspection_definition.duration,
134
- metadata=task_definition.inspection.metadata,
135
- zoom=task_definition.zoom,
136
- )
137
- else:
138
- return TakeVideo(
139
- robot_pose=task_definition.pose.to_alitra_pose(),
140
- tag_id=task_definition.tag,
141
- target=task_definition.inspection.inspection_target.to_alitra_position(),
142
- duration=inspection_definition.duration,
143
- metadata=task_definition.inspection.metadata,
144
- zoom=task_definition.zoom,
145
- )
115
+ return TakeVideo(
116
+ id=task_definition.id if task_definition.id else uuid4_string(),
117
+ robot_pose=task_definition.pose.to_alitra_pose(),
118
+ tag_id=task_definition.tag,
119
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
120
+ duration=inspection_definition.duration,
121
+ metadata=task_definition.inspection.metadata,
122
+ zoom=task_definition.zoom,
123
+ )
146
124
  elif inspection_definition.type == InspectionTypes.thermal_image:
147
- if task_definition.id:
148
- return TakeThermalImage(
149
- id=task_definition.id,
150
- robot_pose=task_definition.pose.to_alitra_pose(),
151
- tag_id=task_definition.tag,
152
- target=task_definition.inspection.inspection_target.to_alitra_position(),
153
- metadata=task_definition.inspection.metadata,
154
- zoom=task_definition.zoom,
155
- )
156
- else:
157
- return TakeThermalImage(
158
- robot_pose=task_definition.pose.to_alitra_pose(),
159
- tag_id=task_definition.tag,
160
- target=task_definition.inspection.inspection_target.to_alitra_position(),
161
- metadata=task_definition.inspection.metadata,
162
- zoom=task_definition.zoom,
163
- )
125
+ return TakeThermalImage(
126
+ id=task_definition.id if task_definition.id else uuid4_string(),
127
+ robot_pose=task_definition.pose.to_alitra_pose(),
128
+ tag_id=task_definition.tag,
129
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
130
+ metadata=task_definition.inspection.metadata,
131
+ zoom=task_definition.zoom,
132
+ )
164
133
  elif inspection_definition.type == InspectionTypes.thermal_video:
165
- if task_definition.id:
166
- return TakeThermalVideo(
167
- id=task_definition.id,
168
- robot_pose=task_definition.pose.to_alitra_pose(),
169
- tag_id=task_definition.tag,
170
- target=task_definition.inspection.inspection_target.to_alitra_position(),
171
- duration=inspection_definition.duration,
172
- metadata=task_definition.inspection.metadata,
173
- zoom=task_definition.zoom,
174
- )
175
- else:
176
- return TakeThermalVideo(
177
- robot_pose=task_definition.pose.to_alitra_pose(),
178
- tag_id=task_definition.tag,
179
- target=task_definition.inspection.inspection_target.to_alitra_position(),
180
- duration=inspection_definition.duration,
181
- metadata=task_definition.inspection.metadata,
182
- zoom=task_definition.zoom,
183
- )
134
+ return TakeThermalVideo(
135
+ id=task_definition.id if task_definition.id else uuid4_string(),
136
+ robot_pose=task_definition.pose.to_alitra_pose(),
137
+ tag_id=task_definition.tag,
138
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
139
+ duration=inspection_definition.duration,
140
+ metadata=task_definition.inspection.metadata,
141
+ zoom=task_definition.zoom,
142
+ )
184
143
  elif inspection_definition.type == InspectionTypes.audio:
185
- if task_definition.id:
186
- return RecordAudio(
187
- id=task_definition.id,
188
- robot_pose=task_definition.pose.to_alitra_pose(),
189
- tag_id=task_definition.tag,
190
- target=task_definition.inspection.inspection_target.to_alitra_position(),
191
- duration=inspection_definition.duration,
192
- metadata=task_definition.inspection.metadata,
193
- )
194
- else:
195
- return RecordAudio(
196
- robot_pose=task_definition.pose.to_alitra_pose(),
197
- tag_id=task_definition.tag,
198
- target=task_definition.inspection.inspection_target.to_alitra_position(),
199
- duration=inspection_definition.duration,
200
- metadata=task_definition.inspection.metadata,
201
- )
144
+ return RecordAudio(
145
+ id=task_definition.id if task_definition.id else uuid4_string(),
146
+ robot_pose=task_definition.pose.to_alitra_pose(),
147
+ tag_id=task_definition.tag,
148
+ target=task_definition.inspection.inspection_target.to_alitra_position(),
149
+ duration=inspection_definition.duration,
150
+ metadata=task_definition.inspection.metadata,
151
+ )
202
152
  elif inspection_definition.type == InspectionTypes.gas_measurement:
203
- if task_definition.id:
204
- return TakeGasMeasurement(
205
- id=task_definition.id,
206
- robot_pose=task_definition.pose.to_alitra_pose(),
207
- tag_id=task_definition.tag,
208
- metadata=task_definition.inspection.metadata,
209
- )
210
- else:
211
- return TakeGasMeasurement(
212
- robot_pose=task_definition.pose.to_alitra_pose(),
213
- tag_id=task_definition.tag,
214
- metadata=task_definition.inspection.metadata,
215
- )
153
+ return TakeGasMeasurement(
154
+ id=task_definition.id if task_definition.id else uuid4_string(),
155
+ robot_pose=task_definition.pose.to_alitra_pose(),
156
+ tag_id=task_definition.tag,
157
+ metadata=task_definition.inspection.metadata,
158
+ )
216
159
  else:
217
160
  raise ValueError(
218
161
  f"Inspection type '{inspection_definition.type}' not supported"
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
 
3
+ from fastapi import HTTPException
3
4
  from injector import inject
4
5
 
5
6
  from isar.apis.models.models import RobotInfoResponse
6
7
  from isar.config.settings import robot_settings, settings
7
8
  from isar.services.utilities.robot_utilities import RobotUtilities
8
- from fastapi import HTTPException
9
+ from robot_interface.models.robots.media import MediaConfig
9
10
 
10
11
 
11
12
  class RobotController:
@@ -17,8 +18,8 @@ class RobotController:
17
18
  self.robot_utilities: RobotUtilities = robot_utilities
18
19
  self.logger = logging.getLogger("api")
19
20
 
20
- def generate_media_config(self):
21
- media_config = self.robot_utilities.generate_media_config()
21
+ def generate_media_config(self) -> MediaConfig:
22
+ media_config: MediaConfig = self.robot_utilities.generate_media_config()
22
23
  if media_config is None:
23
24
  raise HTTPException(
24
25
  status_code=204,
@@ -26,7 +27,7 @@ class RobotController:
26
27
  )
27
28
  return media_config
28
29
 
29
- def get_info(self):
30
+ def get_info(self) -> RobotInfoResponse:
30
31
  return RobotInfoResponse(
31
32
  robot_package=settings.ROBOT_PACKAGE,
32
33
  isar_id=settings.ISAR_ID,
@@ -1,16 +1,13 @@
1
1
  import logging
2
2
  from http import HTTPStatus
3
- from typing import Optional
4
3
 
5
- from alitra import Pose
6
4
  from fastapi import Body, HTTPException, Path
7
5
  from injector import inject
8
6
 
9
7
  from isar.apis.models.models import (
10
8
  ControlMissionResponse,
11
- TaskResponse,
12
- InputPose,
13
9
  StartMissionResponse,
10
+ TaskResponse,
14
11
  )
15
12
  from isar.apis.models.start_mission_definition import (
16
13
  StartMissionDefinition,
@@ -21,12 +18,7 @@ from isar.mission_planner.mission_planner_interface import MissionPlannerError
21
18
  from isar.services.utilities.scheduling_utilities import SchedulingUtilities
22
19
  from isar.state_machine.states_enum import States
23
20
  from robot_interface.models.mission.mission import Mission
24
- from robot_interface.models.mission.task import (
25
- TASKS,
26
- InspectionTask,
27
- MoveArm,
28
- ReturnToHome,
29
- )
21
+ from robot_interface.models.mission.task import TASKS, InspectionTask, MoveArm
30
22
 
31
23
 
32
24
  class SchedulingController:
@@ -45,18 +37,6 @@ class SchedulingController:
45
37
  title="Mission ID",
46
38
  description="ID-number for predefined mission",
47
39
  ),
48
- initial_pose: Optional[InputPose] = Body(
49
- default=None,
50
- description="The starting point of the mission. Used for initial "
51
- "localization of robot",
52
- embed=True,
53
- ),
54
- return_pose: Optional[InputPose] = Body(
55
- default=None,
56
- description="End pose of the mission. The robot return to the specified "
57
- "pose after finishing all inspections",
58
- embed=True,
59
- ),
60
40
  ) -> StartMissionResponse:
61
41
  self.logger.info(f"Received request to start mission with id {mission_id}")
62
42
 
@@ -64,23 +44,14 @@ class SchedulingController:
64
44
  self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
65
45
 
66
46
  mission: Mission = self.scheduling_utilities.get_mission(mission_id)
67
- if return_pose:
68
- pose: Pose = return_pose.to_alitra_pose()
69
- mission.tasks.append(ReturnToHome(pose=pose))
70
47
 
71
48
  self.scheduling_utilities.verify_robot_capable_of_mission(
72
49
  mission=mission, robot_capabilities=robot_settings.CAPABILITIES
73
50
  )
74
51
 
75
- initial_pose_alitra: Optional[Pose] = (
76
- initial_pose.to_alitra_pose() if initial_pose else None
77
- )
78
-
79
52
  self.logger.info(f"Starting mission with ISAR Mission ID: '{mission.id}'")
80
53
 
81
- self.scheduling_utilities.start_mission(
82
- mission=mission, initial_pose=initial_pose_alitra
83
- )
54
+ self.scheduling_utilities.start_mission(mission=mission)
84
55
 
85
56
  return self._api_response(mission)
86
57
 
@@ -92,18 +63,6 @@ class SchedulingController:
92
63
  title="Mission Definition",
93
64
  description="Description of the mission in json format",
94
65
  ),
95
- initial_pose: Optional[InputPose] = Body(
96
- default=None,
97
- description="The starting point of the mission. Used for initial "
98
- "localization of robot",
99
- embed=True,
100
- ),
101
- return_pose: Optional[InputPose] = Body(
102
- default=None,
103
- description="End pose of the mission. The robot return to the specified "
104
- "pose after finishing all inspections",
105
- embed=True,
106
- ),
107
66
  ) -> StartMissionResponse:
108
67
  self.logger.info("Received request to start new mission")
109
68
 
@@ -121,7 +80,7 @@ class SchedulingController:
121
80
 
122
81
  try:
123
82
  mission: Mission = to_isar_mission(
124
- start_mission_definition=mission_definition, return_pose=return_pose
83
+ start_mission_definition=mission_definition
125
84
  )
126
85
  except MissionPlannerError as e:
127
86
  error_message = f"Bad Request - Cannot create ISAR mission: {e}"
@@ -135,14 +94,8 @@ class SchedulingController:
135
94
  mission=mission, robot_capabilities=robot_settings.CAPABILITIES
136
95
  )
137
96
 
138
- initial_pose_alitra: Optional[Pose] = (
139
- initial_pose.to_alitra_pose() if initial_pose else None
140
- )
141
-
142
97
  self.logger.info(f"Starting mission: {mission.id}")
143
- self.scheduling_utilities.start_mission(
144
- mission=mission, initial_pose=initial_pose_alitra
145
- )
98
+ self.scheduling_utilities.start_mission(mission=mission)
146
99
  return self._api_response(mission)
147
100
 
148
101
  def pause_mission(self) -> ControlMissionResponse:
@@ -152,7 +105,6 @@ class SchedulingController:
152
105
 
153
106
  if state not in [
154
107
  States.Monitor,
155
- States.Initiate,
156
108
  ]:
157
109
  error_message = (
158
110
  f"Conflict - Pause command received in invalid state - State: {state}"
@@ -245,10 +197,7 @@ class SchedulingController:
245
197
  self.logger.info(
246
198
  f"Starting move arm mission with ISAR Mission ID: '{mission.id}'"
247
199
  )
248
- self.scheduling_utilities.start_mission(
249
- mission=mission,
250
- initial_pose=None,
251
- )
200
+ self.scheduling_utilities.start_mission(mission=mission)
252
201
  return self._api_response(mission)
253
202
 
254
203
  def _api_response(self, mission: Mission) -> StartMissionResponse:
@@ -50,7 +50,7 @@ class Authenticator:
50
50
  enabled_string = "enabled" if self.authentication_enabled else "disabled"
51
51
  self.logger.info(f"API authentication is {enabled_string}")
52
52
 
53
- def should_authenticate(self):
53
+ def should_authenticate(self) -> bool:
54
54
  return self.authentication_enabled
55
55
 
56
56
  def get_scheme(self):
@@ -58,7 +58,7 @@ class Authenticator:
58
58
  return validate_has_role
59
59
  return NoSecurity
60
60
 
61
- async def load_config(self):
61
+ async def load_config(self) -> None:
62
62
  """
63
63
  Load OpenID config on startup.
64
64
  """
isar/config/settings.env CHANGED
@@ -1,7 +1,5 @@
1
1
  ISAR_ROBOT_PACKAGE = isar_robot
2
2
 
3
- ISAR_RUN_MISSION_BY_TASK = true
4
-
5
3
  ISAR_STORAGE_LOCAL_ENABLED = true
6
4
  ISAR_STORAGE_BLOB_ENABLED = false
7
5
  ISAR_STORAGE_SLIMM_ENABLED = false
@@ -12,7 +10,7 @@ ISAR_LOG_HANDLER_APPLICATION_INSIGHTS_ENABLED = false
12
10
  ISAR_MQTT_ENABLED = true
13
11
  ISAR_MQTT_SSL_ENABLED = true
14
12
 
15
- ISAR_AUTHENTICATION_ENABLED = false
13
+ ISAR_AUTHENTICATION_ENABLED = true
16
14
 
17
15
  ISAR_PLANT_SHORT_NAME = HUA
18
16
 
isar/config/settings.py CHANGED
@@ -51,7 +51,7 @@ class Settings(BaseSettings):
51
51
  # Determines the number of state transitions that are kept in the log
52
52
  STATE_TRANSITIONS_LOG_LENGTH: int = Field(default=20)
53
53
 
54
- # Number of attempts to initiate a task or mission before cancelling
54
+ # Number of attempts to initiate a mission before cancelling
55
55
  INITIATE_FAILURE_COUNTER_LIMIT: int = Field(default=10)
56
56
 
57
57
  # Number of attempts to request a task status in monitor before cancelling
@@ -62,7 +62,7 @@ class Settings(BaseSettings):
62
62
  REQUEST_STATUS_COMMUNICATION_RECONNECT_DELAY: float = Field(default=10)
63
63
 
64
64
  # Number of attempts to stop the robot before giving up
65
- STOP_ROBOT_ATTEMPTS_LIMIT: int = Field(default=10)
65
+ STOP_ROBOT_ATTEMPTS_LIMIT: int = Field(default=3)
66
66
 
67
67
  # Number of attempts to stop the robot before giving up
68
68
  UPLOAD_FAILURE_ATTEMPTS_LIMIT: int = Field(default=10)
@@ -111,7 +111,7 @@ class Settings(BaseSettings):
111
111
  # Enabling this requires certain resources available for OAuth2 authentication
112
112
  # Currently supported authentication is Azure AD
113
113
  # (https://github.com/Intility/fastapi-azure-auth)
114
- AUTHENTICATION_ENABLED: bool = Field(default=False)
114
+ AUTHENTICATION_ENABLED: bool = Field(default=True)
115
115
 
116
116
  # Tenant ID for the Azure tenant with your Azure Active Directory application
117
117
  AZURE_TENANT_ID: str = Field(default="3aa4a235-b6e2-48d5-9195-7fcf05b459b0")
@@ -169,13 +169,13 @@ class Settings(BaseSettings):
169
169
  # submitted with the results once they have been uploaded.
170
170
 
171
171
  # Four digit code indicating facility
172
- PLANT_CODE: str = Field(default="1320")
172
+ PLANT_CODE: str = Field(default="1210")
173
173
 
174
174
  # Name of the facility the robot is operating in
175
- PLANT_NAME: str = Field(default="Kårstø")
175
+ PLANT_NAME: str = Field(default="Huldra")
176
176
 
177
177
  # Shortname of the facility the robot is operating in
178
- PLANT_SHORT_NAME: str = Field(default="KAA")
178
+ PLANT_SHORT_NAME: str = Field(default="HUA")
179
179
 
180
180
  # Country the robot is operating in
181
181
  COUNTRY: str = Field(default="Norway")
@@ -1,7 +1,4 @@
1
1
  from dataclasses import dataclass
2
- from typing import Optional
3
-
4
- from alitra import Pose
5
2
 
6
3
  from robot_interface.models.mission.mission import Mission
7
4
 
@@ -9,4 +6,3 @@ from robot_interface.models.mission.mission import Mission
9
6
  @dataclass
10
7
  class StartMissionMessage:
11
8
  mission: Mission
12
- initial_pose: Optional[Pose]
@@ -0,0 +1,57 @@
1
+ from queue import Queue
2
+
3
+ from transitions import State
4
+
5
+ from isar.config.settings import settings
6
+ from isar.models.communication.queues.queue_io import QueueIO
7
+ from isar.models.communication.queues.status_queue import StatusQueue
8
+ from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
9
+ from robot_interface.models.mission.mission import Mission
10
+ from robot_interface.models.mission.status import RobotStatus, TaskStatus
11
+ from robot_interface.models.mission.task import TASKS
12
+
13
+
14
+ class Events:
15
+ def __init__(self) -> None:
16
+ self.api_requests: APIRequests = APIRequests()
17
+ self.state_machine_events: StateMachineEvents = StateMachineEvents()
18
+ self.robot_service_events: RobotServiceEvents = RobotServiceEvents()
19
+
20
+ self.upload_queue: Queue = Queue(maxsize=10)
21
+
22
+ if settings.MQTT_ENABLED:
23
+ self.mqtt_queue: Queue = Queue()
24
+
25
+
26
+ class APIRequests:
27
+ def __init__(self) -> None:
28
+ self.start_mission: QueueIO = QueueIO(input_size=1, output_size=1)
29
+ self.stop_mission: QueueIO = QueueIO(input_size=1, output_size=1)
30
+ self.pause_mission: QueueIO = QueueIO(input_size=1, output_size=1)
31
+ self.resume_mission: QueueIO = QueueIO(input_size=1, output_size=1)
32
+
33
+
34
+ class StateMachineEvents:
35
+ def __init__(self) -> None:
36
+ self.start_mission: Queue[Mission] = Queue(maxsize=1)
37
+ self.stop_mission: Queue[bool] = Queue(maxsize=1)
38
+ self.pause_mission: Queue[bool] = Queue(maxsize=1)
39
+ self.task_status_request: Queue[str] = Queue(maxsize=1)
40
+
41
+
42
+ class RobotServiceEvents:
43
+ def __init__(self) -> None:
44
+ self.task_status_updated: Queue[TaskStatus] = Queue(maxsize=1)
45
+ self.task_status_failed: Queue[ErrorMessage] = Queue(maxsize=1)
46
+ self.mission_started: Queue[bool] = Queue(maxsize=1)
47
+ self.mission_failed: Queue[ErrorMessage] = Queue(maxsize=1)
48
+ self.robot_status_changed: Queue[bool] = Queue(maxsize=1)
49
+ self.mission_failed_to_stop: Queue[ErrorMessage] = Queue(maxsize=1)
50
+ self.mission_successfully_stopped: Queue[bool] = Queue(maxsize=1)
51
+
52
+
53
+ class SharedState:
54
+ def __init__(self) -> None:
55
+ self.state: StatusQueue[State] = StatusQueue()
56
+ self.robot_status: StatusQueue[RobotStatus] = StatusQueue()
57
+ self.state_machine_current_task: StatusQueue[TASKS] = StatusQueue()
@@ -0,0 +1,32 @@
1
+ from queue import Empty, Queue
2
+ from typing import Optional, TypeVar
3
+
4
+ from isar.models.communication.queues.status_queue import StatusQueue
5
+
6
+ T = TypeVar("T")
7
+
8
+
9
+ def trigger_event_without_data(queue: Queue[bool]) -> None:
10
+ queue.put(True)
11
+
12
+
13
+ def trigger_event(queue: Queue[T], data: T) -> None:
14
+ queue.put(data)
15
+
16
+
17
+ def check_shared_state(queue: StatusQueue[T]) -> Optional[T]:
18
+ try:
19
+ return queue.check()
20
+ except Empty:
21
+ return None
22
+
23
+
24
+ def update_shared_state(queue: StatusQueue[T], data: T) -> None:
25
+ queue.update(data)
26
+
27
+
28
+ def check_for_event(queue: Queue[T]) -> Optional[T]:
29
+ try:
30
+ return queue.get(block=False)
31
+ except Empty:
32
+ return None
@@ -1,20 +1,22 @@
1
1
  from collections import deque
2
2
  from queue import Empty, Queue
3
- from typing import Any
3
+ from typing import TypeVar
4
4
 
5
+ T = TypeVar("T")
5
6
 
6
- class StatusQueue(Queue):
7
+
8
+ class StatusQueue(Queue[T]):
7
9
  def __init__(self) -> None:
8
10
  super().__init__()
9
11
 
10
- def check(self) -> Any:
12
+ def check(self) -> T:
11
13
  if not self._qsize():
12
14
  raise Empty
13
15
  with self.mutex:
14
16
  queueList = list(self.queue)
15
17
  return queueList.pop()
16
18
 
17
- def update(self, item: Any):
19
+ def update(self, item: T):
18
20
  with self.mutex:
19
- self.queue = deque()
21
+ self.queue: deque[T] = deque()
20
22
  self.queue.append(item)