Example #1
0
    def __init__(self, name, protocol, hardware, loop, broker, motion_lock):
        self._broker = broker
        self._default_logger = self._broker.logger
        self._sim_logger = self._broker.logger.getChild('sim')
        self._run_logger = self._broker.logger.getChild('run')
        self._loop = loop
        self.name = name
        self._protocol = protocol
        self.api_level = getattr(self._protocol, 'api_level', APIVersion(2, 0))
        self._use_v2 = self.api_level >= APIVersion(2, 0)
        log.info(f'Protocol API Version: {self.api_level}; '
                 f'Protocol kind: {type(self._protocol)}')

        # self.metadata is exposed via rpc
        self.metadata = getattr(self._protocol, 'metadata', {})

        self._hardware = hardware
        self._simulating_ctx = ProtocolContext.build_using(self._protocol,
                                                           loop=self._loop,
                                                           broker=self._broker)

        self.state: 'State' = None
        #: The current state
        self.stateInfo: 'StateInfo' = {}
        #: A message associated with the current state
        self.commands = []
        self.command_log = {}
        self.errors = []

        self._containers = []
        self._instruments = []
        self._modules = []
        self._interactions = []

        self.instruments = None
        self.containers = None
        self.modules = None
        self.protocol_text = protocol.text

        self.startTime: Optional[float] = None
        self._motion_lock = motion_lock
        self._event_watcher = None
        self.door_state: Optional[str] = None
        self.blocked: Optional[bool] = None

        self._run_lock: Lock = Lock()
        self._is_running: Event = Event()
Example #2
0
    def __init__(self, name, protocol, hardware, loop, broker, motion_lock):
        self._broker = broker
        self._default_logger = self._broker.logger
        self._sim_logger = self._broker.logger.getChild('sim')
        self._run_logger = self._broker.logger.getChild('run')
        self._loop = loop
        self.name = name
        self._protocol = protocol
        self.api_level = getattr(self._protocol, 'api_level', APIVersion(2, 0))
        self._use_v2 = self.api_level >= APIVersion(2, 0)
        log.info(f'Protocol API Version: {self.api_level}; '
                 f'Protocol kind: {type(self._protocol)}')

        # self.metadata is exposed via rpc
        self.metadata = getattr(self._protocol, 'metadata', {})

        self._hardware = hardware
        self._simulating_ctx = ProtocolContext.build_using(self._protocol,
                                                           loop=self._loop,
                                                           broker=self._broker)

        self.state = None
        self.commands = []
        self.command_log = {}
        self.errors = []

        self._containers = []
        self._instruments = []
        self._modules = []
        self._interactions = []

        self.instruments = None
        self.containers = None
        self.modules = None
        self.protocol_text = protocol.text

        self.startTime = None
        self._motion_lock = motion_lock
Example #3
0
    def _run(self):
        def on_command(message):
            if message['$'] == 'before':
                self.log_append()
            if message['name'] == command_types.PAUSE:
                self.set_state('paused')
            if message['name'] == command_types.RESUME:
                self.set_state('running')

        self._reset()

        _unsubscribe = self._broker.subscribe(command_types.COMMAND,
                                              on_command)

        self.startTime = now()
        self.set_state('running')

        try:
            if self._use_v2:
                self.resume()
                self._pre_run_hooks()
                self._hardware.cache_instruments()
                ctx = ProtocolContext.build_using(self._protocol,
                                                  loop=self._loop,
                                                  broker=self._broker,
                                                  extra_labware=getattr(
                                                      self._protocol,
                                                      'extra_labware', {}))
                ctx.connect(self._hardware)
                ctx.home()
                run_protocol(self._protocol, context=ctx)
            else:
                robot.broker = self._broker
                assert isinstance(self._protocol, PythonProtocol),\
                    'Internal error: v1 should only be used for python'
                if not robot.is_connected():
                    robot.connect()
                self.resume()
                self._pre_run_hooks()
                robot.cache_instrument_models()
                robot.discover_modules()
                exec(self._protocol.contents, {})

            # If the last command in a protocol was a pause, the protocol
            # will immediately finish executing because there's no smoothie
            # command to block... except the home that's about to happen,
            # which will confuse the app and lock it up. So we need to
            # do our own pause here, and sleep the thread until/unless the
            # app resumes us.
            #
            # Cancelling from the app during this pause will result in the
            # smoothie giving us an error during the subsequent home, which
            # is tragic but expected.
            while self.state == 'paused':
                sleep(0.1)
            self.set_state('finished')
            self._hw_iface().home()
        except SmoothieAlarm:
            log.info("Protocol cancelled")
        except Exception as e:
            log.exception("Exception during run:")
            self.error_append(e)
            self.set_state('error')
            raise e
        finally:
            _unsubscribe()
Example #4
0
    def _simulate(self):
        self._reset()

        stack = []
        res = []
        commands = []

        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', '')
                        }
                sim = adapters.SynchronousAdapter.build(
                    API.build_hardware_simulator,
                    instrs,
                    [mod.name() for mod in self._hardware.attached_modules],
                    strict_attached_instruments=False)
                sim.home()
                self._simulating_ctx = ProtocolContext.build_using(
                    self._protocol,
                    loop=self._loop,
                    hardware=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.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
                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
                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