Ejemplo n.º 1
0
async def session_command_execute_handler(
    session_id: route_models.IdentifierType,
    command_request: route_models.CommandRequest,
    session_manager: SessionManager = Depends(get_session_manager),
) -> route_models.CommandResponse:
    """
    Execute a session command
    """
    session_obj = get_session(manager=session_manager,
                              session_id=session_id,
                              api_router=router)
    if not session_manager.is_active(session_obj.meta.identifier):
        raise RobotServerError(
            status_code=http_status_codes.HTTP_403_FORBIDDEN,
            error=Error(
                title=f"Session '{session_id}' is not active",
                detail="Only the active session can execute commands",
            ))

    try:
        command = create_command(command_request.data.attributes.command,
                                 command_request.data.attributes.data)
        command_result = await session_obj.command_executor.execute(command)
        log.debug(f"Command completed {command}")
    # TODO: Amit 07/30/2020 - SessionCommandException should be a
    #  RobotServerError. Customized by raiser with status code and rest of
    #  Error attributes. The job here would be to log and re-raise.
    except CommandExecutionConflict as e:
        log.exception("Failed to execute command due to conflict")
        raise RobotServerError(status_code=http_status_codes.HTTP_409_CONFLICT,
                               error=Error(
                                   title="Command execution conflict",
                                   detail=str(e),
                               ))
    except SessionCommandException as e:
        log.exception("Failed to execute command")
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Command execution error",
                detail=str(e),
            ))

    return route_models.CommandResponse(data=ResponseDataModel.create(
        attributes=route_models.SessionCommand(
            data=command_result.content.data,
            command=command_result.content.name,
            status=command_result.result.status,
            createdAt=command_result.meta.created_at,
            startedAt=command_result.result.started_at,
            completedAt=command_result.result.completed_at),
        resource_id=command_result.meta.identifier),
                                        links=get_valid_session_links(
                                            session_id, router))
Ejemplo n.º 2
0
async def create_session_handler(
        create_request: route_models.SessionCreateRequest,
        session_manager: SessionManager = Depends(get_session_manager)) \
        -> route_models.SessionResponse:
    """Create a session"""
    session_type = create_request.data.attributes.sessionType
    create_params = create_request.data.attributes.createParams
    try:
        new_session = await session_manager.add(
            session_type=session_type,
            session_meta_data=SessionMetaData(create_params=create_params))
    # TODO: Amit 07/30/2020 - SessionCreationException should be a
    #  RobotServerError. Customized by raiser with status code and rest of
    #  Error attributes. The job here would be to log and re-raise.
    except SessionCreationException as e:
        log.exception("Failed to create session")
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Creation Failed",
                detail=f"Failed to create session of type "
                f"'{session_type}': {str(e)}.",
            ))
    return route_models.SessionResponse(data=ResponseDataModel.create(
        attributes=new_session.get_response_model(),
        resource_id=new_session.meta.identifier),
                                        links=get_valid_session_links(
                                            new_session.meta.identifier,
                                            router))
Ejemplo n.º 3
0
async def delete_session_handler(
        session_id: route_models.IdentifierType,
        session_manager: SessionManager = Depends(get_session_manager)) \
        -> route_models.SessionResponse:
    """Delete a session"""
    session_obj = get_session(manager=session_manager,
                              session_id=session_id,
                              api_router=router)

    try:
        await session_manager.remove(session_obj.meta.identifier)
    # TODO: Amit 07/30/2020 - SessionException should be a RobotServerError.
    #  Customized by raiser with status code and rest of
    #  Error attributes. The job here would be to log and re-raise.
    except SessionException as e:
        log.exception("Failed to remove a session session")
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Removal Failed",
                detail=f"Failed to remove session "
                f"'{session_id}': {str(e)}.",
            ))

    return route_models.SessionResponse(
        data=ResponseDataModel.create(
            attributes=session_obj.get_response_model(),
            resource_id=session_id),
        links={
            "POST":
            ResourceLink(
                href=router.url_path_for(create_session_handler.__name__)),
        })
Ejemplo n.º 4
0
    def __init__(self,
                 hardware: ThreadManager,
                 mount: Mount,
                 has_calibration_block: bool,
                 tip_rack: 'LabwareDefinition'):
        self._hardware = hardware
        self._mount = mount
        self._has_calibration_block = has_calibration_block
        self._hw_pipette = self._hardware._attached_instruments[mount]
        if not self._hw_pipette:
            raise RobotServerError(
                definition=CalibrationError.NO_PIPETTE_ON_MOUNT,
                mount=mount)
        self._tip_origin_pt: Optional[Point] = None
        self._nozzle_height_at_reference: Optional[float] = None

        deck_load_name = SHORT_TRASH_DECK if ff.short_fixed_trash() \
            else STANDARD_DECK
        self._deck = deck.Deck(load_name=deck_load_name)
        self._tip_rack = self._get_tip_rack_lw(tip_rack)
        self._initialize_deck()

        self._current_state = State.sessionStarted
        self._state_machine = TipCalibrationStateMachine()

        self._command_map: COMMAND_MAP = {
            CalibrationCommand.load_labware: self.load_labware,
            CalibrationCommand.jog: self.jog,
            CalibrationCommand.pick_up_tip: self.pick_up_tip,
            CalibrationCommand.invalidate_tip: self.invalidate_tip,
            CalibrationCommand.save_offset: self.save_offset,
            TipLengthCalibrationCommand.move_to_reference_point: self.move_to_reference_point,  # noqa: E501
            CalibrationCommand.move_to_tip_rack: self.move_to_tip_rack,  # noqa: E501
            CalibrationCommand.exit: self.exit_session,
        }
Ejemplo n.º 5
0
    def __init__(self, hardware: ThreadManager, mount: Mount = Mount.RIGHT):
        self._hardware = hardware
        self._mount = mount
        self._hw_pipette = self._hardware._attached_instruments[mount]
        if not self._hw_pipette:
            raise RobotServerError(
                definition=CalibrationError.NO_PIPETTE_ON_MOUNT, mount=mount)

        deck_load_name = SHORT_TRASH_DECK if ff.short_fixed_trash() \
            else STANDARD_DECK
        self._deck = deck.Deck(load_name=deck_load_name)
        self._tip_rack = self._get_tip_rack_lw()
        self._deck[TIP_RACK_SLOT] = self._tip_rack

        self._current_state = State.sessionStarted
        self._state_machine = PipetteOffsetCalibrationStateMachine()

        point_one_pos = \
            self._deck.get_calibration_position(POINT_ONE_ID).position
        self._cal_ref_point = Point(*point_one_pos)

        self._tip_origin_pt: Optional[Point] = None

        self._command_map: COMMAND_MAP = {
            CalibrationCommand.load_labware: self.load_labware,
            CalibrationCommand.jog: self.jog,
            CalibrationCommand.pick_up_tip: self.pick_up_tip,
            CalibrationCommand.invalidate_tip: self.invalidate_tip,
            CalibrationCommand.save_offset: self.save_offset,
            CalibrationCommand.move_to_tip_rack: self.move_to_tip_rack,
            CalibrationCommand.move_to_deck: self.move_to_deck,
            CalibrationCommand.move_to_point_one: self.move_to_point_one,
            CalibrationCommand.exit: self.exit_session,
        }
Ejemplo n.º 6
0
async def delete_specific_labware_calibration(
        calibrationId: cal_types.CalibrationID):
    try:
        delete.delete_offset_file(calibrationId)
    except (FileNotFoundError, KeyError):
        error = Error(title='{calibrationId} does not exist.')
        raise RobotServerError(status_code=status.HTTP_404_NOT_FOUND,
                               error=error)
Ejemplo n.º 7
0
 def _get_tip_rack_lw(self,
                      tip_rack_def: 'LabwareDefinition') -> labware.Labware:
     try:
         return labware.load_from_definition(
             tip_rack_def,
             self._deck.position_for(TIP_RACK_SLOT))
     except Exception:
         raise RobotServerError(definition=CalibrationError.BAD_LABWARE_DEF)
Ejemplo n.º 8
0
async def get_specific_pipette_offset_calibration(pipette_id: str,
                                                  mount: pip_models.MountType):
    try:
        delete.delete_pipette_offset_file(pipette_id,
                                          ot_types.Mount[mount.upper()])
    except FileNotFoundError:
        raise RobotServerError(definition=CommonErrorDef.RESOURCE_NOT_FOUND,
                               resource='PipetteOffsetCalibration',
                               id=f"{pipette_id}&{mount}")
Ejemplo n.º 9
0
async def create_session_handler(
        create_request: SessionCreateRequest,
        session_manager: SessionManager = Depends(get_session_manager),
        hardware=Depends(get_hardware)) \
        -> session.SessionResponse:
    """Create a session"""
    session_type = create_request.data.attributes.sessionType
    # TODO We use type as ID while we only support one session type.
    session_id = session_type.value

    current_session = session_manager.sessions.get(session_id)
    if not current_session:
        try:
            # TODO generalize for other kinds of sessions
            new_session = await CheckCalibrationSession.build(hardware)
        except (AssertionError, CalibrationException) as e:
            raise RobotServerError(
                status_code=http_status_codes.HTTP_400_BAD_REQUEST,
                error=Error(
                    title="Creation Failed",
                    detail=f"Failed to create session of type "
                    f"'{session_type}': {str(e)}.",
                ))
        session_manager.sessions[session_id] = new_session
        return session.SessionResponse(
            data=ResponseDataModel.create(attributes=session.Session(
                sessionType=session_type,
                details=create_session_details(new_session)),
                                          resource_id=session_id),
            links=get_valid_session_links(session_id, router))
    else:
        raise RobotServerError(
            status_code=http_status_codes.HTTP_409_CONFLICT,
            error=Error(
                title="Conflict",
                detail=f"A session with id '{session_id}' already exists. "
                f"Please delete to proceed.",
                links={
                    "DELETE":
                    router.url_path_for(delete_session_handler.__name__,
                                        session_id=session_id)
                }))
Ejemplo n.º 10
0
def get_session(manager: SessionManager, session_id: IdentifierType,
                api_router: APIRouter) -> BaseSession:
    """Get the session or raise a RobotServerError"""
    found_session = manager.get_by_id(session_id)
    if not found_session:
        # There is no session raise error
        raise RobotServerError(definition=CommonErrorDef.RESOURCE_NOT_FOUND,
                               links=get_sessions_links(api_router),
                               resource='session',
                               id=session_id)
    return found_session
Ejemplo n.º 11
0
async def session_command_execute_handler(
    session_id: route_models.IdentifierType,
    command_request: route_models.CommandRequest,
    session_manager: SessionManager = Depends(get_session_manager),
) -> route_models.CommandResponse:
    """
    Execute a session command
    """
    session_obj = get_session(manager=session_manager,
                              session_id=session_id,
                              api_router=router)
    if not session_manager.is_active(session_obj.meta.identifier):
        raise RobotServerError(
            status_code=http_status_codes.HTTP_403_FORBIDDEN,
            error=Error(
                title=f"Session '{session_id}' is not active",
                detail="Only the active session can execute commands",
            ))

    try:
        command = create_command(command_request.data.attributes.command,
                                 command_request.data.attributes.data)
        command_result = await session_obj.command_executor.execute(command)
        log.debug(f"Command completed {command}")
    except SessionCommandException as e:
        log.exception("Failed to execute command")
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Command execution error",
                detail=str(e),
            ))

    return route_models.CommandResponse(data=ResponseDataModel.create(
        attributes=route_models.SessionCommand(
            data=command_result.content.data,
            command=command_result.content.name,
            status=command_result.result.status),
        resource_id=command_result.meta.identifier),
                                        links=get_valid_session_links(
                                            session_id, router))
Ejemplo n.º 12
0
async def get_specific_labware_calibration(
        calibrationId: str) -> lw_models.SingleCalibrationResponse:
    calibration: Optional[cal_types.CalibrationInformation] = None
    for cal in get_cal.get_all_calibrations():
        if calibrationId == cal.labware_id:
            calibration = cal
            break
    if not calibration:
        error = Error(title='{calibrationId} does not exist.')
        raise RobotServerError(status_code=status.HTTP_404_NOT_FOUND,
                               error=error)

    formatted_calibrations = _format_calibrations([calibration])
    return lw_models.SingleCalibrationResponse(
        data=formatted_calibrations[0])
Ejemplo n.º 13
0
def get_session(manager: SessionManager, session_id: str,
                api_router: APIRouter) -> CalibrationSession:
    """Get the session or raise a RobotServerError"""
    found_session = manager.sessions.get(session_id)
    if not found_session:
        # There is no session raise error
        raise RobotServerError(
            status_code=http_status_codes.HTTP_404_NOT_FOUND,
            error=Error(title="No session",
                        detail=f"Cannot find session with id '{session_id}'.",
                        links={
                            "POST":
                            api_router.url_path_for(
                                create_session_handler.__name__)
                        }))
    return found_session
Ejemplo n.º 14
0
    def _look_up_state(self) -> CalibrationCheckState:
        """
        We want to check whether a tip pick up was dangerous during the
        tip inspection state, but the reference points are actually saved
        during the preparing pipette state, so we should reference those
        states when looking up the reference point.

        :return: The calibration check state that the reference point
        was saved under for tip pick up.
        """
        if self.current_state_name == CalibrationCheckState.inspectingFirstTip:
            return CalibrationCheckState.preparingFirstPipette
        elif self.current_state_name == \
                CalibrationCheckState.inspectingSecondTip:
            return CalibrationCheckState.preparingSecondPipette
        else:
            raise RobotServerError(
                definition=CalibrationError.NO_STATE_TRANSITION,
                state=self.current_state_name)
Ejemplo n.º 15
0
 async def add(
     self,
     session_type: SessionType,
     session_meta_data: SessionMetaData,
 ) -> BaseSession:
     """Add a new session"""
     cls = SessionTypeToClass.get(session_type)
     if not cls:
         raise SessionCreationException("Session type is not supported")
     session = await cls.create(configuration=self._session_common,
                                instance_meta=session_meta_data)
     if session.meta.identifier in self._sessions:
         raise RobotServerError(
             definition=CommonErrorDef.RESOURCE_ALREADY_EXISTS,
             resource="session",
             id=session.meta.identifier)
     self._sessions[session.meta.identifier] = session
     self._active.active_id = session.meta.identifier
     log.debug(f"Added new session: {session}")
     return session
Ejemplo n.º 16
0
async def session_command_create_handler(
    session_id: str,
    command_request: session.CommandRequest,
    session_manager: SessionManager = Depends(get_session_manager),
) -> session.CommandResponse:
    """
    Process a session command
    """
    session_obj = typing.cast(
        CheckCalibrationSession,
        get_session(manager=session_manager,
                    session_id=session_id,
                    api_router=router))
    command = command_request.data.attributes.command
    command_data = command_request.data.attributes.data
    try:
        await session_obj.trigger_transition(
            trigger=command.value,
            **(command_data.dict() if command_data else {}))
    except (AssertionError, StateMachineError) as e:
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Exception",
                detail=str(e),
            ))

    return session.CommandResponse(
        data=ResponseDataModel.create(
            attributes=session.SessionCommand(data=command_data,
                                              command=command,
                                              status='accepted'),
            # TODO have session create id for command for later querying
            resource_id=str(uuid4())),
        meta=session.Session(
            details=create_session_details(session_obj),
            # TODO Get type from session
            sessionType=models.SessionType.calibration_check),
        links=get_valid_session_links(session_id, router))
Ejemplo n.º 17
0
async def create_session_handler(
        create_request: route_models.SessionCreateRequest,
        session_manager: SessionManager = Depends(get_session_manager)) \
        -> route_models.SessionResponse:
    """Create a session"""
    session_type = create_request.data.attributes.sessionType
    try:
        new_session = await session_manager.add(session_type)
    except SessionCreationException as e:
        log.exception("Failed to create session")
        raise RobotServerError(
            status_code=http_status_codes.HTTP_400_BAD_REQUEST,
            error=Error(
                title="Creation Failed",
                detail=f"Failed to create session of type "
                f"'{session_type}': {str(e)}.",
            ))
    return route_models.SessionResponse(data=ResponseDataModel.create(
        attributes=new_session.get_response_model(),
        resource_id=new_session.meta.identifier),
                                        links=get_valid_session_links(
                                            new_session.meta.identifier,
                                            router))
Ejemplo n.º 18
0
    def _select_target_pipette(self) -> Tuple[Pipette, Mount]:
        """
        Select pipette for calibration based on:
        1: smaller max volume
        2: single-channel over multi
        3: right mount over left
        """
        if not any(self._hardware._attached_instruments.values()):
            raise RobotServerError(
                definition=CalibrationError.NO_PIPETTE_ATTACHED,
                flow='Deck Calibration')
        pips = {m: p for m, p in self._hardware._attached_instruments.items()
                if p}
        if len(pips) == 1:
            for mount, pip in pips.items():
                return pip, mount

        right_pip = pips[Mount.RIGHT]
        left_pip = pips[Mount.LEFT]
        if right_pip.config.max_volume > left_pip.config.max_volume or \
                right_pip.config.channels > left_pip.config.channels:
            return left_pip, Mount.LEFT
        else:
            return right_pip, Mount.RIGHT
Ejemplo n.º 19
0
 def _get_pip_info_by_mount(
         new_pipettes: typing.Dict[Mount, Pipette.DictType]) \
         -> typing.Dict[Mount, PipetteInfo]:
     pip_info_by_mount = {}
     attached_pips = {m: p for m, p in new_pipettes.items() if p}
     num_pips = len(attached_pips)
     if num_pips > 0:
         for mount, data in attached_pips.items():
             if data:
                 rank = PipetteRank.first
                 if num_pips == 2 and mount == Mount.LEFT:
                     rank = PipetteRank.second
                 cp = None
                 if data['channels'] == 8:
                     cp = CriticalPoint.FRONT_NOZZLE
                 pip_info_by_mount[mount] = PipetteInfo(tiprack_id=None,
                                                        critical_point=cp,
                                                        rank=rank,
                                                        mount=mount)
         return pip_info_by_mount
     else:
         raise RobotServerError(
             definition=CalibrationError.NO_PIPETTE_ATTACHED,
             flow='calibration check')
Ejemplo n.º 20
0
async def delete_access_token(access_token: access.TokenType) \
        -> access.AccessTokenResponse:
    raise RobotServerError(status_code=status_codes.HTTP_501_NOT_IMPLEMENTED,
                           error=Error(title="Not implemented"))
Ejemplo n.º 21
0
async def post_control(scope: access.ControlScope,
                       access_token: access.AccessTokenRequest) \
        -> access.AccessTokenResponse:
    raise RobotServerError(definition=CommonErrorDef.NOT_IMPLEMENTED)
Ejemplo n.º 22
0
async def put_control(scope: access.ControlScope,
                      token: access.TokenType) \
        -> access.AccessTokenResponse:
    raise RobotServerError(status_code=status_codes.HTTP_501_NOT_IMPLEMENTED,
                           error=Error(title="Not implemented"))
Ejemplo n.º 23
0
async def get_access_tokens() -> access.MultipleAccessTokenResponse:
    raise RobotServerError(definition=CommonErrorDef.NOT_IMPLEMENTED)
Ejemplo n.º 24
0
async def delete_access_token(access_token: access.TokenType) \
        -> access.AccessTokenResponse:
    raise RobotServerError(definition=CommonErrorDef.NOT_IMPLEMENTED)
Ejemplo n.º 25
0
async def get_access_tokens() -> access.MultipleAccessTokenResponse:
    raise RobotServerError(status_code=status_codes.HTTP_501_NOT_IMPLEMENTED,
                           error=Error(title="Not implemented"))
Ejemplo n.º 26
0
async def release_control(scope: access.ControlScope,
                          token: access.TokenType) \
        -> access.AccessTokenResponse:
    raise RobotServerError(definition=CommonErrorDef.NOT_IMPLEMENTED)