示例#1
0
 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
示例#2
0
 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)
示例#3
0
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)
示例#4
0
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))
示例#5
0
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)
示例#6
0
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)
示例#7
0
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)