def _key_by_uuid(self, new_pipettes: typing.Dict) -> typing.Dict: pipette_dict = {} for mount, data in new_pipettes.items(): token = uuid4() data['mount_axis'] = Axis.by_mount(mount) data['plunger_axis'] = Axis.of_plunger(mount) pipette_dict[token] = {**data} return pipette_dict
def make_pipette(mount, o): return pipettes.AttachedPipette( model=o.get('model'), name=o.get('name'), id=o.get('pipette_id'), mount_axis=str(Axis.by_mount(mount)).lower(), plunger_axis=str(Axis.of_plunger(mount)).lower(), tip_length=o.get('tip_length', 0) if o.get('model') else None)
async def get_attached_pipettes(request): """ Query robot for model strings on 'left' and 'right' mounts, and return a dict with the results keyed by mount. By default, this endpoint provides cached values, which will not interrupt a running session. WARNING: if the caller supplies the "refresh=true" query parameter, this method will interrupt a sequence of Smoothie operations that are in progress, such as a protocol run. Example: ``` { 'left': { 'model': 'p300_single_v1', 'name': 'p300_single', 'tip_length': 51.7, 'mount_axis': 'z', 'plunger_axis': 'b', 'id': '<pipette id string>' }, 'right': { 'model': 'p10_multi_v1', 'name': 'p10_multi', 'tip_length': 40, 'mount_axis': 'a', 'plunger_axis': 'c', 'id': '<pipette id string>' } } ``` If a pipette is "uncommissioned" (e.g.: does not have a model string written to on-board memory), or if no pipette is present, the corresponding mount will report `'model': null` """ hw = hw_from_req(request) if request.url.query.get('refresh') == 'true': await hw.cache_instruments() response = {} attached = hw.attached_instruments for mount, data in attached.items(): response[mount.name.lower()] = { 'model': data.get('model', None), 'name': data.get('name', None), 'id': data.get('pipette_id', None), 'mount_axis': str(Axis.by_mount(mount)).lower(), 'plunger_axis': str(Axis.of_plunger(mount)).lower() } if data.get('model'): response[mount.name.lower()]['tip_length'] \ = data.get('tip_length', 0) return web.json_response(response, status=200)
async def post_home_robot( robot_home_target: control.RobotHomeTarget, hardware: ThreadManager = Depends(get_hardware), motion_lock: ThreadedAsyncLock = Depends(get_motion_lock)) \ -> V1BasicResponse: """Home the robot or one of the pipettes""" try: async with motion_lock.forbid(): mount = robot_home_target.mount target = robot_home_target.target home = hardware.home # type: ignore home_plunger = hardware.home_plunger # type: ignore if target == control.HomeTarget.pipette and mount: await home([Axis.by_mount(Mount[mount.upper()])]) await home_plunger(Mount[mount.upper()]) message = f"Pipette on {mount} homed successfully" elif target == control.HomeTarget.robot: await home() message = "Homing robot." else: raise V1HandlerError(message=f"{target} is invalid", status_code=status.HTTP_400_BAD_REQUEST) return V1BasicResponse(message=message) except ThreadedAsyncForbidden as e: raise V1HandlerError(status_code=status.HTTP_403_FORBIDDEN, message=str(e))
async def post_home_robot( robot_home_target: control.RobotHomeTarget, hardware: HardwareAPILike = Depends(get_hardware), motion_lock: ThreadedAsyncLock = Depends(get_motion_lock)) \ -> V1BasicResponse: """Home the robot or one of the pipettes""" async with motion_lock: mount = robot_home_target.mount target = robot_home_target.target home = hardware.home # type: ignore home_plunger = hardware.home_plunger # type: ignore if target == control.HomeTarget.pipette and mount: await home([Axis.by_mount(Mount[mount.upper()])]) await home_plunger(Mount[mount.upper()]) message = f"Pipette on {mount} homed successfully" elif target == control.HomeTarget.robot: await home() message = "Homing robot." else: raise V1HandlerError(message=f"{target} is invalid", status_code=400) return V1BasicResponse(message=message)
def test_home_pipette(api_client, hardware_home, mount_name, mount): test_data = {'target': 'pipette', 'mount': mount_name} res = api_client.post('/robot/home', json=test_data) assert res.json() == { "message": f"Pipette on {mount_name}" f" homed successfully" } assert res.status_code == 200 hardware_home.home.assert_called_once_with([Axis.by_mount(mount)]) hardware_home.home_plunger.assert_called_once_with(mount)
async def home(request): """ This initializes a call to pipette.home() which, as a side effect will: 1. Check the pipette is actually connected (will throw an error if you try to home a non-connected pipette) 2. Re-engages the motor :param request: Information obtained from a POST request. The content type is application/json. The correct packet form should be as follows: { 'target': Can be, 'robot' or 'pipette' 'mount': 'left' or 'right', only used if target is pipette } :return: A success or non-success message. """ hw = hw_from_req(request) req = await request.text() data = json.loads(req) target = data.get('target') if target == 'robot': if ff.use_protocol_api_v2(): await hw.home() else: hw.home() status = 200 message = "Homing robot." elif target == 'pipette': mount = data.get('mount') if mount in ['left', 'right']: if ff.use_protocol_api_v2(): await hw.home([Axis.by_mount(Mount[mount.upper()])]) await hw.home_plunger(Mount[mount.upper()]) status = 200 message = 'Pipette on {} homed successfuly'.format(mount) else: pipette, should_remove = _fetch_or_create_pipette(hw, mount) pipette.home() if should_remove: hw.remove_instrument(mount) status = 200 message = "Pipette on {} homed successfully.".format(mount) else: status = 400 message = "Expected 'left' or 'right' as values for mount" \ "got {} instead.".format(mount) else: status = 400 message = "Expected 'robot' or 'pipette' got {}.".format(target) return web.json_response({"message": message}, status=status)
def _prepare_for_lid_move(self): loaded_instruments = [ instr for mount, instr in self._ctx.loaded_instruments.items() if instr is not None ] try: instr = loaded_instruments[0] except IndexError: MODULE_LOG.warning( "Cannot assure a safe gantry position to avoid colliding" " with the lid of the Thermocycler Module.") else: self._ctx._hw_manager.hardware.retract(instr._mount) high_point = self._ctx._hw_manager.hardware.current_position( instr._mount) trash_top = self._ctx.fixed_trash.wells()[0].top() safe_point = trash_top.point._replace( z=high_point[Axis.by_mount(instr._mount)]) instr.move_to(types.Location(safe_point, None), force_direct=True)