diff --git a/src/isar/apis/api.py b/src/isar/apis/api.py index 43b26d9a..69f4d877 100644 --- a/src/isar/apis/api.py +++ b/src/isar/apis/api.py @@ -243,6 +243,28 @@ def _create_scheduler_router(self) -> APIRouter: }, }, ) + router.add_api_route( + path="/schedule/move_arm/{arm_pose_literal}", + endpoint=self.scheduling_controller.start_move_arm_mission, + methods=["POST"], + dependencies=[authentication_dependency], + summary="Move arm to the given arm pose literal", + responses={ + HTTPStatus.OK.value: { + "description": "Move arm mission successfully started", + }, + HTTPStatus.BAD_REQUEST.value: { + "description": "A move arm mission was scheduled on a robot that " + "does not support it or the input was incorrect", + }, + HTTPStatus.CONFLICT.value: { + "description": "Conflict - Invalid command in the current state", + }, + HTTPStatus.INTERNAL_SERVER_ERROR.value: { + "description": "Internal Server Error - Current state of state machine unknown", + }, + }, + ) return router diff --git a/src/isar/apis/schedule/scheduling_controller.py b/src/isar/apis/schedule/scheduling_controller.py index 4fbfbba8..b7105ef2 100644 --- a/src/isar/apis/schedule/scheduling_controller.py +++ b/src/isar/apis/schedule/scheduling_controller.py @@ -21,7 +21,7 @@ from isar.services.utilities.scheduling_utilities import SchedulingUtilities from isar.state_machine.states_enum import States from robot_interface.models.mission.mission import Mission -from robot_interface.models.mission.step import DriveToPose, Localize +from robot_interface.models.mission.step import DriveToPose, Localize, MoveArm from robot_interface.models.mission.task import Task @@ -37,7 +37,6 @@ def __init__( def start_mission_by_id( self, mission_id: str = Path( - ..., alias="id", title="Mission ID", description="ID-number for predefined mission", @@ -254,6 +253,54 @@ def start_localization_mission( ) return self._api_response(mission) + def start_move_arm_mission( + self, + arm_pose_literal: str = Path( + ..., + alias="arm_pose_literal", + title="Arm pose literal", + description="Arm pose as a literal", + ), + ) -> StartMissionResponse: + self.logger.info("Received request to start new move arm mission") + + if not robot_settings.VALID_ARM_POSES: + error_message: str = ( + f"Received a request to move the arm but the robot " + f"{settings.ROBOT_NAME} does not support moving an arm" + ) + self.logger.warning(error_message) + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, detail=error_message + ) + + if arm_pose_literal not in robot_settings.VALID_ARM_POSES: + error_message = ( + f"Received a request to move the arm but the arm pose " + f"{arm_pose_literal} is not supported by the robot " + f"{settings.ROBOT_NAME}" + ) + self.logger.warning(error_message) + raise HTTPException( + status_code=HTTPStatus.BAD_REQUEST, detail=error_message + ) + + state: States = self.scheduling_utilities.get_state() + + self.scheduling_utilities.verify_state_machine_ready_to_receive_mission(state) + + step: MoveArm = MoveArm(arm_pose=arm_pose_literal) + mission: Mission = Mission(tasks=[Task(steps=[step])]) + + self.logger.info( + f"Starting move arm mission with ISAR Mission ID: '{mission.id}'" + ) + self.scheduling_utilities.start_mission( + mission=mission, + initial_pose=None, + ) + return self._api_response(mission) + def get_info(self): return RobotInfoResponse( robot_package=settings.ROBOT_PACKAGE, diff --git a/src/isar/config/settings.py b/src/isar/config/settings.py index bc9a7a98..b6c5ccda 100644 --- a/src/isar/config/settings.py +++ b/src/isar/config/settings.py @@ -1,6 +1,6 @@ import importlib.resources as pkg_resources import os -from typing import List +from typing import List, Optional from dotenv import load_dotenv from pydantic import BaseSettings, Field, validator @@ -311,6 +311,12 @@ def __init__(self) -> None: # This should be set in the robot package settings.env file ROBOT_MODEL: RobotModel = Field(default=RobotModel.Robot) # type: ignore + # Valid arm poses that the robot may utilize + # This should be set in the robot package settings.env file + # Note that if the robot does not support moving an arm this will be None and + # the functionality will be unavailable + VALID_ARM_POSES: Optional[List[str]] = Field(default=None) + class Config: env_file_encoding = "utf-8" case_sensitive = True diff --git a/src/robot_interface/models/mission/step.py b/src/robot_interface/models/mission/step.py index 5ddd8b13..5e266654 100644 --- a/src/robot_interface/models/mission/step.py +++ b/src/robot_interface/models/mission/step.py @@ -122,6 +122,16 @@ class Localize(MotionStep): type: Literal["localize"] = "localize" +@dataclass +class MoveArm(MotionStep): + """ + Step which causes the robot to move its arm + """ + + arm_pose: str + type: Literal["move_arm"] = "move_arm" + + @dataclass class TakeImage(InspectionStep): """ @@ -210,4 +220,5 @@ def get_inspection_type() -> Type[Inspection]: TakeVideo, TakeThermalVideo, RecordAudio, + MoveArm, ]