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.
- isar/apis/api.py +135 -86
- isar/apis/models/__init__.py +0 -1
- isar/apis/models/models.py +21 -11
- isar/apis/models/start_mission_definition.py +115 -170
- isar/apis/robot_control/robot_controller.py +41 -0
- isar/apis/schedule/scheduling_controller.py +123 -187
- isar/apis/security/authentication.py +5 -5
- isar/config/certs/ca-cert.pem +33 -31
- isar/config/keyvault/keyvault_service.py +4 -2
- isar/config/log.py +45 -40
- isar/config/logging.conf +16 -31
- isar/config/open_telemetry.py +102 -0
- isar/config/settings.py +74 -117
- isar/eventhandlers/eventhandler.py +123 -0
- isar/models/events.py +184 -0
- isar/models/status.py +22 -0
- isar/modules.py +117 -200
- isar/robot/robot.py +383 -0
- isar/robot/robot_battery.py +60 -0
- isar/robot/robot_monitor_mission.py +357 -0
- isar/robot/robot_pause_mission.py +74 -0
- isar/robot/robot_resume_mission.py +67 -0
- isar/robot/robot_start_mission.py +66 -0
- isar/robot/robot_status.py +61 -0
- isar/robot/robot_stop_mission.py +68 -0
- isar/robot/robot_upload_inspection.py +75 -0
- isar/script.py +58 -41
- isar/services/service_connections/mqtt/mqtt_client.py +47 -11
- isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +5 -2
- isar/services/service_connections/mqtt/robot_info_publisher.py +3 -3
- isar/services/service_connections/persistent_memory.py +69 -0
- isar/services/utilities/mqtt_utilities.py +93 -0
- isar/services/utilities/robot_utilities.py +20 -0
- isar/services/utilities/scheduling_utilities.py +386 -100
- isar/state_machine/state_machine.py +242 -539
- isar/state_machine/states/__init__.py +0 -8
- isar/state_machine/states/await_next_mission.py +114 -0
- isar/state_machine/states/blocked_protective_stop.py +60 -0
- isar/state_machine/states/going_to_lockdown.py +95 -0
- isar/state_machine/states/going_to_recharging.py +92 -0
- isar/state_machine/states/home.py +115 -0
- isar/state_machine/states/intervention_needed.py +77 -0
- isar/state_machine/states/lockdown.py +38 -0
- isar/state_machine/states/maintenance.py +43 -0
- isar/state_machine/states/monitor.py +137 -247
- isar/state_machine/states/offline.py +51 -53
- isar/state_machine/states/paused.py +92 -23
- isar/state_machine/states/pausing.py +48 -0
- isar/state_machine/states/pausing_return_home.py +48 -0
- isar/state_machine/states/recharging.py +80 -0
- isar/state_machine/states/resuming.py +57 -0
- isar/state_machine/states/resuming_return_home.py +64 -0
- isar/state_machine/states/return_home_paused.py +109 -0
- isar/state_machine/states/returning_home.py +217 -0
- isar/state_machine/states/stopping.py +69 -0
- isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
- isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
- isar/state_machine/states/stopping_go_to_recharge.py +51 -0
- isar/state_machine/states/stopping_paused_mission.py +36 -0
- isar/state_machine/states/stopping_paused_return_home.py +59 -0
- isar/state_machine/states/stopping_return_home.py +59 -0
- isar/state_machine/states/unknown_status.py +74 -0
- isar/state_machine/states_enum.py +23 -5
- isar/state_machine/transitions/mission.py +225 -0
- isar/state_machine/transitions/return_home.py +108 -0
- isar/state_machine/transitions/robot_status.py +87 -0
- isar/state_machine/utils/common_event_handlers.py +138 -0
- isar/storage/blob_storage.py +70 -52
- isar/storage/local_storage.py +25 -12
- isar/storage/storage_interface.py +28 -7
- isar/storage/uploader.py +174 -55
- isar/storage/utilities.py +32 -29
- {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/METADATA +119 -123
- isar-1.34.13.dist-info/RECORD +120 -0
- {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/WHEEL +1 -1
- {isar-1.20.2.dist-info → isar-1.34.13.dist-info}/entry_points.txt +1 -0
- robot_interface/models/exceptions/robot_exceptions.py +91 -41
- robot_interface/models/inspection/__init__.py +0 -13
- robot_interface/models/inspection/inspection.py +42 -33
- robot_interface/models/mission/mission.py +14 -15
- robot_interface/models/mission/status.py +20 -26
- robot_interface/models/mission/task.py +154 -121
- robot_interface/models/robots/battery_state.py +6 -0
- robot_interface/models/robots/media.py +13 -0
- robot_interface/models/robots/robot_model.py +7 -7
- robot_interface/robot_interface.py +119 -84
- robot_interface/telemetry/mqtt_client.py +74 -12
- robot_interface/telemetry/payloads.py +91 -13
- robot_interface/utilities/json_service.py +7 -1
- isar/config/configuration_error.py +0 -2
- isar/config/keyvault/keyvault_error.py +0 -2
- isar/config/predefined_mission_definition/__init__.py +0 -0
- isar/config/predefined_mission_definition/default_exr.json +0 -51
- isar/config/predefined_mission_definition/default_mission.json +0 -91
- isar/config/predefined_mission_definition/default_turtlebot.json +0 -124
- isar/config/predefined_missions/__init__.py +0 -0
- isar/config/predefined_missions/default.json +0 -92
- isar/config/predefined_missions/default_turtlebot.json +0 -110
- isar/config/predefined_poses/__init__.py +0 -0
- isar/config/predefined_poses/predefined_poses.py +0 -616
- isar/config/settings.env +0 -25
- isar/mission_planner/__init__.py +0 -0
- isar/mission_planner/local_planner.py +0 -82
- isar/mission_planner/mission_planner_interface.py +0 -26
- isar/mission_planner/sequential_task_selector.py +0 -23
- isar/mission_planner/task_selector_interface.py +0 -31
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -12
- isar/models/communication/queues/__init__.py +0 -4
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queues.py +0 -19
- isar/models/communication/queues/status_queue.py +0 -20
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/auth/__init__.py +0 -0
- isar/services/auth/azure_credentials.py +0 -14
- isar/services/readers/__init__.py +0 -0
- isar/services/readers/base_reader.py +0 -37
- isar/services/service_connections/request_handler.py +0 -153
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/utilities/queue_utilities.py +0 -39
- isar/services/utilities/threaded_request.py +0 -68
- isar/state_machine/states/idle.py +0 -85
- isar/state_machine/states/initialize.py +0 -71
- isar/state_machine/states/initiate.py +0 -142
- isar/state_machine/states/off.py +0 -18
- isar/state_machine/states/stop.py +0 -95
- isar/storage/slimm_storage.py +0 -191
- isar-1.20.2.dist-info/RECORD +0 -116
- robot_interface/models/initialize/__init__.py +0 -1
- robot_interface/models/initialize/initialize_params.py +0 -9
- robot_interface/models/mission/step.py +0 -234
- {isar-1.20.2.dist-info → isar-1.34.13.dist-info/licenses}/LICENSE +0 -0
- {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
|
|
6
|
-
from
|
|
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
|
-
|
|
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
|
|
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.
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
error_message_no_mission_definition: str = (
|
|
114
49
|
"Unprocessable entity - 'mission_definition' empty or invalid"
|
|
115
50
|
)
|
|
116
|
-
self.logger.error(
|
|
51
|
+
self.logger.error(error_message_no_mission_definition)
|
|
117
52
|
raise HTTPException(
|
|
118
|
-
status_code=HTTPStatus.UNPROCESSABLE_ENTITY,
|
|
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(
|
|
126
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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.
|
|
147
|
-
self.scheduling_utilities.
|
|
148
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
|
|
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
|
-
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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
|
-
|
|
253
|
-
|
|
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
|
-
|
|
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
|
|
273
|
-
error_message
|
|
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.
|
|
211
|
+
status_code=HTTPStatus.CONFLICT,
|
|
212
|
+
detail=error_message,
|
|
280
213
|
)
|
|
281
214
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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.
|
|
228
|
+
status_code=HTTPStatus.CONFLICT,
|
|
229
|
+
detail=message,
|
|
291
230
|
)
|
|
292
231
|
|
|
293
|
-
|
|
232
|
+
self.scheduling_utilities.set_maintenance_mode()
|
|
233
|
+
self.logger.info("Maintenance mode has been set")
|
|
294
234
|
|
|
295
|
-
|
|
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
|
-
|
|
298
|
-
mission: Mission = Mission(tasks=[Task(steps=[step])])
|
|
239
|
+
state: States = self.scheduling_utilities.get_state()
|
|
299
240
|
|
|
300
|
-
|
|
301
|
-
f"
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
310
|
-
|
|
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:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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
|
"""
|
isar/config/certs/ca-cert.pem
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
|
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:
|