isar 1.20.2__py3-none-any.whl → 1.34.13__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.
Files changed (134) hide show
  1. isar/apis/api.py +135 -86
  2. isar/apis/models/__init__.py +0 -1
  3. isar/apis/models/models.py +21 -11
  4. isar/apis/models/start_mission_definition.py +115 -170
  5. isar/apis/robot_control/robot_controller.py +41 -0
  6. isar/apis/schedule/scheduling_controller.py +123 -187
  7. isar/apis/security/authentication.py +5 -5
  8. isar/config/certs/ca-cert.pem +33 -31
  9. isar/config/keyvault/keyvault_service.py +4 -2
  10. isar/config/log.py +45 -40
  11. isar/config/logging.conf +16 -31
  12. isar/config/open_telemetry.py +102 -0
  13. isar/config/settings.py +74 -117
  14. isar/eventhandlers/eventhandler.py +123 -0
  15. isar/models/events.py +184 -0
  16. isar/models/status.py +22 -0
  17. isar/modules.py +117 -200
  18. isar/robot/robot.py +383 -0
  19. isar/robot/robot_battery.py +60 -0
  20. isar/robot/robot_monitor_mission.py +357 -0
  21. isar/robot/robot_pause_mission.py +74 -0
  22. isar/robot/robot_resume_mission.py +67 -0
  23. isar/robot/robot_start_mission.py +66 -0
  24. isar/robot/robot_status.py +61 -0
  25. isar/robot/robot_stop_mission.py +68 -0
  26. isar/robot/robot_upload_inspection.py +75 -0
  27. isar/script.py +58 -41
  28. isar/services/service_connections/mqtt/mqtt_client.py +47 -11
  29. isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +5 -2
  30. isar/services/service_connections/mqtt/robot_info_publisher.py +3 -3
  31. isar/services/service_connections/persistent_memory.py +69 -0
  32. isar/services/utilities/mqtt_utilities.py +93 -0
  33. isar/services/utilities/robot_utilities.py +20 -0
  34. isar/services/utilities/scheduling_utilities.py +386 -100
  35. isar/state_machine/state_machine.py +242 -539
  36. isar/state_machine/states/__init__.py +0 -8
  37. isar/state_machine/states/await_next_mission.py +114 -0
  38. isar/state_machine/states/blocked_protective_stop.py +60 -0
  39. isar/state_machine/states/going_to_lockdown.py +95 -0
  40. isar/state_machine/states/going_to_recharging.py +92 -0
  41. isar/state_machine/states/home.py +115 -0
  42. isar/state_machine/states/intervention_needed.py +77 -0
  43. isar/state_machine/states/lockdown.py +38 -0
  44. isar/state_machine/states/maintenance.py +43 -0
  45. isar/state_machine/states/monitor.py +137 -247
  46. isar/state_machine/states/offline.py +51 -53
  47. isar/state_machine/states/paused.py +92 -23
  48. isar/state_machine/states/pausing.py +48 -0
  49. isar/state_machine/states/pausing_return_home.py +48 -0
  50. isar/state_machine/states/recharging.py +80 -0
  51. isar/state_machine/states/resuming.py +57 -0
  52. isar/state_machine/states/resuming_return_home.py +64 -0
  53. isar/state_machine/states/return_home_paused.py +109 -0
  54. isar/state_machine/states/returning_home.py +217 -0
  55. isar/state_machine/states/stopping.py +69 -0
  56. isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
  57. isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
  58. isar/state_machine/states/stopping_go_to_recharge.py +51 -0
  59. isar/state_machine/states/stopping_paused_mission.py +36 -0
  60. isar/state_machine/states/stopping_paused_return_home.py +59 -0
  61. isar/state_machine/states/stopping_return_home.py +59 -0
  62. isar/state_machine/states/unknown_status.py +74 -0
  63. isar/state_machine/states_enum.py +23 -5
  64. isar/state_machine/transitions/mission.py +225 -0
  65. isar/state_machine/transitions/return_home.py +108 -0
  66. isar/state_machine/transitions/robot_status.py +87 -0
  67. isar/state_machine/utils/common_event_handlers.py +138 -0
  68. isar/storage/blob_storage.py +70 -52
  69. isar/storage/local_storage.py +25 -12
  70. isar/storage/storage_interface.py +28 -7
  71. isar/storage/uploader.py +174 -55
  72. isar/storage/utilities.py +32 -29
  73. {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/METADATA +119 -123
  74. isar-1.34.13.dist-info/RECORD +120 -0
  75. {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/WHEEL +1 -1
  76. {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/entry_points.txt +1 -0
  77. robot_interface/models/exceptions/robot_exceptions.py +91 -41
  78. robot_interface/models/inspection/__init__.py +0 -13
  79. robot_interface/models/inspection/inspection.py +42 -33
  80. robot_interface/models/mission/mission.py +14 -15
  81. robot_interface/models/mission/status.py +20 -26
  82. robot_interface/models/mission/task.py +154 -121
  83. robot_interface/models/robots/battery_state.py +6 -0
  84. robot_interface/models/robots/media.py +13 -0
  85. robot_interface/models/robots/robot_model.py +7 -7
  86. robot_interface/robot_interface.py +119 -84
  87. robot_interface/telemetry/mqtt_client.py +74 -12
  88. robot_interface/telemetry/payloads.py +91 -13
  89. robot_interface/utilities/json_service.py +7 -1
  90. isar/config/configuration_error.py +0 -2
  91. isar/config/keyvault/keyvault_error.py +0 -2
  92. isar/config/predefined_mission_definition/__init__.py +0 -0
  93. isar/config/predefined_mission_definition/default_exr.json +0 -51
  94. isar/config/predefined_mission_definition/default_mission.json +0 -91
  95. isar/config/predefined_mission_definition/default_turtlebot.json +0 -124
  96. isar/config/predefined_missions/__init__.py +0 -0
  97. isar/config/predefined_missions/default.json +0 -92
  98. isar/config/predefined_missions/default_turtlebot.json +0 -110
  99. isar/config/predefined_poses/__init__.py +0 -0
  100. isar/config/predefined_poses/predefined_poses.py +0 -616
  101. isar/config/settings.env +0 -25
  102. isar/mission_planner/__init__.py +0 -0
  103. isar/mission_planner/local_planner.py +0 -82
  104. isar/mission_planner/mission_planner_interface.py +0 -26
  105. isar/mission_planner/sequential_task_selector.py +0 -23
  106. isar/mission_planner/task_selector_interface.py +0 -31
  107. isar/models/communication/__init__.py +0 -0
  108. isar/models/communication/message.py +0 -12
  109. isar/models/communication/queues/__init__.py +0 -4
  110. isar/models/communication/queues/queue_io.py +0 -12
  111. isar/models/communication/queues/queue_timeout_error.py +0 -2
  112. isar/models/communication/queues/queues.py +0 -19
  113. isar/models/communication/queues/status_queue.py +0 -20
  114. isar/models/mission_metadata/__init__.py +0 -0
  115. isar/services/auth/__init__.py +0 -0
  116. isar/services/auth/azure_credentials.py +0 -14
  117. isar/services/readers/__init__.py +0 -0
  118. isar/services/readers/base_reader.py +0 -37
  119. isar/services/service_connections/request_handler.py +0 -153
  120. isar/services/service_connections/stid/__init__.py +0 -0
  121. isar/services/utilities/queue_utilities.py +0 -39
  122. isar/services/utilities/threaded_request.py +0 -68
  123. isar/state_machine/states/idle.py +0 -85
  124. isar/state_machine/states/initialize.py +0 -71
  125. isar/state_machine/states/initiate.py +0 -142
  126. isar/state_machine/states/off.py +0 -18
  127. isar/state_machine/states/stop.py +0 -95
  128. isar/storage/slimm_storage.py +0 -191
  129. isar-1.20.2.dist-info/RECORD +0 -116
  130. robot_interface/models/initialize/__init__.py +0 -1
  131. robot_interface/models/initialize/initialize_params.py +0 -9
  132. robot_interface/models/mission/step.py +0 -234
  133. {isar-1.20.2.dist-info → isar-1.34.13.dist-info/licenses}/LICENSE +0 -0
  134. {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,30 @@
1
1
  import logging
2
2
  from http import HTTPStatus
3
- from typing import List, Optional
4
3
 
5
- from alitra import Pose
6
- from fastapi import Body, HTTPException, Path
7
- from injector import inject
4
+ from fastapi import Body, HTTPException
5
+ from opentelemetry import trace
8
6
 
9
- from isar.apis.models import InputPose, StartMissionResponse
10
7
  from isar.apis.models.models import (
11
8
  ControlMissionResponse,
12
- RobotInfoResponse,
9
+ StartMissionResponse,
13
10
  TaskResponse,
14
11
  )
15
12
  from isar.apis.models.start_mission_definition import (
13
+ MissionFormatError,
16
14
  StartMissionDefinition,
15
+ StopMissionDefinition,
17
16
  to_isar_mission,
18
17
  )
19
- from isar.config.settings import robot_settings, settings
20
- from isar.mission_planner.mission_planner_interface import MissionPlannerError
18
+ from isar.config.settings import robot_settings
21
19
  from isar.services.utilities.scheduling_utilities import SchedulingUtilities
22
20
  from isar.state_machine.states_enum import States
23
21
  from robot_interface.models.mission.mission import Mission
24
- from robot_interface.models.mission.step import (
25
- DriveToPose,
26
- Localize,
27
- MoveArm,
28
- ReturnToHome,
29
- )
30
- from robot_interface.models.mission.task import Task
22
+ from robot_interface.models.mission.task import TASKS, InspectionTask
23
+
24
+ tracer = trace.get_tracer(__name__)
31
25
 
32
26
 
33
27
  class SchedulingController:
34
- @inject
35
28
  def __init__(
36
29
  self,
37
30
  scheduling_utilities: SchedulingUtilities,
@@ -39,53 +32,7 @@ class SchedulingController:
39
32
  self.scheduling_utilities: SchedulingUtilities = scheduling_utilities
40
33
  self.logger = logging.getLogger("api")
41
34
 
42
- def start_mission_by_id(
43
- self,
44
- mission_id: str = Path(
45
- alias="id",
46
- title="Mission ID",
47
- description="ID-number for predefined mission",
48
- ),
49
- initial_pose: Optional[InputPose] = Body(
50
- default=None,
51
- description="The starting point of the mission. Used for initial "
52
- "localization of robot",
53
- embed=True,
54
- ),
55
- return_pose: Optional[InputPose] = Body(
56
- default=None,
57
- description="End pose of the mission. The robot return to the specified "
58
- "pose after finishing all inspections",
59
- embed=True,
60
- ),
61
- ) -> StartMissionResponse:
62
- self.logger.info(f"Received request to start mission with id {mission_id}")
63
-
64
- state: States = self.scheduling_utilities.get_state()
65
- self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
66
-
67
- mission: Mission = self.scheduling_utilities.get_mission(mission_id)
68
- if return_pose:
69
- pose: Pose = return_pose.to_alitra_pose()
70
- step: DriveToPose = DriveToPose(pose=pose)
71
- mission.tasks.append(Task(steps=[step]))
72
-
73
- self.scheduling_utilities.verify_robot_capable_of_mission(
74
- mission=mission, robot_capabilities=robot_settings.CAPABILITIES
75
- )
76
-
77
- initial_pose_alitra: Optional[Pose] = (
78
- initial_pose.to_alitra_pose() if initial_pose else None
79
- )
80
-
81
- self.logger.info(f"Starting mission with ISAR Mission ID: '{mission.id}'")
82
-
83
- self.scheduling_utilities.start_mission(
84
- mission=mission, initial_pose=initial_pose_alitra
85
- )
86
-
87
- return self._api_response(mission)
88
-
35
+ @tracer.start_as_current_span("start_mission")
89
36
  def start_mission(
90
37
  self,
91
38
  mission_definition: StartMissionDefinition = Body(
@@ -94,36 +41,27 @@ class SchedulingController:
94
41
  title="Mission Definition",
95
42
  description="Description of the mission in json format",
96
43
  ),
97
- initial_pose: Optional[InputPose] = Body(
98
- default=None,
99
- description="The starting point of the mission. Used for initial "
100
- "localization of robot",
101
- embed=True,
102
- ),
103
- return_pose: Optional[InputPose] = Body(
104
- default=None,
105
- description="End pose of the mission. The robot return to the specified "
106
- "pose after finishing all inspections",
107
- embed=True,
108
- ),
109
44
  ) -> StartMissionResponse:
110
45
  self.logger.info("Received request to start new mission")
111
46
 
112
47
  if not mission_definition:
113
- error_message: str = (
48
+ error_message_no_mission_definition: str = (
114
49
  "Unprocessable entity - 'mission_definition' empty or invalid"
115
50
  )
116
- self.logger.error(error_message)
51
+ self.logger.error(error_message_no_mission_definition)
117
52
  raise HTTPException(
118
- status_code=HTTPStatus.UNPROCESSABLE_ENTITY, detail=error_message
53
+ status_code=HTTPStatus.UNPROCESSABLE_ENTITY,
54
+ detail=error_message_no_mission_definition,
119
55
  )
120
56
 
121
57
  state: States = self.scheduling_utilities.get_state()
122
58
  self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
123
59
 
124
60
  try:
125
- mission: Mission = to_isar_mission(mission_definition)
126
- except MissionPlannerError as e:
61
+ mission: Mission = to_isar_mission(
62
+ start_mission_definition=mission_definition
63
+ )
64
+ except MissionFormatError as e:
127
65
  error_message = f"Bad Request - Cannot create ISAR mission: {e}"
128
66
  self.logger.warning(error_message)
129
67
  raise HTTPException(
@@ -134,21 +72,23 @@ class SchedulingController:
134
72
  self.scheduling_utilities.verify_robot_capable_of_mission(
135
73
  mission=mission, robot_capabilities=robot_settings.CAPABILITIES
136
74
  )
137
- if return_pose:
138
- pose: Pose = return_pose.to_alitra_pose()
139
- step: DriveToPose = DriveToPose(pose=pose)
140
- mission.tasks.append(Task(steps=[step]))
141
75
 
142
- initial_pose_alitra: Optional[Pose] = (
143
- initial_pose.to_alitra_pose() if initial_pose else None
144
- )
76
+ self.logger.info("Starting mission: %s", mission.id)
77
+ self.scheduling_utilities.start_mission(mission=mission)
78
+ return self._api_response(mission)
79
+
80
+ @tracer.start_as_current_span("return_home")
81
+ def return_home(self) -> None:
82
+ self.logger.info("Received request to return home")
145
83
 
146
- self.logger.info(f"Starting mission: {mission.id}")
147
- self.scheduling_utilities.start_mission(
148
- mission=mission, initial_pose=initial_pose_alitra
84
+ state: States = self.scheduling_utilities.get_state()
85
+ self.scheduling_utilities.verify_state_machine_ready_to_receive_return_home_mission(
86
+ state
149
87
  )
150
- return self._api_response(mission)
151
88
 
89
+ self.scheduling_utilities.return_home()
90
+
91
+ @tracer.start_as_current_span("pause_mission")
152
92
  def pause_mission(self) -> ControlMissionResponse:
153
93
  self.logger.info("Received request to pause current mission")
154
94
 
@@ -156,7 +96,7 @@ class SchedulingController:
156
96
 
157
97
  if state not in [
158
98
  States.Monitor,
159
- States.Initiate,
99
+ States.ReturningHome,
160
100
  ]:
161
101
  error_message = (
162
102
  f"Conflict - Pause command received in invalid state - State: {state}"
@@ -172,12 +112,13 @@ class SchedulingController:
172
112
  )
173
113
  return pause_mission_response
174
114
 
115
+ @tracer.start_as_current_span("resume_mission")
175
116
  def resume_mission(self) -> ControlMissionResponse:
176
117
  self.logger.info("Received request to resume current mission")
177
118
 
178
119
  state: States = self.scheduling_utilities.get_state()
179
120
 
180
- if state != States.Paused:
121
+ if state not in [States.Paused, States.ReturnHomePaused]:
181
122
  error_message = (
182
123
  f"Conflict - Resume command received in invalid state - State: {state}"
183
124
  )
@@ -189,12 +130,29 @@ class SchedulingController:
189
130
  )
190
131
  return resume_mission_response
191
132
 
192
- def stop_mission(self) -> ControlMissionResponse:
133
+ @tracer.start_as_current_span("stop_mission")
134
+ def stop_mission(
135
+ self,
136
+ mission_id: StopMissionDefinition = Body(
137
+ default=None,
138
+ embed=True,
139
+ title="Mission ID to stop",
140
+ description="The mission ID of the mission being stopped, in json format",
141
+ ),
142
+ ) -> ControlMissionResponse:
143
+
193
144
  self.logger.info("Received request to stop current mission")
194
145
 
195
146
  state: States = self.scheduling_utilities.get_state()
196
147
 
197
- if state in [States.Off, States.Idle]:
148
+ if (
149
+ state == States.UnknownStatus
150
+ or state == States.Stopping
151
+ or state == States.BlockedProtectiveStop
152
+ or state == States.Offline
153
+ or state == States.Home
154
+ or state == States.ReturningHome
155
+ ):
198
156
  error_message = (
199
157
  f"Conflict - Stop command received in invalid state - State: {state}"
200
158
  )
@@ -202,119 +160,94 @@ class SchedulingController:
202
160
  raise HTTPException(status_code=HTTPStatus.CONFLICT, detail=error_message)
203
161
 
204
162
  stop_mission_response: ControlMissionResponse = (
205
- self.scheduling_utilities.stop_mission()
163
+ self.scheduling_utilities.stop_mission(mission_id.mission_id)
206
164
  )
207
165
  return stop_mission_response
208
166
 
209
- def drive_to(
210
- self,
211
- target_pose: InputPose = Body(
212
- default=None,
213
- title="Target Pose",
214
- description="The target pose for the drive_to step",
215
- ),
216
- ) -> StartMissionResponse:
217
- self.logger.info("Received request to start new drive-to mission")
167
+ @tracer.start_as_current_span("release_intervention_needed")
168
+ def release_intervention_needed(self) -> None:
169
+ self.logger.info("Received request to release intervention needed state")
218
170
 
219
171
  state: States = self.scheduling_utilities.get_state()
220
172
 
221
- self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
173
+ if state != States.InterventionNeeded:
174
+ error_message = f"Conflict - Release intervention needed command received in invalid state - State: {state}"
175
+ self.logger.warning(error_message)
176
+ raise HTTPException(
177
+ status_code=HTTPStatus.CONFLICT,
178
+ detail=error_message,
179
+ )
222
180
 
223
- pose: Pose = target_pose.to_alitra_pose()
224
- step: DriveToPose = DriveToPose(pose=pose)
225
- mission: Mission = Mission(tasks=[Task(steps=[step])])
181
+ self.scheduling_utilities.release_intervention_needed()
182
+ self.logger.info("Released intervention needed state successfully")
226
183
 
227
- self.logger.info(
228
- f"Starting drive to mission with ISAR Mission ID: '{mission.id}'"
229
- )
230
- self.scheduling_utilities.start_mission(mission=mission, initial_pose=None)
231
- return self._api_response(mission)
232
-
233
- def start_localization_mission(
234
- self,
235
- localization_pose: InputPose = Body(
236
- default=None,
237
- embed=True,
238
- title="Localization Pose",
239
- description="The current position of the robot",
240
- ),
241
- ) -> StartMissionResponse:
242
- self.logger.info("Received request to start new localization mission")
184
+ @tracer.start_as_current_span("lockdown")
185
+ def lockdown(self) -> None:
186
+ self.logger.info("Received request to lockdown robot")
243
187
 
244
188
  state: States = self.scheduling_utilities.get_state()
245
189
 
246
- self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
190
+ if state == States.Lockdown:
191
+ error_message = "Conflict - Lockdown command received in lockdown state"
192
+ self.logger.warning(error_message)
193
+ raise HTTPException(
194
+ status_code=HTTPStatus.CONFLICT,
195
+ detail=error_message,
196
+ )
247
197
 
248
- pose: Pose = localization_pose.to_alitra_pose()
249
- step: Localize = Localize(localization_pose=pose)
250
- mission: Mission = Mission(tasks=[Task(steps=[step])])
198
+ self.scheduling_utilities.lock_down_robot()
199
+ self.logger.info("Lockdown started successfully")
251
200
 
252
- self.logger.info(
253
- f"Starting localization mission with ISAR Mission ID: '{mission.id}'"
254
- )
255
- self.scheduling_utilities.start_mission(
256
- mission=mission,
257
- initial_pose=None,
258
- )
259
- return self._api_response(mission)
201
+ @tracer.start_as_current_span("release_lockdown")
202
+ def release_lockdown(self) -> None:
203
+ self.logger.info("Received request to release robot lockdown")
260
204
 
261
- def start_move_arm_mission(
262
- self,
263
- arm_pose_literal: str = Path(
264
- ...,
265
- alias="arm_pose_literal",
266
- title="Arm pose literal",
267
- description="Arm pose as a literal",
268
- ),
269
- ) -> StartMissionResponse:
270
- self.logger.info("Received request to start new move arm mission")
205
+ state: States = self.scheduling_utilities.get_state()
271
206
 
272
- if not robot_settings.VALID_ARM_POSES:
273
- error_message: str = (
274
- f"Received a request to move the arm but the robot "
275
- f"{settings.ROBOT_NAME} does not support moving an arm"
276
- )
207
+ if state != States.Lockdown:
208
+ error_message = f"Conflict - Release lockdown command received in invalid state - State: {state}"
277
209
  self.logger.warning(error_message)
278
210
  raise HTTPException(
279
- status_code=HTTPStatus.BAD_REQUEST, detail=error_message
211
+ status_code=HTTPStatus.CONFLICT,
212
+ detail=error_message,
280
213
  )
281
214
 
282
- if arm_pose_literal not in robot_settings.VALID_ARM_POSES:
283
- error_message = (
284
- f"Received a request to move the arm but the arm pose "
285
- f"{arm_pose_literal} is not supported by the robot "
286
- f"{settings.ROBOT_NAME}"
287
- )
288
- self.logger.warning(error_message)
215
+ self.scheduling_utilities.release_robot_lockdown()
216
+ self.logger.info("Released lockdown successfully")
217
+
218
+ @tracer.start_as_current_span("maintenance_mode")
219
+ def set_maintenance_mode(self) -> None:
220
+ self.logger.info("Received request to set maintenance_mode")
221
+
222
+ state: States = self.scheduling_utilities.get_state()
223
+
224
+ if state == States.Maintenance or state == States.StoppingDueToMaintenance:
225
+ message = f"Conflict - Call to set maintenance mode was given while in state {state}."
226
+ self.logger.info(message)
289
227
  raise HTTPException(
290
- status_code=HTTPStatus.BAD_REQUEST, detail=error_message
228
+ status_code=HTTPStatus.CONFLICT,
229
+ detail=message,
291
230
  )
292
231
 
293
- state: States = self.scheduling_utilities.get_state()
232
+ self.scheduling_utilities.set_maintenance_mode()
233
+ self.logger.info("Maintenance mode has been set")
294
234
 
295
- self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
235
+ @tracer.start_as_current_span("release_maintenance_mode")
236
+ def release_maintenance_mode(self) -> None:
237
+ self.logger.info("Received request to release robot from maintenance mode")
296
238
 
297
- step: MoveArm = MoveArm(arm_pose=arm_pose_literal)
298
- mission: Mission = Mission(tasks=[Task(steps=[step])])
239
+ state: States = self.scheduling_utilities.get_state()
299
240
 
300
- self.logger.info(
301
- f"Starting move arm mission with ISAR Mission ID: '{mission.id}'"
302
- )
303
- self.scheduling_utilities.start_mission(
304
- mission=mission,
305
- initial_pose=None,
306
- )
307
- return self._api_response(mission)
241
+ if state != States.Maintenance:
242
+ message = f"Conflict - Release maintenance mode command received in invalid state - State: {state}"
243
+ self.logger.info(message)
244
+ raise HTTPException(
245
+ status_code=HTTPStatus.CONFLICT,
246
+ detail=message,
247
+ )
308
248
 
309
- def get_info(self):
310
- return RobotInfoResponse(
311
- robot_package=settings.ROBOT_PACKAGE,
312
- isar_id=settings.ISAR_ID,
313
- robot_name=settings.ROBOT_NAME,
314
- robot_capabilities=robot_settings.CAPABILITIES,
315
- robot_map_name=settings.DEFAULT_MAP,
316
- plant_short_name=settings.PLANT_SHORT_NAME,
317
- )
249
+ self.scheduling_utilities.release_maintenance_mode()
250
+ self.logger.info("Maintenance mode successfully released")
318
251
 
319
252
  def _api_response(self, mission: Mission) -> StartMissionResponse:
320
253
  return StartMissionResponse(
@@ -322,9 +255,12 @@ class SchedulingController:
322
255
  tasks=[self._task_api_response(task) for task in mission.tasks],
323
256
  )
324
257
 
325
- def _task_api_response(self, task: Task) -> TaskResponse:
326
- steps: List[dict] = []
327
- for step in task.steps:
328
- steps.append({"id": step.id, "type": step.__class__.__name__})
258
+ def _task_api_response(self, task: TASKS) -> TaskResponse:
259
+ if isinstance(task, InspectionTask):
260
+ inspection_id = task.inspection_id
261
+ else:
262
+ inspection_id = None
329
263
 
330
- return TaskResponse(id=task.id, tag_id=task.tag_id, steps=steps)
264
+ return TaskResponse(
265
+ id=task.id, tag_id=task.tag_id, inspection_id=inspection_id, type=task.type
266
+ )
@@ -3,7 +3,7 @@ import logging
3
3
  from fastapi import Depends
4
4
  from fastapi.security.base import SecurityBase
5
5
  from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
6
- from fastapi_azure_auth.exceptions import InvalidAuth
6
+ from fastapi_azure_auth.exceptions import InvalidAuthHttp
7
7
  from fastapi_azure_auth.user import User
8
8
  from pydantic import BaseModel
9
9
 
@@ -35,7 +35,7 @@ async def validate_has_role(user: User = Depends(azure_scheme)) -> None:
35
35
  Raises a 403 authorization error if not.
36
36
  """
37
37
  if settings.REQUIRED_ROLE not in user.roles:
38
- raise InvalidAuth(
38
+ raise InvalidAuthHttp(
39
39
  "Current user does not possess the required role for this endpoint"
40
40
  )
41
41
 
@@ -48,9 +48,9 @@ class Authenticator:
48
48
  self.logger = logging.getLogger("api")
49
49
  self.authentication_enabled: bool = authentication_enabled
50
50
  enabled_string = "enabled" if self.authentication_enabled else "disabled"
51
- self.logger.info(f"API authentication is {enabled_string}")
51
+ self.logger.info("API authentication is %s", 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
  """
@@ -1,33 +1,35 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIFrDCCA5QCCQDxWBtOiKrXyDANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMC
3
- Tk8xDzANBgNVBAgMBkJlcmdlbjEPMA0GA1UEBwwGQmVyZ2VuMRQwEgYDVQQKDAtF
4
- cV9Sb2JvdGljczERMA8GA1UECwwIUm9ib3RpY3MxHDAaBgNVBAMME1JvYm90aWNz
5
- X1NlbGZTaWduZWQxHzAdBgkqhkiG9w0BCQEWEHRsbmVAZXF1aW5vci5jb20wHhcN
6
- MjMwOTI4MTIwMjU0WhcNMjQwOTI3MTIwMjU0WjCBlzELMAkGA1UEBhMCTk8xDzAN
7
- BgNVBAgMBkJlcmdlbjEPMA0GA1UEBwwGQmVyZ2VuMRQwEgYDVQQKDAtFcV9Sb2Jv
8
- dGljczERMA8GA1UECwwIUm9ib3RpY3MxHDAaBgNVBAMME1JvYm90aWNzX1NlbGZT
9
- aWduZWQxHzAdBgkqhkiG9w0BCQEWEHRsbmVAZXF1aW5vci5jb20wggIiMA0GCSqG
10
- SIb3DQEBAQUAA4ICDwAwggIKAoICAQDlId+Y8n3aQDx49siw+2iuJL+QPcr2W/Li
11
- XLKzKDaJ39oB7cIAfvX17c5tOgpTx+u/lsQBDfNhweclrPB4oCcJdDPFGWsTuI1H
12
- PE/6ytVpinBFub/RpIHY049LlGRPvDEIc+qWiwZBkwG38KO8Ly/4oU3vW28L0YF/
13
- LUDnsmZ/kZV/dBXe/z+1FqG7nqQC1kl0LmbQtIJT+yW9ybK/Olu1uMhGpy10cBca
14
- k+lZDZWhi1Y76jTqxlVR9bCsP0XWR9czbU26R29JzSAChm2gAy+ekAqO+ol0xbhG
15
- msVNn640zTPjIcsouHB/QjZEwJ/amGMS8o+wfQc+rOUQozRl1gEhFTby9Vn66IsE
16
- WRkXkI8AgGcxrnwOnVjgRaJPtgNWfL35EYEZxw5+mIKEhvEcApoTEcr7LUv8E10n
17
- afpZhEzjgBZGRLMw0myn21+PgsJwG/jDQHd/oDXyWRcDu53humJZN3b+WIZa2BXy
18
- 98H0ZNKyP78uvG3mrqG2kW3tgws4XOqRuBEEj5PJuddShtkhpUJtobwksTH5vDt7
19
- L5JiVKDEYvXT9xt9FW8cDC1ZO4RRbfbMLv9xs1CXrbS75Wz0TyAqUkAPFAwfFgR5
20
- etES5ZeI4oPFW+zxgTkqLvT2PZh6I39OHE+Gi8P7aKZfnb7QqT4GkckJ1JWXuPee
21
- urkiGIrvowIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQAAAjrgEWNnR2fEAMYtR/RK
22
- VLgycBph2b1l6I3kwfDHF+HNwlrAihvP8M8xjCmbTODHOZ9ZaHMqnAoODpZpxgdg
23
- J3lEo3NuzTqYBQ+hXO0PLhVl5jaolX6vD/i7zqzK4mnQ34zKQb8LaJlEeCwuVhyN
24
- Mjwc84JreVD+VOaXIydVZ4vVmfylv79JsM6j7e4YsNofpvEiCAk/R/apDGXnOCkD
25
- WwoTxRFGl9HMqYigHg5TTfszfC765njRxwsrgLWN5cFPNai28ueBG0473pdUGlgN
26
- tT8NtXClr7R3avJ+v1pM4MlJXbyvl7ADsl9Bj+iHQusZgKEq/rARk8yzh6vH8Pwa
27
- QrsbgQ0OLcMMQrDSsl8BrwzV7boO+KHM6ypBsxJWBIJPCTdTq5cGHMLL+5+vfmE+
28
- jXqdrrO7b+xiiFPszMBjmyGo8/55Pr63YYz7pyXKQtjyev91yO7+niCQ4+MkR5XQ
29
- NTQdcfdd/LZwLhO7BS6U08Ytcg0ZMr3tb/hxCHD8vx+XS6gSxXVHqDqMZlmEcLOo
30
- cOQy73tOFX4Htpge0vQKhnH0TIDPb8um2IivdWdMz8wAoNGwj+A/aP3Eh9qqVZ5w
31
- 1t/Erqpxw+FIiAjqRJ1yE9I7I3buhostxj0y4ry7g+faNtgu8hrrILz84kJw4YdC
32
- qtASXmP/Nm1dAHV2UjM33A==
2
+ MIIGGzCCBAOgAwIBAgIUCVuS8tL7R2bdjRJznkk1NN0oUa8wDQYJKoZIhvcNAQEL
3
+ BQAwgZwxCzAJBgNVBAYTAk5PMQ8wDQYDVQQIDAZCZXJnZW4xDzANBgNVBAcMBkJl
4
+ cmdlbjEUMBIGA1UECgwLRXFfUm9ib3RpY3MxETAPBgNVBAsMCFJvYm90aWNzMRww
5
+ GgYDVQQDDBNSb2JvdGljc19TZWxmU2lnbmVkMSQwIgYJKoZIhvcNAQkBFhVmZ19y
6
+ b2JvdHNAZXF1aW5vci5jb20wHhcNMjQxMjExMDczNzAzWhcNMjUxMjExMDczNzAz
7
+ WjCBnDELMAkGA1UEBhMCTk8xDzANBgNVBAgMBkJlcmdlbjEPMA0GA1UEBwwGQmVy
8
+ Z2VuMRQwEgYDVQQKDAtFcV9Sb2JvdGljczERMA8GA1UECwwIUm9ib3RpY3MxHDAa
9
+ BgNVBAMME1JvYm90aWNzX1NlbGZTaWduZWQxJDAiBgkqhkiG9w0BCQEWFWZnX3Jv
10
+ Ym90c0BlcXVpbm9yLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
11
+ ALVlT8EjA8UpXdARJM6YfhH+9DEyytb0SJYOEm9jWL7X0G5eMbNfKtfFj/BLQBBw
12
+ GDqyvhFa8SO56zM3Yi1Lir41JkpOKf2zJpNaL1syPxOA87MAm43UN/p2Mo6QAYpC
13
+ lJzmeNoagLFi/kIzGajeUwISOf//Zej0jK/9CpuvvNVHz4wRk4Ap9qtgrM9qhlNq
14
+ rZL4ZQODnvDZ/nwAYwEh9O2sbRr73aiHFcbxNAU2XxtT5UwbZMHom2Xic2Dmubqn
15
+ cWFU+C4upEqPvgYU/hxia/rkhHyicXmdXT6IzjVxPbrlPBHoKVodJOCQ3UoBQl5X
16
+ caHCluKOblA/yX+vEhv2h/m8XOtWiq0Jxnx+6D7B1piZfQnnNVSBT0bOYASb2cpn
17
+ 8rJE3RBdBwiqjKTotVTZ8PAS/GY0Gz3GoMAUCvIGA02+/cO9ENls9D79/86lgiCQ
18
+ UqyteNNmo+oMqKHBw9Jmeejw1kbpnJN1abc270+qCT17aeX8yiMza5CyUTQTtgFI
19
+ 62xIYaHWhN7GrxOvoMU8X0G1HINwjS5heIhu5bND/26dLZqV3aBUWwb4ql5QIce2
20
+ q5BQKmMqQLZF6J0aFL9dKJv5u3ioPFmiYPsPiRlwusPOoOFCiMt2kqZE3h3wxu6V
21
+ cXWdj0sH+dgxuGYTxrLuoSru4Xv/HPXh15mVY0/eI+u/AgMBAAGjUzBRMB0GA1Ud
22
+ DgQWBBQW2Ms6M3iadKVLOnraMlx6Exm8KjAfBgNVHSMEGDAWgBQW2Ms6M3iadKVL
23
+ OnraMlx6Exm8KjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQB0
24
+ XorfCLmVUN3aQWBouwG0fXHBqi3d4RA0vPsi8w+R/dOuHnLBLlHAiOkCMqW23bLs
25
+ ir2UVqpGXcp5keKGeCTj+bmdqhnS/B9eoaeA8np/zDPH834v+Qv7JA5ch50gYrL7
26
+ llF/02e5m0egP2xx9V7k90Du8OBm7xQgfbyrUcB2laC+loPyQiHMF24CU4ESfC4K
27
+ DBXOZm8Kd6+fvzEs8XDLKwjNb9MocHIJikysZTncxfhgVPaU5KCqF3afHkCkZGzn
28
+ Z+yMBfWbZ2WtM3lfwmM7z5NCTSqSFucW5+kw/OnzS3nQLyz2Q4nh4ApojsI3v+FH
29
+ xz8aCITPbVwKAHGYO9jbk9x8LlkzDPCCzBH7dGKLKaQSCoD+IOwig1cI7pM77WeI
30
+ BNC0g2ZCuLdb7c37/kT/1xGrIGUrpDsO34ZWW7E2X8kQPkAgqMNp0uXcnvIoNHO1
31
+ eVL08Om+7WbwK7wS3te5IWDq4rRWVW4Jv709w8z6porr3W8ipdtUoYqbgZI8jOiW
32
+ osY4UVcRwQ4S1oF5pNRzXlZAPwAnaDipglxHBiy9/ARS6HKpOCbmUHbj+goXYNyZ
33
+ ps8TcYc9LFOq4HBE4KKbV971jf/wQy1nVcm2qSbOJ9MPHM5BG22m1iVhSYbLPgE2
34
+ SBZvCS+voD8MqpBB1tPcvVqlPb1pFxf1lOc5QV3uOA==
33
35
  -----END CERTIFICATE-----
@@ -9,7 +9,9 @@ from azure.core.exceptions import (
9
9
  from azure.identity import ClientSecretCredential, DefaultAzureCredential
10
10
  from azure.keyvault.secrets import KeyVaultSecret, SecretClient
11
11
 
12
- from isar.config.keyvault.keyvault_error import KeyvaultError
12
+
13
+ class KeyvaultError(Exception):
14
+ pass
13
15
 
14
16
 
15
17
  class Keyvault:
@@ -65,7 +67,7 @@ class Keyvault:
65
67
  raise KeyvaultError # type: ignore
66
68
 
67
69
  def get_secret_client(self) -> SecretClient:
68
- if self.client == None:
70
+ if self.client is None:
69
71
  try:
70
72
  credential: Union[ClientSecretCredential, DefaultAzureCredential]
71
73
  if self.client_id and self.client_secret and self.tenant_id: