Example #1
0
async def test_synch_adapter():
    thread_manager = ThreadManager(API.build_hardware_simulator)
    synch = thread_manager.sync
    synch.cache_instruments({Mount.LEFT: 'p10_single'})
    assert synch.attached_instruments[Mount.LEFT]['name']\
                .startswith('p10_single')
    thread_manager.clean_up()
Example #2
0
async def test_hw_manager(loop):
    # When built without an input it should build its own adapter
    mgr = HardwareManager(None)
    adapter = mgr.hardware
    # When "disconnecting" from its own simulator, the adapter should
    # be stopped and a new one created
    new = mgr.reset_hw()
    assert new is not adapter
    # When deleted, the self-created adapter should be stopped
    del mgr

    # When built with a hardware API input it should wrap it with a new
    # synchronous adapter and not build its own API
    sim = await API.build_hardware_simulator(loop=loop)
    mgr = HardwareManager(sim)
    assert isinstance(mgr.hardware, adapters.SynchronousAdapter)
    passed = mgr.hardware
    # When disconnecting from a real external adapter, it should create
    # its own simulator and should
    new = mgr.reset_hw()
    assert new is not passed

    thread_manager = ThreadManager(API.build_hardware_simulator)
    sa = thread_manager.sync
    # When connecting to an adapter it shouldn’t rewrap it
    assert mgr.set_hw(sa) is sa
    del mgr
    thread_manager.clean_up()
Example #3
0
async def initialize_robot() -> ThreadManager:
    if os.environ.get("ENABLE_VIRTUAL_SMOOTHIE"):
        log.info("Initialized robot using virtual Smoothie")
        return ThreadManager(API.build_hardware_simulator)
    packed_smoothie_fw_file, packed_smoothie_fw_ver = _find_smoothie_file()
    systemd.daemon.notify("READY=1")
    hardware = ThreadManager(API.build_hardware_controller,
                             threadmanager_nonblocking=True,
                             firmware=(packed_smoothie_fw_file,
                                       packed_smoothie_fw_ver))
    try:
        await hardware.managed_thread_ready_async()
    except RuntimeError:
        log.exception('Could not build hardware controller, forcing virtual')
        return ThreadManager(API.build_hardware_simulator)

    loop = asyncio.get_event_loop()

    async def blink():
        while True:
            await hardware.set_lights(button=True)
            await asyncio.sleep(0.5)
            await hardware.set_lights(button=False)
            await asyncio.sleep(0.5)

    blink_task = loop.create_task(blink())

    if not ff.disable_home_on_boot():
        log.info("Homing Z axes")
        await hardware.home_z()

    blink_task.cancel()
    await hardware.set_lights(button=True)

    return hardware
Example #4
0
def sync_hardware(request, loop, virtual_smoothie_env):
    thread_manager = ThreadManager(API.build_hardware_controller)
    hardware = thread_manager.sync
    try:
        yield hardware
    finally:
        hardware.reset()
        hardware.set_config(config.robot_configs.load())
        thread_manager.clean_up()
Example #5
0
async def initialize_robot() -> ThreadManager:
    if os.environ.get("ENABLE_VIRTUAL_SMOOTHIE"):
        log.info("Initialized robot using virtual Smoothie")
        return ThreadManager(API.build_hardware_simulator)

    await check_for_smoothie_update()

    hardware = ThreadManager(API.build_hardware_controller)

    if not ff.disable_home_on_boot():
        log.info("Homing Z axes")
        await hardware.home_z()

    return hardware
Example #6
0
def main():
    """ The main entrypoint for the Opentrons robot API server stack.

    This function
    - creates and starts the server for both the RPC routes
      handled by :py:mod:`opentrons.server.rpc` and the HTTP routes handled
      by :py:mod:`opentrons.server.http`
    - initializes the hardware interaction handled by either
      :py:mod:`opentrons.legacy_api` or :py:mod:`opentrons.hardware_control`

    This function does not return until the server is brought down.
    """
    app_settings = get_settings()

    if app_settings.simulator_configuration_file_path:
        # A path to a simulation configuration is defined. Let's use it.
        checked_hardware = ThreadManager(
            load_simulator,
            Path(app_settings.simulator_configuration_file_path))
    else:
        # Create the hardware
        checked_hardware = initialize_api(
            hardware_server=app_settings.hardware_server_enable,
            hardware_server_socket=app_settings.hardware_server_socket_path)

    run(hardware=checked_hardware,
        hostname=app_settings.ws_host_name,
        port=app_settings.ws_port,
        path=app_settings.ws_domain_socket)
Example #7
0
async def test_session_no_pipettes_error():
    simulator = ThreadManager(API.build_hardware_simulator)

    with pytest.raises(RobotServerError) as e:
        await session.CheckCalibrationSession.build(simulator)

    assert e.value.status_code == 403
    assert e.value.error.title == "No Pipette Attached"
async def get_calibration_status(hardware: ThreadManager = Depends(
    get_hardware)) -> CalibrationStatus:
    robot_conf = robot_configs.load()
    return CalibrationStatus(
        deckCalibration=DeckCalibrationStatus(
            status=hardware.validate_calibration(),
            data=robot_conf.gantry_calibration),
        instrumentCalibration=robot_conf.instrument_offset)
Example #9
0
 def __init__(self, hardware: Optional[HardwareToManage]):
     if hardware is None:
         self._current = ThreadManager(API.build_hardware_simulator).sync
     elif isinstance(hardware, SynchronousAdapter):
         self._current = hardware
     elif isinstance(hardware, ThreadManager):
         self._current = hardware.sync
     else:
         self._current = SynchronousAdapter(hardware)
Example #10
0
 def __init__(self, hardware: ThreadManager):
     self._hardware = hardware
     self._relate_mount = self._key_by_uuid(
         hardware.get_attached_instruments()
     )
     self._deck = geometry.Deck()
     self._slot_options = ['8', '6']
     self._labware_info = self._determine_required_labware()
     self._moves = self._build_deck_moves()
Example #11
0
async def check_calibration_session_only_left(loop) \
        -> session.CheckCalibrationSession:
    attached_instruments = {
        types.Mount.LEFT: {
            'model': 'p300_single_v1',
            'id': 'fake300pip'
        }
    }
    simulator = ThreadManager(API.build_hardware_simulator,
                              attached_instruments=attached_instruments)
    return await session.CheckCalibrationSession.build(simulator)
Example #12
0
    def __init__(self,
                 hardware: ThreadManager,
                 lights_on_before: bool = False):
        self._hardware = hardware
        self._lights_on_before = lights_on_before

        deck_load_name = SHORT_TRASH_DECK if ff.short_fixed_trash() \
            else STANDARD_DECK
        self._deck = geometry.Deck(load_name=deck_load_name)
        self._pip_info_by_mount = self._get_pip_info_by_mount(
            hardware.get_attached_instruments())
        self._labware_info = self._determine_required_labware()
        self._moves = self._build_deck_moves()
Example #13
0
async def hardware(request, loop, virtual_smoothie_env):
    hw_sim = ThreadManager(API.build_hardware_simulator)
    old_config = config.robot_configs.load()
    try:
        yield hw_sim
    finally:
        config.robot_configs.clear()
        hw_sim.set_config(old_config)
        hw_sim.clean_up()
Example #14
0
 def __init__(self, hardware: ThreadManager):
     self._hardware = hardware
     self._deck = geometry.Deck()
     self._pip_info_by_mount = self._get_pip_info_by_mount(
             hardware.get_attached_instruments())
     if ff.short_fixed_trash():
         trash_lw = labware.load(
             'opentrons_1_trash_850ml_fixed',
             self._deck.position_for('12'))
     else:
         trash_lw = labware.load(
             'opentrons_1_trash_1100ml_fixed',
             self._deck.position_for('12'))
     self._deck['12'] = trash_lw
     self._trash_lw = trash_lw
     self._labware_info = self._determine_required_labware()
     self._moves = self._build_deck_moves()
Example #15
0
async def get_calibration_status(hardware: ThreadManager = Depends(
    get_hardware)) -> CalibrationStatus:
    robot_conf = robot_configs.load()
    if ff.enable_calibration_overhaul():
        deck_cal = hardware.robot_calibration.deck_calibration
        deck_cal_data = DeckCalibrationData(
            type=MatrixType.attitude,
            matrix=deck_cal.attitude,
            lastModified=deck_cal.last_modified,
            pipetteCalibratedWith=deck_cal.pipette_calibrated_with,
            tiprack=deck_cal.tiprack)
    else:
        deck_cal_data = DeckCalibrationData(
            type=MatrixType.affine, matrix=robot_conf.gantry_calibration)

    return CalibrationStatus(
        deckCalibration=DeckCalibrationStatus(
            status=hardware.validate_calibration(), data=deck_cal_data),
        instrumentCalibration=robot_conf.instrument_offset)
Example #16
0
def main(loop=None):
    """
    A CLI application for performing factory calibration of an Opentrons robot

    Instructions:
        - Robot must be set up with two 300ul or 50ul single-channel pipettes
          installed on the right-hand and left-hand mount.
        - Put a GEB 300ul tip onto the pipette.
        - Use the arrow keys to jog the robot over slot 5 in an open space that
          is not an engraving or a hole.
        - Use the 'q' and 'a' keys to jog the pipette up and down respectively
          until the tip is just touching the deck surface, then press 'z'. This
          will save the 'Z' height.
        - Press '1' to automatically go to the expected location of the first
          calibration point. Jog the robot until the tip is actually at
          the point, then press 'enter'.
        - Repeat with '2' and '3'.
        - After calibrating all three points, press the space bar to save the
          configuration.
        - Optionally, press 4,5,6 or 7 to validate the new configuration.
        - Press 'p' to perform tip probe. Press the space bar to save again.
        - Press 'm' to perform mount calibration.
          Press enter and then space bar to save again.
        - Press 'esc' to exit the program.
    """
    prompt = input(
        ">>> Warning! Running this tool backup and clear any previous "
        "calibration data. Proceed (y/[n])? ")
    if prompt not in ['y', 'Y', 'yes']:
        print('Exiting--prior configuration data not changed')
        sys.exit()
    # Notes:
    #  - 200ul tip is 51.7mm long when attached to a pipette
    #  - For xyz coordinates, (0, 0, 0) is the lower-left corner of the robot
    parser = argparse.ArgumentParser(prog='opentrons deck calibration',
                                     description=__doc__)
    parser.add_argument('-t',
                        '--pickupTip',
                        help='What to output during simulations',
                        default=None,
                        action='store_true')
    parser.add_argument('-l',
                        '--log-level',
                        action='store',
                        help=('Log level for deck calibration.'),
                        choices=['error', 'warning', 'info', 'debug'],
                        default='info')
    args = parser.parse_args()

    sync_hardware = ThreadManager(API.build_hardware_controller).sync
    sync_hardware.set_lights(rails=True)
    # Register hook to reboot the robot after exiting this tool (regardless of
    # whether this process exits normally or not)
    atexit.register(notify_and_restart)
    backup_configuration_and_reload(sync_hardware)
    cli = CLITool(point_set=get_calibration_points(),
                  hardware=sync_hardware,
                  pickup_tip=args.pickupTip,
                  loop=loop,
                  log_level=args.log_level)

    cli.home()
    # lights help the script user to see the points on the deck
    cli.ui_loop.run()

    try:
        print('Robot config: \n', cli.hardware.config)
    except Exception:
        pass
async def test_session_no_pipettes_error():
    simulator = ThreadManager(API.build_hardware_simulator)

    with pytest.raises(NoPipetteException):
        await session.CheckCalibrationSession.build(simulator)
Example #18
0
def get_protocol_api(
    version: Union[str, APIVersion],
    bundled_labware: Dict[str, 'LabwareDefinition'] = None,
    bundled_data: Dict[str, bytes] = None,
    extra_labware: Dict[str, 'LabwareDefinition'] = None,
) -> protocol_api.ProtocolContext:
    """
    Build and return a :py:class:`ProtocolContext` connected to the robot.

    This can be used to run protocols from interactive Python sessions
    such as Jupyter or an interpreter on the command line:

    .. code-block:: python

        >>> from opentrons.execute import get_protocol_api
        >>> protocol = get_protocol_api('2.0')
        >>> instr = protocol.load_instrument('p300_single', 'right')
        >>> instr.home()

    If ``extra_labware`` is not specified, any labware definitions saved in
    the ``labware`` directory of the Jupyter notebook directory will be
    available.

    When this function is called, modules and instruments will be recached.

    :param version: The API version to use. This must be lower than
                    :py:attr:`opentrons.protocol_api.MAX_SUPPORTED_VERSION`.
                    It may be specified either as a string (``'2.0'``) or
                    as a :py:class:`.protocols.types.APIVersion`
                    (``APIVersion(2, 0)``).
    :param bundled_labware: If specified, a mapping from labware names to
                            labware definitions for labware to consider in the
                            protocol. Note that if you specify this, _only_
                            labware in this argument will be allowed in the
                            protocol. This is preparation for a beta feature
                            and is best not used.
    :param bundled_data: If specified, a mapping from filenames to contents
                         for data to be available in the protocol from
                         :py:attr:`.ProtocolContext.bundled_data`.
    :param extra_labware: If specified, a mapping from labware names to
                          labware definitions for labware to consider in the
                          protocol in addition to those stored on the robot.
                          If this is an empty dict, and this function is called
                          on a robot, it will look in the 'labware'
                          subdirectory of the Jupyter data directory for
                          custom labware.
    :returns opentrons.protocol_api.ProtocolContext: The protocol context.
    """
    global _THREAD_MANAGED_HW
    if not _THREAD_MANAGED_HW:
        # Build a hardware controller in a worker thread, which is necessary
        # because ipython runs its notebook in asyncio but the notebook
        # is at script/repl scope not function scope and is synchronous so
        # you can't control the loop from inside. If we update to
        # IPython 7 we can avoid this, but for now we can't
        _THREAD_MANAGED_HW = ThreadManager(API.build_hardware_controller)
    if isinstance(version, str):
        checked_version = version_from_string(version)
    elif not isinstance(version, APIVersion):
        raise TypeError('version must be either a string or an APIVersion')
    else:
        checked_version = version

    if extra_labware is None\
       and IS_ROBOT\
       and JUPYTER_NOTEBOOK_LABWARE_DIR.is_dir():  # type: ignore
        extra_labware = labware_from_paths([str(JUPYTER_NOTEBOOK_LABWARE_DIR)])

    context = protocol_api.ProtocolContext(hardware=_THREAD_MANAGED_HW,
                                           bundled_labware=bundled_labware,
                                           bundled_data=bundled_data,
                                           extra_labware=extra_labware,
                                           api_version=checked_version)
    context._hw_manager.hardware.cache_instruments()
    return context
Example #19
0
def build_driver(
        port: str = None)\
        -> Tuple[adapters.SynchronousAdapter, SmoothieDriver_3_0_0]:
    hardware = ThreadManager(API.build_hardware_controller).sync
    driver = hardware._backend._smoothie_driver
    return hardware, driver
Example #20
0
def connect_to_port(hardware):

    if options.port:
        hardware.connect(options.port)
    else:
        hardware.connect()


driver = opentrons.drivers.smoothie_drivers.SimulatingDriver()

try:
    api = opentrons.hardware_control.API
    adapter = opentrons.hardware_control.adapters
    parser = optparse.OptionParser(usage='usage: %prog [options] ')
    parser.add_option(
        "-p", "--p", dest="port", default='',
        type='str', help='serial port of the smoothie'
    )

    options, _ = parser.parse_args(args=sys.argv, values=None)
    if options.port:
        port = options.port
    else:
        port = None
    hardware = ThreadManager(API.build_hardware_controller).sync
    driver = hardware._backend._smoothie_driver
except AttributeError:
    hardware = None  # type: ignore
    driver = None  # type: ignore
Example #21
0
 def reset_hw(self):
     self._current = ThreadManager(API.build_hardware_simulator).sync
     return self._current
Example #22
0
async def toggle_lights(hardware: ThreadManager, *args):
    light_state = hardware.get_lights()
    await hardware.set_lights(rails=not light_state.get('rails', False))
Example #23
0
 async def build(cls, hardware: ThreadManager):
     lights_on = hardware.get_lights()['rails']
     await hardware.cache_instruments()
     await hardware.set_lights(rails=True)
     await hardware.home()
     return cls(hardware=hardware, lights_on_before=lights_on)
Example #24
0
async def get_robot_light_state(
        hardware: ThreadManager = Depends(get_hardware)) \
        -> control.RobotLightState:
    light_state = hardware.get_lights()  # type: ignore
    return control.RobotLightState(on=light_state.get('rails', False))
Example #25
0
 def __init__(self, hardware: ThreadManager):
     self._pipettes = self._key_by_uuid(hardware.get_attached_instruments())
     self._hardware = hardware
Example #26
0
    def _simulate(self):
        self._reset()

        stack: List[Dict[str, Any]] = []
        res: List[Dict[str, Any]] = []
        commands: List[Dict[str, Any]] = []

        self._containers.clear()
        self._instruments.clear()
        self._modules.clear()
        self._interactions.clear()

        def on_command(message):
            payload = message['payload']
            description = payload.get('text', '').format(
                **payload
            )

            if message['$'] == 'before':
                level = len(stack)

                stack.append(message)
                commands.append(payload)

                res.append(
                    {
                        'level': level,
                        'description': description,
                        'id': len(res)})
            else:
                stack.pop()
        unsubscribe = self._broker.subscribe(command_types.COMMAND, on_command)
        old_robot_connect = robot.connect

        try:
            # ensure actual pipettes are cached before driver is disconnected
            self._hardware.cache_instruments()
            if self._use_v2:
                instrs = {}
                for mount, pip in self._hardware.attached_instruments.items():
                    if pip:
                        instrs[mount] = {'model': pip['model'],
                                         'id': pip.get('pipette_id', '')}
                sync_sim = ThreadManager(
                        API.build_hardware_simulator,
                        instrs,
                        [mod.name()
                            for mod in self._hardware.attached_modules],
                        strict_attached_instruments=False
                        ).sync
                sync_sim.home()
                self._simulating_ctx = ProtocolContext.build_using(
                    self._protocol,
                    loop=self._loop,
                    hardware=sync_sim,
                    broker=self._broker,
                    extra_labware=getattr(self._protocol, 'extra_labware', {}))
                run_protocol(self._protocol,
                             context=self._simulating_ctx)
            else:
                robot.broker = self._broker
                # we don't rely on being connected anymore so make sure we are
                robot.connect()
                robot._driver.gpio_chardev = SimulatingGPIOCharDev('sim_chip')
                robot.cache_instrument_models()
                robot.disconnect()

                def robot_connect_error(port=None, options=None):
                    raise RuntimeError(
                        'Protocols executed through the Opentrons App may not '
                        'use robot.connect(). Allowing this call would cause '
                        'the robot to execute commands during simulation, and '
                        'then raise an error on execution.')
                robot.connect = robot_connect_error  # type: ignore
                exec(self._protocol.contents, {})
        finally:
            # physically attached pipettes are re-cached during robot.connect()
            # which is important, because during a simulation, the robot could
            # think that it holds a pipette model that it actually does not
            if not self._use_v2:
                robot.connect = old_robot_connect  # type: ignore
                robot.connect()

            unsubscribe()

            instruments, containers, modules, interactions = _accumulate(
                [_get_labware(command) for command in commands])

            self._containers.extend(_dedupe(containers))
            self._instruments.extend(_dedupe(
                instruments
                + list(self._simulating_ctx.loaded_instruments.values())))
            self._modules.extend(_dedupe(
                modules
                + [m._geometry
                   for m in self._simulating_ctx.loaded_modules.values()]))
            self._interactions.extend(_dedupe(interactions))

            # Labware calibration happens after simulation and before run, so
            # we have to clear the tips if they are left on after simulation
            # to ensure that the instruments are in the expected state at the
            # beginning of the labware calibration flow
            if not self._use_v2:
                robot.clear_tips()

        return res