async def test_cancel_tasks(loop): """ Test that an execution manager cancels all un-protected running asyncio Tasks when cancel is called """ async def fake_task(): while True: await asyncio.sleep(1) exec_mgr = ExecutionManager(loop=loop) cancellable_task = loop.create_task(fake_task()) exec_mgr.register_cancellable_task(cancellable_task) other_task = loop.create_task(fake_task()) # current, cancellable, and other assert len(asyncio.all_tasks(loop)) == 3 assert len([t for t in asyncio.all_tasks(loop) if t.cancelled()]) == 0 await exec_mgr.cancel() await asyncio.sleep(0.1) all_tasks = asyncio.all_tasks(loop) assert len(all_tasks) == 2 # current and other assert other_task in all_tasks assert cancellable_task not in all_tasks
async def test_state_machine(loop): """ Test that an execution manager's state is RUNNING on init and PAUSE when it when pause is called, unless CANCELLED """ exec_mgr = ExecutionManager(loop=loop) assert await exec_mgr.get_state() == ExecutionState.RUNNING # passes through on wait_for_is_running if state is RUNNING await asyncio.wait_for(exec_mgr.wait_for_is_running(), timeout=0.2) await exec_mgr.pause() assert await exec_mgr.get_state() == ExecutionState.PAUSED with pytest.raises(asyncio.TimeoutError): # should stall on wait_for_is_running when state is PAUSED await asyncio.wait_for(exec_mgr.wait_for_is_running(), timeout=0.2) await exec_mgr.resume() assert await exec_mgr.get_state() == ExecutionState.RUNNING await exec_mgr.cancel() assert await exec_mgr.get_state() == ExecutionState.CANCELLED with pytest.raises(ExecutionCancelledError): # attempting to pause when CANCELLED should raise await exec_mgr.pause() with pytest.raises(ExecutionCancelledError): # should raise on wait_for_is_running when state is CANCELLED await asyncio.wait_for(exec_mgr.wait_for_is_running(), timeout=0.2) await exec_mgr.reset() assert await exec_mgr.get_state() == ExecutionState.RUNNING
async def test_get_modules(async_server, loop, async_client, monkeypatch): hw = async_server['com.opentrons.hardware'] magdeck = await hw._backend.build_module( port='/dev/ot_module_magdeck1', model='magdeck', interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=loop), loop=loop) monkeypatch.setattr(API, 'attached_modules', [magdeck]) keys = sorted([ 'name', 'port', 'serial', 'model', 'fwVersion', 'status', 'data', 'hasAvailableUpdate', 'revision', 'moduleModel', 'displayName' ]) resp = await async_client.get('/modules') body = await resp.json() assert resp.status == 200 assert 'modules' in body assert len(body['modules']) == 1 assert sorted(body['modules'][0].keys()) == keys assert 'engaged' in body['modules'][0]['data'] tempdeck = await hw._backend.build_module( port='/dev/ot_module_tempdeck1', model='tempdeck', interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=loop), loop=loop) monkeypatch.setattr(API, 'attached_modules', [tempdeck]) for model in ('temp_deck_v1', 'temp_deck_v1.1', 'temp_deck_v2'): tempdeck._device_info['model'] = model resp = await async_client.get('/modules') body = await resp.json() assert resp.status == 200 assert len(body['modules']) == 1 assert not body['modules'][0]['hasAvailableUpdate']
async def test_sim_initialization(loop): mag = await modules.build(port='/dev/ot_module_sim_magdeck0', which='magdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) assert isinstance(mag, modules.AbstractModule)
def magdeck(): m = asyncio.get_event_loop().run_until_complete( utils.build( port='/dev/ot_module_magdeck1', which='magdeck', simulating=True, interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=asyncio.get_event_loop()), loop=asyncio.get_event_loop())) yield m
def magdeck(): m = asyncio.get_event_loop().run_until_complete( utils.build( port='/dev/ot_module_magdeck1', which='magdeck', simulating=True, interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=asyncio.get_event_loop()), loop=asyncio.get_event_loop())) MagDeck.current_height = PropertyMock(return_value=321) yield m
async def test_set_temperature(monkeypatch, loop): hw_tc = await modules.build(port='/dev/ot_module_sim_thermocycler0', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) def async_return(result): f = asyncio.Future() f.set_result(result) return f set_temp_driver_mock = mock.Mock(return_value=async_return('')) monkeypatch.setattr( hw_tc._driver, 'set_temperature', set_temp_driver_mock) # Test volume param await hw_tc.set_temperature(30, hold_time_seconds=20, hold_time_minutes=1, volume=35) set_temp_driver_mock.assert_called_once_with(temp=30, hold_time=80, volume=35, ramp_rate=None) set_temp_driver_mock.reset_mock() # Test just seconds hold await hw_tc.set_temperature(20, hold_time_seconds=30) set_temp_driver_mock.assert_called_once_with(temp=20, hold_time=30, volume=None, ramp_rate=None) set_temp_driver_mock.reset_mock() # Test just minutes hold await hw_tc.set_temperature(40, hold_time_minutes=5.5) set_temp_driver_mock.assert_called_once_with(temp=40, hold_time=330, volume=None, ramp_rate=None) set_temp_driver_mock.reset_mock() # Test hold_time < HOLD_TIME_FUZZY_SECONDS start = time.time() await hw_tc.set_temperature(40, hold_time_seconds=2) time_taken = time.time() - start assert 1.9 < time_taken < 2.1 set_temp_driver_mock.assert_called_once_with(temp=40, hold_time=2, volume=None, ramp_rate=None) set_temp_driver_mock.reset_mock()
async def test_sim_state_update(loop): mag = await modules.build(port='/dev/ot_module_sim_magdeck0', which='magdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) await mag.calibrate() assert mag.status == 'disengaged' await mag.engage(2) assert mag.status == 'engaged' await mag.deactivate() assert mag.status == 'disengaged'
async def test_revision_model_parsing(loop): mag = await modules.build('', 'magdeck', True, lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) mag._device_info['model'] = 'mag_deck_v1.1' assert mag.model() == 'magneticModuleV1' mag._device_info['model'] = 'mag_deck_v20' assert mag.model() == 'magneticModuleV2' del mag._device_info['model'] assert mag.model() == 'magneticModuleV1'
def tempdeck(): t = asyncio.get_event_loop().run_until_complete( utils.build( port='/dev/ot_module_tempdeck1', which='tempdeck', simulating=True, interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=asyncio.get_event_loop()), loop=asyncio.get_event_loop())) yield t # Have to stop the poller t._poller.join()
async def test_sim_data(loop): mag = await modules.build(port='/dev/ot_module_sim_magdeck0', which='magdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) assert mag.status == 'disengaged' assert mag.device_info['serial'] == 'dummySerialMD' # return v1 when sim_model is not passed assert mag.device_info['model'] == 'mag_deck_v1.1' assert mag.device_info['version'] == 'dummyVersionMD' assert mag.live_data['status'] == mag.status assert 'data' in mag.live_data
async def test_sim_update(loop): temp = await modules.build(port='/dev/ot_module_sim_tempdeck0', which='tempdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) await asyncio.wait_for(temp.set_temperature(10), 0.2) assert temp.temperature == 10 assert temp.target == 10 assert temp.status == 'holding at target' await temp.deactivate() assert temp.temperature == 0 assert temp.target is None assert temp.status == 'idle'
async def test_poller(monkeypatch, loop): temp = modules.tempdeck.TempDeck( port='/dev/ot_module_sim_tempdeck0', execution_manager=ExecutionManager(loop=loop), simulating=True, loop=loop) hit = False def update_called(): nonlocal hit hit = True monkeypatch.setattr(temp._driver, 'update_temperature', update_called) await temp._connect() assert temp._poller.is_alive() await asyncio.sleep(tempdeck.TEMP_POLL_INTERVAL_SECS * 1.1) assert hit
def tempdeck(): t = asyncio.get_event_loop().run_until_complete( utils.build( port='/dev/ot_module_tempdeck1', which='tempdeck', simulating=True, interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=asyncio.get_event_loop()), loop=asyncio.get_event_loop())) TempDeck.temperature = PropertyMock(return_value=123.0) TempDeck.target = PropertyMock(return_value=321.0) yield t # Have to stop the poller t._poller.stop() t._poller.join()
async def test_execute_module_command(virtual_smoothie_env, loop, async_server, async_client, monkeypatch): hw = async_server['com.opentrons.hardware'] magdeck = await hw._backend.build_module( port='/dev/ot_module_magdeck1', model='magdeck', interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=loop), loop=loop) monkeypatch.setattr(API, 'attached_modules', [magdeck]) resp = await async_client.post('/modules/dummySerialMD', json={'command_type': 'deactivate'}) body = await resp.json() assert resp.status == 200 assert 'message' in body assert body['message'] == 'Success'
async def test_sim_state(loop): temp = await modules.build(port='/dev/ot_module_sim_tempdeck0', which='tempdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) assert temp.temperature == 0 assert temp.target is None assert temp.status == 'idle' assert temp.live_data['status'] == temp.status assert temp.live_data['data']['currentTemp'] == temp.temperature assert temp.live_data['data']['targetTemp'] == temp.target status = temp.device_info assert status['serial'] == 'dummySerialTD' # return v1 if sim_model is not passed assert status['model'] == 'temp_deck_v1.1' assert status['version'] == 'dummyVersionTD'
async def test_sim_state(loop): therm = await modules.build(port='/dev/ot_module_sim_thermocycler0', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) assert therm.temperature is None assert therm.target is None assert therm.status == 'idle' assert therm.live_data['status'] == therm.status assert therm.live_data['data']['currentTemp'] == therm.temperature assert therm.live_data['data']['targetTemp'] == therm.target status = therm.device_info assert status['serial'] == 'dummySerialTC' assert status['model'] == 'dummyModelTC' assert status['version'] == 'dummyVersionTC'
async def test_sim_update(loop): therm = await modules.build(port='/dev/ot_module_sim_thermocycler0', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) await therm.set_temperature(temperature=10, hold_time_seconds=None, hold_time_minutes=4.0, volume=50) assert therm.temperature == 10 assert therm.target == 10 assert therm.status == 'holding at target' await asyncio.wait_for(therm.wait_for_temp(), timeout=0.2) await therm.deactivate_block() assert therm.temperature is None assert therm.target is None assert therm.status == 'idle' await therm.set_lid_temperature(temperature=80) assert therm.lid_temp == 80 assert therm.lid_target == 80 await asyncio.wait_for(therm.wait_for_lid_temp(), timeout=0.2) await therm.deactivate_lid() assert therm.lid_temp is None assert therm.lid_target is None await therm.set_temperature(temperature=10, volume=60, hold_time_seconds=2) await therm.set_lid_temperature(temperature=70) await asyncio.wait_for(therm.wait_for_temp(), timeout=0.2) await asyncio.wait_for(therm.wait_for_lid_temp(), timeout=0.2) assert therm.temperature == 10 assert therm.target == 10 assert therm.lid_temp == 70 assert therm.lid_target == 70 await therm.deactivate() assert therm.temperature is None assert therm.target is None assert therm.status == 'idle' assert therm.lid_temp is None assert therm.lid_target is None
def thermocycler(): t = asyncio.get_event_loop().run_until_complete( utils.build( port='/dev/ot_module_thermocycler1', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, execution_manager=ExecutionManager(loop=asyncio.get_event_loop()), loop=asyncio.get_event_loop())) Thermocycler.lid_status = PropertyMock(return_value="open") Thermocycler.lid_target = PropertyMock(return_value=1.2) Thermocycler.lid_temp = PropertyMock(return_value=22.0) Thermocycler.temperature = PropertyMock(return_value=100.0) Thermocycler.target = PropertyMock(return_value=200.0) Thermocycler.hold_time = PropertyMock(return_value=1) Thermocycler.ramp_rate = PropertyMock(return_value=3) Thermocycler.current_cycle_index = PropertyMock(return_value=1) Thermocycler.total_cycle_count = PropertyMock(return_value=3) Thermocycler.current_step_index = PropertyMock(return_value=5) Thermocycler.total_step_count = PropertyMock(return_value=2) return t
async def test_lid(loop): therm = await modules.build(port='/dev/ot_module_sim_thermocycler0', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) assert therm.lid_status == 'open' await therm.open() assert therm.lid_status == 'open' await therm.close() assert therm.lid_status == 'closed' await therm.close() assert therm.lid_status == 'closed' await therm.open() assert therm.lid_status == 'open'
async def test_module_update_integration(monkeypatch, loop): from opentrons.hardware_control import modules def async_return(result): f = asyncio.Future() f.set_result(result) return f bootloader_kwargs = { 'stdout': asyncio.subprocess.PIPE, 'stderr': asyncio.subprocess.PIPE, 'loop': loop, } # test temperature module update with avrdude bootloader tempdeck = await modules.build( port='/dev/ot_module_sim_tempdeck0', which='tempdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop), sim_model='temperatureModuleV2') upload_via_avrdude_mock = mock.Mock( return_value=(async_return((True, 'avrdude bootloader worked')))) monkeypatch.setattr(modules.update, 'upload_via_avrdude', upload_via_avrdude_mock) async def mock_find_avrdude_bootloader_port(): return 'ot_module_avrdude_bootloader1' monkeypatch.setattr(modules.update, 'find_bootloader_port', mock_find_avrdude_bootloader_port) await modules.update_firmware(tempdeck, 'fake_fw_file_path', loop) upload_via_avrdude_mock.assert_called_once_with( 'ot_module_avrdude_bootloader1', 'fake_fw_file_path', bootloader_kwargs) upload_via_avrdude_mock.reset_mock() # test magnetic module update with avrdude bootloader magdeck = await modules.build( port='/dev/ot_module_sim_magdeck0', which='magdeck', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) await modules.update_firmware(magdeck, 'fake_fw_file_path', loop) upload_via_avrdude_mock.assert_called_once_with( 'ot_module_avrdude_bootloader1', 'fake_fw_file_path', bootloader_kwargs) # test thermocycler module update with bossa bootloader thermocycler = await modules.build( port='/dev/ot_module_sim_thermocycler0', which='thermocycler', simulating=True, interrupt_callback=lambda x: None, loop=loop, execution_manager=ExecutionManager(loop=loop)) upload_via_bossa_mock = mock.Mock( return_value=(async_return((True, 'bossa bootloader worked')))) monkeypatch.setattr(modules.update, 'upload_via_bossa', upload_via_bossa_mock) async def mock_find_bossa_bootloader_port(): return 'ot_module_bossa_bootloader1' monkeypatch.setattr(modules.update, 'find_bootloader_port', mock_find_bossa_bootloader_port) await modules.update_firmware(thermocycler, 'fake_fw_file_path', loop) upload_via_bossa_mock.assert_called_once_with( 'ot_module_bossa_bootloader1', 'fake_fw_file_path', bootloader_kwargs)
def load_module( self, module_name: str, location: Optional[types.DeckLocation] = None, configuration: str = None) -> ModuleTypes: """ Load a module onto the deck given its name. This is the function to call to use a module in your protocol, like :py:meth:`load_instrument` is the method to call to use an instrument in your protocol. It returns the created and initialized module context, which will be a different class depending on the kind of module loaded. A map of deck positions to loaded modules can be accessed later using :py:attr:`loaded_modules`. :param str module_name: The name or model of the module. :param location: The location of the module. This is usually the name or number of the slot on the deck where you will be placing the module. Some modules, like the Thermocycler, are only valid in one deck location. You do not have to specify a location when loading a Thermocycler - it will always be in Slot 7. :param configuration: Used to specify the slot configuration of the Thermocycler. Only Valid in Python API Version 2.4 and later. If you wish to use the non-full plate configuration, you must pass in the key word value `semi` :type location: str or int or None :returns ModuleContext: The loaded and initialized :py:class:`ModuleContext`. """ resolved_model = resolve_module_model(module_name) resolved_type = resolve_module_type(resolved_model) resolved_location = self._deck_layout.resolve_module_location( resolved_type, location) if self._api_version < APIVersion(2, 4) and configuration: raise APIVersionError( f'You have specified API {self._api_version}, but you are' 'using thermocycler parameters only available in 2.4') geometry = load_module( resolved_model, self._deck_layout.position_for( resolved_location), self._api_version, configuration) hc_mod_instance = None mod_class = { ModuleType.MAGNETIC: MagneticModuleContext, ModuleType.TEMPERATURE: TemperatureModuleContext, ModuleType.THERMOCYCLER: ThermocyclerContext}[resolved_type] for mod in self._hw_manager.hardware.attached_modules: if models_compatible( module_model_from_string(mod.model()), resolved_model): hc_mod_instance = SynchronousAdapter(mod) break if self.is_simulating() and hc_mod_instance is None: mod_type = { ModuleType.MAGNETIC: modules.magdeck.MagDeck, ModuleType.TEMPERATURE: modules.tempdeck.TempDeck, ModuleType.THERMOCYCLER: modules.thermocycler.Thermocycler }[resolved_type] hc_mod_instance = SynchronousAdapter(mod_type( port='', simulating=True, loop=self._hw_manager.hardware.loop, execution_manager=ExecutionManager( loop=self._hw_manager.hardware.loop), sim_model=resolved_model.value)) hc_mod_instance._connect() if hc_mod_instance: mod_ctx = mod_class(self, hc_mod_instance, geometry, self.api_version, self._loop) else: raise RuntimeError( f'Could not find specified module: {module_name}') self._modules.add(mod_ctx) self._deck_layout[resolved_location] = geometry return mod_ctx