Skip to content

Commit c8ff71c

Browse files
committed
Give ISAR responsibility to create ids
for mission, task and inspection. Therefore the ids are removed from the StartMissionDefinitions and instead generated when the Mission and Tasks are created Some refactors are also applied, most notably replacing dataclass with pydantic BaseModels
1 parent 593e4c7 commit c8ff71c

36 files changed

+371
-554
lines changed

src/isar/apis/models/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class TaskResponse(BaseModel):
1010
id: str
1111
tag_id: Optional[str] = None
12+
inspection_id: Optional[str] = None
1213
type: TaskTypes
1314

1415

src/isar/apis/models/start_mission_definition.py

Lines changed: 73 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from enum import Enum
33
from typing import List, Optional
44

5-
from alitra import Frame, Orientation, Pose, Position
65
from pydantic import BaseModel, Field
76

87
from isar.apis.models.models import InputPose, InputPosition
@@ -44,188 +43,139 @@ class StartMissionInspectionDefinition(BaseModel):
4443
analysis_type: Optional[str] = None
4544
duration: Optional[float] = None
4645
metadata: Optional[dict] = None
47-
id: Optional[str] = None
4846

4947

5048
class StartMissionTaskDefinition(BaseModel):
5149
type: TaskType = Field(default=TaskType.Inspection)
5250
pose: InputPose
5351
inspection: Optional[StartMissionInspectionDefinition] = None
5452
tag: Optional[str] = None
55-
id: Optional[str] = None
5653
zoom: Optional[ZoomDescription] = None
5754

5855

5956
class StartMissionDefinition(BaseModel):
6057
tasks: List[StartMissionTaskDefinition]
61-
id: Optional[str] = None
6258
name: Optional[str] = None
6359
start_pose: Optional[InputPose] = None
64-
dock: Optional[bool] = None
65-
undock: Optional[bool] = None
60+
dock: Optional[bool] = Field(default=False)
61+
undock: Optional[bool] = Field(default=False)
6662

6763

68-
def to_isar_mission(start_mission_definition: StartMissionDefinition) -> Mission:
64+
def to_isar_mission(
65+
start_mission_definition: StartMissionDefinition,
66+
return_pose: Optional[InputPose] = None,
67+
) -> Mission:
6968
isar_tasks: List[TASKS] = []
7069

71-
for start_mission_task_definition in start_mission_definition.tasks:
72-
task: TASKS = create_isar_task(start_mission_task_definition)
73-
if start_mission_task_definition.id:
74-
task.id = start_mission_task_definition.id
70+
for task_definition in start_mission_definition.tasks:
71+
task: TASKS = to_isar_task(task_definition)
7572
isar_tasks.append(task)
7673

74+
if return_pose:
75+
isar_tasks.append(ReturnToHome(pose=return_pose.to_alitra_pose()))
76+
7777
if not isar_tasks:
7878
raise MissionPlannerError("Mission does not contain any valid tasks")
7979

80-
check_for_duplicate_ids(isar_tasks)
81-
82-
isar_mission: Mission = Mission(tasks=isar_tasks)
83-
84-
isar_mission.dock = start_mission_definition.dock
85-
isar_mission.undock = start_mission_definition.undock
86-
80+
isar_mission_name: str
8781
if start_mission_definition.name:
88-
isar_mission.name = start_mission_definition.name
82+
isar_mission_name = start_mission_definition.name
8983
else:
90-
isar_mission.name = _build_mission_name()
91-
92-
if start_mission_definition.id:
93-
isar_mission.id = start_mission_definition.id
84+
isar_mission_name = _build_mission_name()
9485

86+
start_pose = None
9587
if start_mission_definition.start_pose:
96-
input_pose: InputPose = start_mission_definition.start_pose
97-
input_frame: Frame = Frame(name=input_pose.frame_name)
98-
input_position: Position = Position(
99-
input_pose.position.x,
100-
input_pose.position.y,
101-
input_pose.position.z,
102-
input_frame,
103-
)
104-
input_orientation: Orientation = Orientation(
105-
input_pose.orientation.x,
106-
input_pose.orientation.y,
107-
input_pose.orientation.z,
108-
input_pose.orientation.w,
109-
input_frame,
110-
)
111-
isar_mission.start_pose = Pose(
112-
position=input_position, orientation=input_orientation, frame=input_frame
113-
)
114-
115-
return isar_mission
116-
117-
118-
def check_for_duplicate_ids(items: List[TASKS]):
119-
duplicate_ids = get_duplicate_ids(items=items)
120-
if len(duplicate_ids) > 0:
121-
raise MissionPlannerError(
122-
f"Failed to create as there were duplicate IDs which is not allowed "
123-
f"({duplicate_ids})"
124-
)
125-
88+
start_pose = start_mission_definition.start_pose.to_alitra_pose()
89+
90+
return Mission(
91+
tasks=isar_tasks,
92+
name=isar_mission_name,
93+
start_pose=start_pose,
94+
dock=start_mission_definition.dock,
95+
undock=start_mission_definition.undock,
96+
)
12697

127-
def create_isar_task(start_mission_task_definition) -> TASKS:
12898

129-
if start_mission_task_definition.type == TaskType.Inspection:
130-
return create_inspection_task(start_mission_task_definition)
131-
elif start_mission_task_definition.type == TaskType.Localization:
132-
return create_localization_task(start_mission_task_definition)
133-
elif start_mission_task_definition.type == TaskType.ReturnToHome:
134-
return create_return_to_home_task(start_mission_task_definition)
135-
elif start_mission_task_definition.type == TaskType.Dock:
99+
def to_isar_task(task_definition: StartMissionTaskDefinition) -> TASKS:
100+
if task_definition.type == TaskType.Inspection:
101+
return to_inspection_task(task_definition)
102+
elif task_definition.type == TaskType.Localization:
103+
return to_localization_task(task_definition)
104+
elif task_definition.type == TaskType.ReturnToHome:
105+
return create_return_to_home_task(task_definition)
106+
elif task_definition.type == TaskType.Dock:
136107
return create_dock_task()
137108
else:
138109
raise MissionPlannerError(
139-
f"Failed to create task: '{start_mission_task_definition.type}' is not a valid"
110+
f"Failed to create task: '{task_definition.type}' is not a valid"
140111
)
141112

142113

143-
def create_inspection_task(
144-
start_mission_task_definition: StartMissionTaskDefinition,
145-
) -> TASKS:
114+
def to_inspection_task(task_definition: StartMissionTaskDefinition) -> TASKS:
115+
inspection_definition = task_definition.inspection
146116

147-
if start_mission_task_definition.inspection.type == InspectionTypes.image:
117+
if inspection_definition.type == InspectionTypes.image:
148118
return TakeImage(
149-
target=start_mission_task_definition.inspection.inspection_target.to_alitra_position(),
150-
tag_id=start_mission_task_definition.tag,
151-
robot_pose=start_mission_task_definition.pose.to_alitra_pose(),
152-
metadata=start_mission_task_definition.inspection.metadata,
153-
zoom=start_mission_task_definition.zoom,
119+
robot_pose=task_definition.pose.to_alitra_pose(),
120+
tag_id=task_definition.tag,
121+
target=task_definition.inspection.inspection_target.to_alitra_position(),
122+
metadata=task_definition.inspection.metadata,
123+
zoom=task_definition.zoom,
154124
)
155-
elif start_mission_task_definition.inspection.type == InspectionTypes.video:
125+
elif inspection_definition.type == InspectionTypes.video:
156126
return TakeVideo(
157-
target=start_mission_task_definition.inspection.inspection_target.to_alitra_position(),
158-
duration=start_mission_task_definition.inspection.duration,
159-
tag_id=start_mission_task_definition.tag,
160-
robot_pose=start_mission_task_definition.pose.to_alitra_pose(),
161-
metadata=start_mission_task_definition.inspection.metadata,
162-
zoom=start_mission_task_definition.zoom,
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+
duration=inspection_definition.duration,
131+
metadata=task_definition.inspection.metadata,
132+
zoom=task_definition.zoom,
163133
)
164-
165-
elif start_mission_task_definition.inspection.type == InspectionTypes.thermal_image:
134+
elif inspection_definition.type == InspectionTypes.thermal_image:
166135
return TakeThermalImage(
167-
target=start_mission_task_definition.inspection.inspection_target.to_alitra_position(),
168-
tag_id=start_mission_task_definition.tag,
169-
robot_pose=start_mission_task_definition.pose.to_alitra_pose(),
170-
metadata=start_mission_task_definition.inspection.metadata,
171-
zoom=start_mission_task_definition.zoom,
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+
metadata=task_definition.inspection.metadata,
140+
zoom=task_definition.zoom,
172141
)
173-
174-
elif start_mission_task_definition.inspection.type == InspectionTypes.thermal_video:
142+
elif inspection_definition.type == InspectionTypes.thermal_video:
175143
return TakeThermalVideo(
176-
target=start_mission_task_definition.inspection.inspection_target.to_alitra_position(),
177-
duration=start_mission_task_definition.inspection.duration,
178-
tag_id=start_mission_task_definition.tag,
179-
robot_pose=start_mission_task_definition.pose.to_alitra_pose(),
180-
metadata=start_mission_task_definition.inspection.metadata,
181-
zoom=start_mission_task_definition.zoom,
144+
robot_pose=task_definition.pose.to_alitra_pose(),
145+
tag_id=task_definition.tag,
146+
target=task_definition.inspection.inspection_target.to_alitra_position(),
147+
duration=inspection_definition.duration,
148+
metadata=task_definition.inspection.metadata,
149+
zoom=task_definition.zoom,
182150
)
183-
184-
elif start_mission_task_definition.inspection.type == InspectionTypes.audio:
151+
elif inspection_definition.type == InspectionTypes.audio:
185152
return RecordAudio(
186-
target=start_mission_task_definition.inspection.inspection_target.to_alitra_position(),
187-
duration=start_mission_task_definition.inspection.duration,
188-
tag_id=start_mission_task_definition.tag,
189-
robot_pose=start_mission_task_definition.pose.to_alitra_pose(),
190-
metadata=start_mission_task_definition.inspection.metadata,
191-
zoom=start_mission_task_definition.zoom,
153+
robot_pose=task_definition.pose.to_alitra_pose(),
154+
tag_id=task_definition.tag,
155+
target=task_definition.inspection.inspection_target.to_alitra_position(),
156+
duration=inspection_definition.duration,
157+
metadata=task_definition.inspection.metadata,
158+
zoom=task_definition.zoom,
192159
)
193160
else:
194161
raise ValueError(
195-
f"Inspection type '{start_mission_task_definition.inspection.type}' not supported"
162+
f"Inspection type '{inspection_definition.type}' not supported"
196163
)
197164

198165

199-
def create_localization_task(
200-
start_mission_task_definition: StartMissionTaskDefinition,
201-
) -> Localize:
202-
return Localize(
203-
localization_pose=start_mission_task_definition.pose.to_alitra_pose()
204-
)
166+
def to_localization_task(task_definition: StartMissionTaskDefinition) -> Localize:
167+
return Localize(localization_pose=task_definition.pose.to_alitra_pose())
205168

206169

207170
def create_return_to_home_task(
208-
start_mission_task_definition: StartMissionTaskDefinition,
171+
task_definition: StartMissionTaskDefinition,
209172
) -> ReturnToHome:
210-
return ReturnToHome(pose=start_mission_task_definition.pose.to_alitra_pose())
173+
return ReturnToHome(pose=task_definition.pose.to_alitra_pose())
211174

212175

213176
def create_dock_task() -> DockingProcedure:
214177
return DockingProcedure(behavior="dock")
215178

216179

217-
def get_duplicate_ids(items: List[TASKS]) -> List[str]:
218-
unique_ids: List[str] = []
219-
duplicate_ids: List[str] = []
220-
for item in items:
221-
id: str = item.id
222-
if id not in unique_ids:
223-
unique_ids.append(id)
224-
else:
225-
duplicate_ids.append(id)
226-
227-
return duplicate_ids
228-
229-
230180
def _build_mission_name() -> str:
231181
return f"{settings.PLANT_SHORT_NAME}{settings.ROBOT_NAME}{int(time.time())}"

src/isar/apis/schedule/scheduling_controller.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@
2121
from isar.services.utilities.scheduling_utilities import SchedulingUtilities
2222
from isar.state_machine.states_enum import States
2323
from robot_interface.models.mission.mission import Mission
24-
from robot_interface.models.mission.task import TASKS, Localize, MoveArm, ReturnToHome
24+
from robot_interface.models.mission.task import (
25+
TASKS,
26+
InspectionTask,
27+
Localize,
28+
MoveArm,
29+
ReturnToHome,
30+
)
2531

2632

2733
class SchedulingController:
@@ -115,7 +121,9 @@ def start_mission(
115121
self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
116122

117123
try:
118-
mission: Mission = to_isar_mission(mission_definition)
124+
mission: Mission = to_isar_mission(
125+
start_mission_definition=mission_definition, return_pose=return_pose
126+
)
119127
except MissionPlannerError as e:
120128
error_message = f"Bad Request - Cannot create ISAR mission: {e}"
121129
self.logger.warning(error_message)
@@ -127,9 +135,6 @@ def start_mission(
127135
self.scheduling_utilities.verify_robot_capable_of_mission(
128136
mission=mission, robot_capabilities=robot_settings.CAPABILITIES
129137
)
130-
if return_pose:
131-
pose: Pose = return_pose.to_alitra_pose()
132-
mission.tasks.append(ReturnToHome(pose=pose))
133138

134139
initial_pose_alitra: Optional[Pose] = (
135140
initial_pose.to_alitra_pose() if initial_pose else None
@@ -213,7 +218,9 @@ def drive_to(
213218
self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
214219

215220
pose: Pose = target_pose.to_alitra_pose()
216-
mission: Mission = Mission(tasks=[ReturnToHome(pose=pose)])
221+
mission: Mission = Mission(
222+
name="Drive to pose", tasks=[ReturnToHome(pose=pose)]
223+
)
217224

218225
self.logger.info(
219226
f"Starting drive to mission with ISAR Mission ID: '{mission.id}'"
@@ -237,7 +244,9 @@ def start_localization_mission(
237244
self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
238245

239246
pose: Pose = localization_pose.to_alitra_pose()
240-
mission: Mission = Mission(tasks=[Localize(localization_pose=pose)])
247+
mission: Mission = Mission(
248+
name="Localization mission", tasks=[Localize(localization_pose=pose)]
249+
)
241250

242251
self.logger.info(
243252
f"Starting localization mission with ISAR Mission ID: '{mission.id}'"
@@ -284,7 +293,9 @@ def start_move_arm_mission(
284293

285294
self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state)
286295

287-
mission: Mission = Mission(tasks=[MoveArm(arm_pose=arm_pose_literal)])
296+
mission: Mission = Mission(
297+
name="Move arm mission", tasks=[MoveArm(arm_pose=arm_pose_literal)]
298+
)
288299

289300
self.logger.info(
290301
f"Starting move arm mission with ISAR Mission ID: '{mission.id}'"
@@ -302,4 +313,11 @@ def _api_response(self, mission: Mission) -> StartMissionResponse:
302313
)
303314

304315
def _task_api_response(self, task: TASKS) -> TaskResponse:
305-
return TaskResponse(id=task.id, tag_id=task.tag_id, type=task.type)
316+
if isinstance(task, InspectionTask):
317+
inspection_id = task.inspection_id
318+
else:
319+
inspection_id = None
320+
321+
return TaskResponse(
322+
id=task.id, tag_id=task.tag_id, inspection_id=inspection_id, type=task.type
323+
)

0 commit comments

Comments
 (0)