opentrons 8.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/__init__.py +150 -0
- opentrons/_version.py +34 -0
- opentrons/calibration_storage/__init__.py +54 -0
- opentrons/calibration_storage/deck_configuration.py +62 -0
- opentrons/calibration_storage/encoder_decoder.py +31 -0
- opentrons/calibration_storage/file_operators.py +142 -0
- opentrons/calibration_storage/helpers.py +103 -0
- opentrons/calibration_storage/ot2/__init__.py +34 -0
- opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
- opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
- opentrons/calibration_storage/ot2/models/__init__.py +0 -0
- opentrons/calibration_storage/ot2/models/v1.py +149 -0
- opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
- opentrons/calibration_storage/ot2/tip_length.py +281 -0
- opentrons/calibration_storage/ot3/__init__.py +31 -0
- opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
- opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
- opentrons/calibration_storage/ot3/models/__init__.py +0 -0
- opentrons/calibration_storage/ot3/models/v1.py +122 -0
- opentrons/calibration_storage/ot3/module_offset.py +138 -0
- opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
- opentrons/calibration_storage/types.py +45 -0
- opentrons/cli/__init__.py +21 -0
- opentrons/cli/__main__.py +5 -0
- opentrons/cli/analyze.py +557 -0
- opentrons/config/__init__.py +631 -0
- opentrons/config/advanced_settings.py +871 -0
- opentrons/config/defaults_ot2.py +214 -0
- opentrons/config/defaults_ot3.py +499 -0
- opentrons/config/feature_flags.py +86 -0
- opentrons/config/gripper_config.py +55 -0
- opentrons/config/reset.py +203 -0
- opentrons/config/robot_configs.py +187 -0
- opentrons/config/types.py +183 -0
- opentrons/drivers/__init__.py +0 -0
- opentrons/drivers/absorbance_reader/__init__.py +11 -0
- opentrons/drivers/absorbance_reader/abstract.py +72 -0
- opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
- opentrons/drivers/absorbance_reader/driver.py +81 -0
- opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
- opentrons/drivers/absorbance_reader/simulator.py +84 -0
- opentrons/drivers/asyncio/__init__.py +0 -0
- opentrons/drivers/asyncio/communication/__init__.py +22 -0
- opentrons/drivers/asyncio/communication/async_serial.py +187 -0
- opentrons/drivers/asyncio/communication/errors.py +88 -0
- opentrons/drivers/asyncio/communication/serial_connection.py +557 -0
- opentrons/drivers/command_builder.py +102 -0
- opentrons/drivers/flex_stacker/__init__.py +13 -0
- opentrons/drivers/flex_stacker/abstract.py +214 -0
- opentrons/drivers/flex_stacker/driver.py +768 -0
- opentrons/drivers/flex_stacker/errors.py +68 -0
- opentrons/drivers/flex_stacker/simulator.py +309 -0
- opentrons/drivers/flex_stacker/types.py +367 -0
- opentrons/drivers/flex_stacker/utils.py +19 -0
- opentrons/drivers/heater_shaker/__init__.py +5 -0
- opentrons/drivers/heater_shaker/abstract.py +76 -0
- opentrons/drivers/heater_shaker/driver.py +204 -0
- opentrons/drivers/heater_shaker/simulator.py +94 -0
- opentrons/drivers/mag_deck/__init__.py +6 -0
- opentrons/drivers/mag_deck/abstract.py +44 -0
- opentrons/drivers/mag_deck/driver.py +208 -0
- opentrons/drivers/mag_deck/simulator.py +63 -0
- opentrons/drivers/rpi_drivers/__init__.py +33 -0
- opentrons/drivers/rpi_drivers/dev_types.py +94 -0
- opentrons/drivers/rpi_drivers/gpio.py +282 -0
- opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
- opentrons/drivers/rpi_drivers/interfaces.py +15 -0
- opentrons/drivers/rpi_drivers/types.py +364 -0
- opentrons/drivers/rpi_drivers/usb.py +102 -0
- opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
- opentrons/drivers/serial_communication.py +151 -0
- opentrons/drivers/smoothie_drivers/__init__.py +4 -0
- opentrons/drivers/smoothie_drivers/connection.py +51 -0
- opentrons/drivers/smoothie_drivers/constants.py +121 -0
- opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
- opentrons/drivers/smoothie_drivers/errors.py +49 -0
- opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
- opentrons/drivers/smoothie_drivers/simulator.py +99 -0
- opentrons/drivers/smoothie_drivers/types.py +16 -0
- opentrons/drivers/temp_deck/__init__.py +10 -0
- opentrons/drivers/temp_deck/abstract.py +54 -0
- opentrons/drivers/temp_deck/driver.py +197 -0
- opentrons/drivers/temp_deck/simulator.py +57 -0
- opentrons/drivers/thermocycler/__init__.py +12 -0
- opentrons/drivers/thermocycler/abstract.py +99 -0
- opentrons/drivers/thermocycler/driver.py +395 -0
- opentrons/drivers/thermocycler/simulator.py +126 -0
- opentrons/drivers/types.py +107 -0
- opentrons/drivers/utils.py +222 -0
- opentrons/execute.py +742 -0
- opentrons/hardware_control/__init__.py +65 -0
- opentrons/hardware_control/__main__.py +77 -0
- opentrons/hardware_control/adapters.py +98 -0
- opentrons/hardware_control/api.py +1347 -0
- opentrons/hardware_control/backends/__init__.py +7 -0
- opentrons/hardware_control/backends/controller.py +400 -0
- opentrons/hardware_control/backends/errors.py +9 -0
- opentrons/hardware_control/backends/estop_state.py +164 -0
- opentrons/hardware_control/backends/flex_protocol.py +497 -0
- opentrons/hardware_control/backends/ot3controller.py +1930 -0
- opentrons/hardware_control/backends/ot3simulator.py +900 -0
- opentrons/hardware_control/backends/ot3utils.py +664 -0
- opentrons/hardware_control/backends/simulator.py +442 -0
- opentrons/hardware_control/backends/status_bar_state.py +240 -0
- opentrons/hardware_control/backends/subsystem_manager.py +431 -0
- opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
- opentrons/hardware_control/backends/types.py +14 -0
- opentrons/hardware_control/constants.py +6 -0
- opentrons/hardware_control/dev_types.py +125 -0
- opentrons/hardware_control/emulation/__init__.py +0 -0
- opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
- opentrons/hardware_control/emulation/app.py +56 -0
- opentrons/hardware_control/emulation/connection_handler.py +38 -0
- opentrons/hardware_control/emulation/heater_shaker.py +150 -0
- opentrons/hardware_control/emulation/magdeck.py +60 -0
- opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
- opentrons/hardware_control/emulation/module_server/client.py +78 -0
- opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
- opentrons/hardware_control/emulation/module_server/models.py +31 -0
- opentrons/hardware_control/emulation/module_server/server.py +110 -0
- opentrons/hardware_control/emulation/parser.py +74 -0
- opentrons/hardware_control/emulation/proxy.py +241 -0
- opentrons/hardware_control/emulation/run_emulator.py +68 -0
- opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
- opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
- opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
- opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
- opentrons/hardware_control/emulation/settings.py +119 -0
- opentrons/hardware_control/emulation/simulations.py +133 -0
- opentrons/hardware_control/emulation/smoothie.py +192 -0
- opentrons/hardware_control/emulation/tempdeck.py +69 -0
- opentrons/hardware_control/emulation/thermocycler.py +128 -0
- opentrons/hardware_control/emulation/types.py +10 -0
- opentrons/hardware_control/emulation/util.py +38 -0
- opentrons/hardware_control/errors.py +43 -0
- opentrons/hardware_control/execution_manager.py +164 -0
- opentrons/hardware_control/instruments/__init__.py +5 -0
- opentrons/hardware_control/instruments/instrument_abc.py +39 -0
- opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
- opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
- opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
- opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
- opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
- opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
- opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
- opentrons/hardware_control/module_control.py +332 -0
- opentrons/hardware_control/modules/__init__.py +69 -0
- opentrons/hardware_control/modules/absorbance_reader.py +373 -0
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/flex_stacker.py +948 -0
- opentrons/hardware_control/modules/heater_shaker.py +426 -0
- opentrons/hardware_control/modules/lid_temp_status.py +35 -0
- opentrons/hardware_control/modules/magdeck.py +233 -0
- opentrons/hardware_control/modules/mod_abc.py +245 -0
- opentrons/hardware_control/modules/module_calibration.py +93 -0
- opentrons/hardware_control/modules/plate_temp_status.py +61 -0
- opentrons/hardware_control/modules/tempdeck.py +299 -0
- opentrons/hardware_control/modules/thermocycler.py +731 -0
- opentrons/hardware_control/modules/types.py +417 -0
- opentrons/hardware_control/modules/update.py +255 -0
- opentrons/hardware_control/modules/utils.py +73 -0
- opentrons/hardware_control/motion_utilities.py +318 -0
- opentrons/hardware_control/nozzle_manager.py +422 -0
- opentrons/hardware_control/ot3_calibration.py +1171 -0
- opentrons/hardware_control/ot3api.py +3227 -0
- opentrons/hardware_control/pause_manager.py +31 -0
- opentrons/hardware_control/poller.py +112 -0
- opentrons/hardware_control/protocols/__init__.py +106 -0
- opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
- opentrons/hardware_control/protocols/calibratable.py +45 -0
- opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
- opentrons/hardware_control/protocols/configurable.py +48 -0
- opentrons/hardware_control/protocols/event_sourcer.py +18 -0
- opentrons/hardware_control/protocols/execution_controllable.py +33 -0
- opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
- opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
- opentrons/hardware_control/protocols/gripper_controller.py +55 -0
- opentrons/hardware_control/protocols/hardware_manager.py +51 -0
- opentrons/hardware_control/protocols/identifiable.py +16 -0
- opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
- opentrons/hardware_control/protocols/liquid_handler.py +266 -0
- opentrons/hardware_control/protocols/module_provider.py +16 -0
- opentrons/hardware_control/protocols/motion_controller.py +243 -0
- opentrons/hardware_control/protocols/position_estimator.py +45 -0
- opentrons/hardware_control/protocols/simulatable.py +10 -0
- opentrons/hardware_control/protocols/stoppable.py +9 -0
- opentrons/hardware_control/protocols/types.py +27 -0
- opentrons/hardware_control/robot_calibration.py +224 -0
- opentrons/hardware_control/scripts/README.md +28 -0
- opentrons/hardware_control/scripts/__init__.py +1 -0
- opentrons/hardware_control/scripts/gripper_control.py +208 -0
- opentrons/hardware_control/scripts/ot3gripper +7 -0
- opentrons/hardware_control/scripts/ot3repl +7 -0
- opentrons/hardware_control/scripts/repl.py +187 -0
- opentrons/hardware_control/scripts/tc_control.py +97 -0
- opentrons/hardware_control/scripts/update_module_fw.py +274 -0
- opentrons/hardware_control/simulator_setup.py +260 -0
- opentrons/hardware_control/thread_manager.py +431 -0
- opentrons/hardware_control/threaded_async_lock.py +97 -0
- opentrons/hardware_control/types.py +792 -0
- opentrons/hardware_control/util.py +234 -0
- opentrons/legacy_broker.py +53 -0
- opentrons/legacy_commands/__init__.py +1 -0
- opentrons/legacy_commands/commands.py +483 -0
- opentrons/legacy_commands/helpers.py +153 -0
- opentrons/legacy_commands/module_commands.py +276 -0
- opentrons/legacy_commands/protocol_commands.py +54 -0
- opentrons/legacy_commands/publisher.py +155 -0
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +1186 -0
- opentrons/motion_planning/__init__.py +32 -0
- opentrons/motion_planning/adjacent_slots_getters.py +168 -0
- opentrons/motion_planning/deck_conflict.py +501 -0
- opentrons/motion_planning/errors.py +35 -0
- opentrons/motion_planning/types.py +42 -0
- opentrons/motion_planning/waypoints.py +218 -0
- opentrons/ordered_set.py +138 -0
- opentrons/protocol_api/__init__.py +105 -0
- opentrons/protocol_api/_liquid.py +157 -0
- opentrons/protocol_api/_liquid_properties.py +814 -0
- opentrons/protocol_api/_nozzle_layout.py +31 -0
- opentrons/protocol_api/_parameter_context.py +300 -0
- opentrons/protocol_api/_parameters.py +31 -0
- opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
- opentrons/protocol_api/_types.py +43 -0
- opentrons/protocol_api/config.py +23 -0
- opentrons/protocol_api/core/__init__.py +23 -0
- opentrons/protocol_api/core/common.py +33 -0
- opentrons/protocol_api/core/core_map.py +74 -0
- opentrons/protocol_api/core/engine/__init__.py +22 -0
- opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
- opentrons/protocol_api/core/engine/deck_conflict.py +400 -0
- opentrons/protocol_api/core/engine/exceptions.py +19 -0
- opentrons/protocol_api/core/engine/instrument.py +2391 -0
- opentrons/protocol_api/core/engine/labware.py +238 -0
- opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
- opentrons/protocol_api/core/engine/module_core.py +1027 -0
- opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
- opentrons/protocol_api/core/engine/point_calculations.py +64 -0
- opentrons/protocol_api/core/engine/protocol.py +1153 -0
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/stringify.py +74 -0
- opentrons/protocol_api/core/engine/transfer_components_executor.py +1006 -0
- opentrons/protocol_api/core/engine/well.py +241 -0
- opentrons/protocol_api/core/instrument.py +459 -0
- opentrons/protocol_api/core/labware.py +151 -0
- opentrons/protocol_api/core/legacy/__init__.py +11 -0
- opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
- opentrons/protocol_api/core/legacy/deck.py +369 -0
- opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
- opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
- opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
- opentrons/protocol_api/core/legacy/load_info.py +67 -0
- opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
- opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
- opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
- opentrons/protocol_api/core/module.py +484 -0
- opentrons/protocol_api/core/protocol.py +311 -0
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/core/well.py +116 -0
- opentrons/protocol_api/core/well_grid.py +45 -0
- opentrons/protocol_api/create_protocol_context.py +177 -0
- opentrons/protocol_api/deck.py +223 -0
- opentrons/protocol_api/disposal_locations.py +244 -0
- opentrons/protocol_api/instrument_context.py +3272 -0
- opentrons/protocol_api/labware.py +1579 -0
- opentrons/protocol_api/module_contexts.py +1447 -0
- opentrons/protocol_api/module_validation_and_errors.py +61 -0
- opentrons/protocol_api/protocol_context.py +1688 -0
- opentrons/protocol_api/robot_context.py +303 -0
- opentrons/protocol_api/validation.py +761 -0
- opentrons/protocol_engine/__init__.py +155 -0
- opentrons/protocol_engine/actions/__init__.py +65 -0
- opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
- opentrons/protocol_engine/actions/action_handler.py +13 -0
- opentrons/protocol_engine/actions/actions.py +302 -0
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/__init__.py +5 -0
- opentrons/protocol_engine/clients/sync_client.py +174 -0
- opentrons/protocol_engine/clients/transports.py +197 -0
- opentrons/protocol_engine/commands/__init__.py +757 -0
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
- opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
- opentrons/protocol_engine/commands/aspirate.py +244 -0
- opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
- opentrons/protocol_engine/commands/blow_out.py +146 -0
- opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
- opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
- opentrons/protocol_engine/commands/command.py +308 -0
- opentrons/protocol_engine/commands/command_unions.py +974 -0
- opentrons/protocol_engine/commands/comment.py +57 -0
- opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
- opentrons/protocol_engine/commands/custom.py +67 -0
- opentrons/protocol_engine/commands/dispense.py +194 -0
- opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
- opentrons/protocol_engine/commands/drop_tip.py +232 -0
- opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
- opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
- opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
- opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +339 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
- opentrons/protocol_engine/commands/hash_command_params.py +38 -0
- opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
- opentrons/protocol_engine/commands/home.py +100 -0
- opentrons/protocol_engine/commands/identify_module.py +86 -0
- opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
- opentrons/protocol_engine/commands/liquid_probe.py +464 -0
- opentrons/protocol_engine/commands/load_labware.py +210 -0
- opentrons/protocol_engine/commands/load_lid.py +154 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
- opentrons/protocol_engine/commands/load_liquid.py +95 -0
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +223 -0
- opentrons/protocol_engine/commands/load_pipette.py +167 -0
- opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
- opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
- opentrons/protocol_engine/commands/move_labware.py +546 -0
- opentrons/protocol_engine/commands/move_relative.py +102 -0
- opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
- opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
- opentrons/protocol_engine/commands/move_to_well.py +119 -0
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
- opentrons/protocol_engine/commands/pipetting_common.py +443 -0
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
- opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
- opentrons/protocol_engine/commands/reload_labware.py +90 -0
- opentrons/protocol_engine/commands/retract_axis.py +75 -0
- opentrons/protocol_engine/commands/robot/__init__.py +70 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/save_position.py +109 -0
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
- opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
- opentrons/protocol_engine/commands/set_status_bar.py +89 -0
- opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
- opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
- opentrons/protocol_engine/commands/touch_tip.py +189 -0
- opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
- opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
- opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
- opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
- opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
- opentrons/protocol_engine/create_protocol_engine.py +193 -0
- opentrons/protocol_engine/engine_support.py +28 -0
- opentrons/protocol_engine/error_recovery_policy.py +81 -0
- opentrons/protocol_engine/errors/__init__.py +191 -0
- opentrons/protocol_engine/errors/error_occurrence.py +182 -0
- opentrons/protocol_engine/errors/exceptions.py +1308 -0
- opentrons/protocol_engine/execution/__init__.py +50 -0
- opentrons/protocol_engine/execution/command_executor.py +216 -0
- opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
- opentrons/protocol_engine/execution/door_watcher.py +119 -0
- opentrons/protocol_engine/execution/equipment.py +819 -0
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +686 -0
- opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
- opentrons/protocol_engine/execution/labware_movement.py +297 -0
- opentrons/protocol_engine/execution/movement.py +350 -0
- opentrons/protocol_engine/execution/pipetting.py +607 -0
- opentrons/protocol_engine/execution/queue_worker.py +86 -0
- opentrons/protocol_engine/execution/rail_lights.py +25 -0
- opentrons/protocol_engine/execution/run_control.py +33 -0
- opentrons/protocol_engine/execution/status_bar.py +34 -0
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
- opentrons/protocol_engine/execution/tip_handler.py +550 -0
- opentrons/protocol_engine/labware_offset_standardization.py +194 -0
- opentrons/protocol_engine/notes/__init__.py +17 -0
- opentrons/protocol_engine/notes/notes.py +59 -0
- opentrons/protocol_engine/plugins.py +104 -0
- opentrons/protocol_engine/protocol_engine.py +683 -0
- opentrons/protocol_engine/resources/__init__.py +26 -0
- opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
- opentrons/protocol_engine/resources/file_provider.py +161 -0
- opentrons/protocol_engine/resources/fixture_validation.py +68 -0
- opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
- opentrons/protocol_engine/resources/labware_validation.py +73 -0
- opentrons/protocol_engine/resources/model_utils.py +32 -0
- opentrons/protocol_engine/resources/module_data_provider.py +44 -0
- opentrons/protocol_engine/resources/ot3_validation.py +21 -0
- opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
- opentrons/protocol_engine/slot_standardization.py +128 -0
- opentrons/protocol_engine/state/__init__.py +1 -0
- opentrons/protocol_engine/state/_abstract_store.py +27 -0
- opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
- opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
- opentrons/protocol_engine/state/_move_types.py +83 -0
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +699 -0
- opentrons/protocol_engine/state/command_history.py +309 -0
- opentrons/protocol_engine/state/commands.py +1164 -0
- opentrons/protocol_engine/state/config.py +39 -0
- opentrons/protocol_engine/state/files.py +57 -0
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/geometry.py +2408 -0
- opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
- opentrons/protocol_engine/state/labware.py +1432 -0
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +73 -0
- opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
- opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
- opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
- opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
- opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
- opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
- opentrons/protocol_engine/state/modules.py +1515 -0
- opentrons/protocol_engine/state/motion.py +373 -0
- opentrons/protocol_engine/state/pipettes.py +905 -0
- opentrons/protocol_engine/state/state.py +421 -0
- opentrons/protocol_engine/state/state_summary.py +36 -0
- opentrons/protocol_engine/state/tips.py +420 -0
- opentrons/protocol_engine/state/update_types.py +904 -0
- opentrons/protocol_engine/state/wells.py +290 -0
- opentrons/protocol_engine/types/__init__.py +310 -0
- opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
- opentrons/protocol_engine/types/command_annotations.py +53 -0
- opentrons/protocol_engine/types/deck_configuration.py +81 -0
- opentrons/protocol_engine/types/execution.py +96 -0
- opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
- opentrons/protocol_engine/types/instrument.py +47 -0
- opentrons/protocol_engine/types/instrument_sensors.py +47 -0
- opentrons/protocol_engine/types/labware.py +131 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +111 -0
- opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
- opentrons/protocol_engine/types/liquid.py +40 -0
- opentrons/protocol_engine/types/liquid_class.py +59 -0
- opentrons/protocol_engine/types/liquid_handling.py +13 -0
- opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
- opentrons/protocol_engine/types/location.py +194 -0
- opentrons/protocol_engine/types/module.py +310 -0
- opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
- opentrons/protocol_engine/types/run_time_parameters.py +133 -0
- opentrons/protocol_engine/types/tip.py +18 -0
- opentrons/protocol_engine/types/util.py +21 -0
- opentrons/protocol_engine/types/well_position.py +124 -0
- opentrons/protocol_reader/__init__.py +37 -0
- opentrons/protocol_reader/extract_labware_definitions.py +66 -0
- opentrons/protocol_reader/file_format_validator.py +152 -0
- opentrons/protocol_reader/file_hasher.py +27 -0
- opentrons/protocol_reader/file_identifier.py +284 -0
- opentrons/protocol_reader/file_reader_writer.py +90 -0
- opentrons/protocol_reader/input_file.py +16 -0
- opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
- opentrons/protocol_reader/protocol_reader.py +188 -0
- opentrons/protocol_reader/protocol_source.py +124 -0
- opentrons/protocol_reader/role_analyzer.py +86 -0
- opentrons/protocol_runner/__init__.py +26 -0
- opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
- opentrons/protocol_runner/json_file_reader.py +55 -0
- opentrons/protocol_runner/json_translator.py +314 -0
- opentrons/protocol_runner/legacy_command_mapper.py +852 -0
- opentrons/protocol_runner/legacy_context_plugin.py +116 -0
- opentrons/protocol_runner/protocol_runner.py +530 -0
- opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
- opentrons/protocol_runner/run_orchestrator.py +496 -0
- opentrons/protocol_runner/task_queue.py +95 -0
- opentrons/protocols/__init__.py +6 -0
- opentrons/protocols/advanced_control/__init__.py +0 -0
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +60 -0
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +180 -0
- opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
- opentrons/protocols/api_support/__init__.py +0 -0
- opentrons/protocols/api_support/constants.py +8 -0
- opentrons/protocols/api_support/deck_type.py +110 -0
- opentrons/protocols/api_support/definitions.py +18 -0
- opentrons/protocols/api_support/instrument.py +151 -0
- opentrons/protocols/api_support/labware_like.py +233 -0
- opentrons/protocols/api_support/tip_tracker.py +175 -0
- opentrons/protocols/api_support/types.py +32 -0
- opentrons/protocols/api_support/util.py +403 -0
- opentrons/protocols/bundle.py +89 -0
- opentrons/protocols/duration/__init__.py +4 -0
- opentrons/protocols/duration/errors.py +5 -0
- opentrons/protocols/duration/estimator.py +628 -0
- opentrons/protocols/execution/__init__.py +0 -0
- opentrons/protocols/execution/dev_types.py +181 -0
- opentrons/protocols/execution/errors.py +40 -0
- opentrons/protocols/execution/execute.py +84 -0
- opentrons/protocols/execution/execute_json_v3.py +275 -0
- opentrons/protocols/execution/execute_json_v4.py +359 -0
- opentrons/protocols/execution/execute_json_v5.py +28 -0
- opentrons/protocols/execution/execute_python.py +169 -0
- opentrons/protocols/execution/json_dispatchers.py +87 -0
- opentrons/protocols/execution/types.py +7 -0
- opentrons/protocols/geometry/__init__.py +0 -0
- opentrons/protocols/geometry/planning.py +297 -0
- opentrons/protocols/labware.py +312 -0
- opentrons/protocols/models/__init__.py +0 -0
- opentrons/protocols/models/json_protocol.py +679 -0
- opentrons/protocols/parameters/__init__.py +0 -0
- opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
- opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
- opentrons/protocols/parameters/exceptions.py +34 -0
- opentrons/protocols/parameters/parameter_definition.py +272 -0
- opentrons/protocols/parameters/types.py +17 -0
- opentrons/protocols/parameters/validation.py +267 -0
- opentrons/protocols/parse.py +671 -0
- opentrons/protocols/types.py +159 -0
- opentrons/py.typed +0 -0
- opentrons/resources/scripts/lpc21isp +0 -0
- opentrons/resources/smoothie-edge-8414642.hex +23010 -0
- opentrons/simulate.py +1065 -0
- opentrons/system/__init__.py +6 -0
- opentrons/system/camera.py +51 -0
- opentrons/system/log_control.py +59 -0
- opentrons/system/nmcli.py +856 -0
- opentrons/system/resin.py +24 -0
- opentrons/system/smoothie_update.py +15 -0
- opentrons/system/wifi.py +204 -0
- opentrons/tools/__init__.py +0 -0
- opentrons/tools/args_handler.py +22 -0
- opentrons/tools/write_pipette_memory.py +157 -0
- opentrons/types.py +618 -0
- opentrons/util/__init__.py +1 -0
- opentrons/util/async_helpers.py +166 -0
- opentrons/util/broker.py +84 -0
- opentrons/util/change_notifier.py +47 -0
- opentrons/util/entrypoint_util.py +278 -0
- opentrons/util/get_union_elements.py +26 -0
- opentrons/util/helpers.py +6 -0
- opentrons/util/linal.py +178 -0
- opentrons/util/logging_config.py +265 -0
- opentrons/util/logging_queue_handler.py +61 -0
- opentrons/util/performance_helpers.py +157 -0
- opentrons-8.6.0.dist-info/METADATA +37 -0
- opentrons-8.6.0.dist-info/RECORD +601 -0
- opentrons-8.6.0.dist-info/WHEEL +4 -0
- opentrons-8.6.0.dist-info/entry_points.txt +3 -0
- opentrons-8.6.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,1579 @@
|
|
|
1
|
+
"""opentrons.protocol_api.labware: classes and functions for labware handling
|
|
2
|
+
|
|
3
|
+
This module provides things like :py:class:`Labware`, and :py:class:`Well`
|
|
4
|
+
to encapsulate labware instances used in protocols
|
|
5
|
+
and their wells. It also provides helper functions to load and save labware
|
|
6
|
+
and labware calibration offsets. It contains all the code necessary to
|
|
7
|
+
transform from labware symbolic points (such as "well a1 of an opentrons
|
|
8
|
+
tiprack") to points in deck coordinates.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
from itertools import dropwhile
|
|
16
|
+
from typing import (
|
|
17
|
+
TYPE_CHECKING,
|
|
18
|
+
Any,
|
|
19
|
+
List,
|
|
20
|
+
Dict,
|
|
21
|
+
Optional,
|
|
22
|
+
Tuple,
|
|
23
|
+
cast,
|
|
24
|
+
Sequence,
|
|
25
|
+
Mapping,
|
|
26
|
+
Union,
|
|
27
|
+
Literal,
|
|
28
|
+
Callable,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
from opentrons_shared_data.labware.types import (
|
|
32
|
+
LabwareDefinition,
|
|
33
|
+
LabwareDefinition2,
|
|
34
|
+
LabwareParameters2,
|
|
35
|
+
LabwareParameters3,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
from opentrons.types import (
|
|
39
|
+
Location,
|
|
40
|
+
Point,
|
|
41
|
+
NozzleMapInterface,
|
|
42
|
+
MeniscusTrackingTarget,
|
|
43
|
+
Mount,
|
|
44
|
+
)
|
|
45
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
46
|
+
from opentrons.protocols.api_support.util import (
|
|
47
|
+
requires_version,
|
|
48
|
+
APIVersionError,
|
|
49
|
+
UnsupportedAPIError,
|
|
50
|
+
)
|
|
51
|
+
from opentrons.protocol_engine.types import LiquidTrackingType
|
|
52
|
+
|
|
53
|
+
# TODO(mc, 2022-09-02): re-exports provided for backwards compatibility
|
|
54
|
+
# remove when their usage is no longer needed
|
|
55
|
+
from opentrons.protocols.labware import ( # noqa: F401
|
|
56
|
+
get_labware_definition as get_labware_definition,
|
|
57
|
+
verify_definition as verify_definition,
|
|
58
|
+
save_definition as save_definition,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
from . import validation
|
|
62
|
+
from ._liquid import Liquid
|
|
63
|
+
from ._types import OffDeckType
|
|
64
|
+
from .core import well_grid
|
|
65
|
+
from .core.engine import (
|
|
66
|
+
ENGINE_CORE_API_VERSION,
|
|
67
|
+
SET_OFFSET_RESTORED_API_VERSION,
|
|
68
|
+
)
|
|
69
|
+
from .core.labware import AbstractLabware
|
|
70
|
+
from .core.module import AbstractModuleCore
|
|
71
|
+
from .core.core_map import LoadedCoreMap
|
|
72
|
+
from .core.legacy.legacy_labware_core import LegacyLabwareCore
|
|
73
|
+
from .core.legacy.legacy_well_core import LegacyWellCore
|
|
74
|
+
from .core.legacy.well_geometry import WellGeometry
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if TYPE_CHECKING:
|
|
78
|
+
from .core.common import LabwareCore, WellCore, ProtocolCore
|
|
79
|
+
from .protocol_context import ModuleTypes
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
_log = logging.getLogger(__name__)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_IGNORE_API_VERSION_BREAKPOINT = APIVersion(2, 13)
|
|
86
|
+
"""API version after which to respect... the API version setting.
|
|
87
|
+
|
|
88
|
+
At this API version and below, `Labware` objects were always
|
|
89
|
+
erroneously constructed set to MAX_SUPPORTED_VERSION.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TipSelectionError(Exception):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class OutOfTipsError(Exception):
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Well:
|
|
102
|
+
"""
|
|
103
|
+
The Well class represents a single well in a :py:class:`Labware`. It provides parameters and functions for three major uses:
|
|
104
|
+
|
|
105
|
+
- Calculating positions relative to the well. See :ref:`position-relative-labware` for details.
|
|
106
|
+
|
|
107
|
+
- Returning well measurements. See :ref:`new-labware-well-properties` for details.
|
|
108
|
+
|
|
109
|
+
- Specifying what liquid should be in the well at the beginning of a protocol. See :ref:`labeling-liquids` for details.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def __init__(self, parent: Labware, core: WellCore, api_version: APIVersion):
|
|
113
|
+
if api_version <= _IGNORE_API_VERSION_BREAKPOINT:
|
|
114
|
+
api_version = _IGNORE_API_VERSION_BREAKPOINT
|
|
115
|
+
|
|
116
|
+
self._parent = parent
|
|
117
|
+
self._core = core
|
|
118
|
+
self._api_version = api_version
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
@requires_version(2, 0)
|
|
122
|
+
def api_version(self) -> APIVersion:
|
|
123
|
+
return self._api_version
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
@requires_version(2, 0)
|
|
127
|
+
def parent(self) -> Labware:
|
|
128
|
+
"""The :py:class:`.Labware` object that the well is a part of."""
|
|
129
|
+
return self._parent
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
@requires_version(2, 0)
|
|
133
|
+
def has_tip(self) -> bool:
|
|
134
|
+
"""Whether this well contains an unused tip.
|
|
135
|
+
|
|
136
|
+
From API v2.2 on:
|
|
137
|
+
|
|
138
|
+
- Returns ``False`` if:
|
|
139
|
+
|
|
140
|
+
- the well has no tip present, or
|
|
141
|
+
- the well has a tip that's been used by the protocol previously
|
|
142
|
+
|
|
143
|
+
- Returns ``True`` if the well has an unused tip.
|
|
144
|
+
|
|
145
|
+
Before API v2.2:
|
|
146
|
+
|
|
147
|
+
- Returns ``True`` as long as the well has a tip, even if it is used.
|
|
148
|
+
|
|
149
|
+
Always ``False`` if the parent labware isn't a tip rack.
|
|
150
|
+
"""
|
|
151
|
+
return self._core.has_tip()
|
|
152
|
+
|
|
153
|
+
@has_tip.setter
|
|
154
|
+
def has_tip(self, value: bool) -> None:
|
|
155
|
+
_log.warning(
|
|
156
|
+
"Setting the `Well.has_tip` property manually has been deprecated"
|
|
157
|
+
" and will raise an error in a future version of the Python Protocol API."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
self._core.set_has_tip(value)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def max_volume(self) -> float:
|
|
164
|
+
"""The maximum volume, in µL, that the well can hold.
|
|
165
|
+
|
|
166
|
+
This amount is set by the JSON labware definition, specifically the ``totalLiquidVolume`` property of the particular well.
|
|
167
|
+
"""
|
|
168
|
+
return self._core.get_max_volume()
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def geometry(self) -> WellGeometry:
|
|
172
|
+
if isinstance(self._core, LegacyWellCore):
|
|
173
|
+
return self._core.geometry
|
|
174
|
+
raise UnsupportedAPIError(api_element="Well.geometry")
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
@requires_version(2, 0)
|
|
178
|
+
def diameter(self) -> Optional[float]:
|
|
179
|
+
"""
|
|
180
|
+
The diameter, in mm, of a circular well. Returns ``None``
|
|
181
|
+
if the well is not circular.
|
|
182
|
+
"""
|
|
183
|
+
return self._core.diameter
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
@requires_version(2, 9)
|
|
187
|
+
def length(self) -> Optional[float]:
|
|
188
|
+
"""
|
|
189
|
+
The length, in mm, of a rectangular well along the x-axis (left to right).
|
|
190
|
+
Returns ``None`` if the well is not rectangular.
|
|
191
|
+
"""
|
|
192
|
+
return self._core.length
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
@requires_version(2, 9)
|
|
196
|
+
def width(self) -> Optional[float]:
|
|
197
|
+
"""
|
|
198
|
+
The width, in mm, of a rectangular well along the y-axis (front to back).
|
|
199
|
+
Returns ``None`` if the well is not rectangular.
|
|
200
|
+
"""
|
|
201
|
+
return self._core.width
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
@requires_version(2, 9)
|
|
205
|
+
def depth(self) -> float:
|
|
206
|
+
"""
|
|
207
|
+
The depth, in mm, of a well along the z-axis, from the very top of the well to
|
|
208
|
+
the very bottom.
|
|
209
|
+
"""
|
|
210
|
+
return self._core.depth
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def display_name(self) -> str:
|
|
214
|
+
"""A human-readable name for the well, including labware and deck location.
|
|
215
|
+
|
|
216
|
+
For example, "A1 of Corning 96 Well Plate 360 µL Flat on slot D1". Run log
|
|
217
|
+
entries use this format for identifying wells. See
|
|
218
|
+
:py:meth:`.ProtocolContext.commands`.
|
|
219
|
+
"""
|
|
220
|
+
return self._core.get_display_name()
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
@requires_version(2, 7)
|
|
224
|
+
def well_name(self) -> str:
|
|
225
|
+
"""A string representing the well's coordinates.
|
|
226
|
+
|
|
227
|
+
For example, "A1" or "H12".
|
|
228
|
+
|
|
229
|
+
The format of strings that this property returns is the same format as the key
|
|
230
|
+
for :ref:`accessing wells in a dictionary <well-dictionary-access>`.
|
|
231
|
+
"""
|
|
232
|
+
return self._core.get_name()
|
|
233
|
+
|
|
234
|
+
@requires_version(2, 0)
|
|
235
|
+
def top(self, z: float = 0.0) -> Location:
|
|
236
|
+
"""
|
|
237
|
+
:param z: An offset on the z-axis, in mm. Positive offsets are higher and
|
|
238
|
+
negative offsets are lower.
|
|
239
|
+
|
|
240
|
+
:return: A :py:class:`~opentrons.types.Location` corresponding to the
|
|
241
|
+
absolute position of the top-center of the well, plus the ``z`` offset
|
|
242
|
+
(if specified).
|
|
243
|
+
"""
|
|
244
|
+
return Location(self._core.get_top(z_offset=z), self)
|
|
245
|
+
|
|
246
|
+
@requires_version(2, 0)
|
|
247
|
+
def bottom(self, z: float = 0.0) -> Location:
|
|
248
|
+
"""
|
|
249
|
+
:param z: An offset on the z-axis, in mm. Positive offsets are higher and
|
|
250
|
+
negative offsets are lower.
|
|
251
|
+
|
|
252
|
+
:return: A :py:class:`~opentrons.types.Location` corresponding to the
|
|
253
|
+
absolute position of the bottom-center of the well, plus the ``z`` offset
|
|
254
|
+
(if specified).
|
|
255
|
+
"""
|
|
256
|
+
return Location(self._core.get_bottom(z_offset=z), self)
|
|
257
|
+
|
|
258
|
+
@requires_version(2, 0)
|
|
259
|
+
def center(self) -> Location:
|
|
260
|
+
"""
|
|
261
|
+
:return: A :py:class:`~opentrons.types.Location` corresponding to the
|
|
262
|
+
absolute position of the center of the well (in all three dimensions).
|
|
263
|
+
"""
|
|
264
|
+
return Location(self._core.get_center(), self)
|
|
265
|
+
|
|
266
|
+
@requires_version(2, 21)
|
|
267
|
+
def meniscus(
|
|
268
|
+
self, z: float = 0.0, target: Literal["start", "end", "dynamic"] = "end"
|
|
269
|
+
) -> Location:
|
|
270
|
+
"""
|
|
271
|
+
:param z: An offset on the z-axis, in mm. Positive offsets are higher and
|
|
272
|
+
negative offsets are lower.
|
|
273
|
+
:param target: The relative position of the liquid meniscus inside the well to target when performing a liquid handling operation.
|
|
274
|
+
|
|
275
|
+
:return: A :py:class:`~opentrons.types.Location` corresponding to the liquid meniscus, plus a target position and ``z`` offset as specified.
|
|
276
|
+
|
|
277
|
+
"""
|
|
278
|
+
return Location(
|
|
279
|
+
point=Point(x=0, y=0, z=z),
|
|
280
|
+
labware=self,
|
|
281
|
+
_meniscus_tracking=MeniscusTrackingTarget(target),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
@requires_version(2, 8)
|
|
285
|
+
def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
|
|
286
|
+
"""
|
|
287
|
+
Specifies a :py:class:`~opentrons.types.Point` based on fractions of the
|
|
288
|
+
distance from the center of the well to the edge along each axis.
|
|
289
|
+
|
|
290
|
+
For example, ``from_center_cartesian(0, 0, 0.5)`` specifies a point at the
|
|
291
|
+
well's center on the x- and y-axis, and half of the distance from the center of
|
|
292
|
+
the well to its top along the z-axis. To move the pipette to that location,
|
|
293
|
+
construct a :py:class:`~opentrons.types.Location` relative to the same well::
|
|
294
|
+
|
|
295
|
+
location = types.Location(
|
|
296
|
+
plate["A1"].from_center_cartesian(0, 0, 0.5), plate["A1"]
|
|
297
|
+
)
|
|
298
|
+
pipette.move_to(location)
|
|
299
|
+
|
|
300
|
+
See :ref:`points-locations` for more information.
|
|
301
|
+
|
|
302
|
+
:param x: The fraction of the distance from the well's center to its edge
|
|
303
|
+
along the x-axis. Negative values are to the left, and positive values
|
|
304
|
+
are to the right.
|
|
305
|
+
:param y: The fraction of the distance from the well's center to its edge
|
|
306
|
+
along the y-axis. Negative values are to the front, and positive values
|
|
307
|
+
are to the back.
|
|
308
|
+
:param z: The fraction of the distance from the well's center to its edge
|
|
309
|
+
along the x-axis. Negative values are down, and positive values are up.
|
|
310
|
+
|
|
311
|
+
:return: A :py:class:`~opentrons.types.Point` representing the specified
|
|
312
|
+
position in absolute deck coordinates.
|
|
313
|
+
|
|
314
|
+
.. note:: Even if the absolute values of ``x``, ``y``, and ``z`` are all less
|
|
315
|
+
than 1, a location constructed from the well and the result of
|
|
316
|
+
``from_center_cartesian`` may be outside of the physical well. For example,
|
|
317
|
+
``from_center_cartesian(0.9, 0.9, 0)`` would be outside of a cylindrical
|
|
318
|
+
well, but inside a square well.
|
|
319
|
+
|
|
320
|
+
"""
|
|
321
|
+
return self._core.from_center_cartesian(x, y, z)
|
|
322
|
+
|
|
323
|
+
@requires_version(2, 14)
|
|
324
|
+
def load_liquid(self, liquid: Liquid, volume: float) -> None:
|
|
325
|
+
"""
|
|
326
|
+
Load a liquid into a well.
|
|
327
|
+
|
|
328
|
+
:param Liquid liquid: The liquid to load into the well.
|
|
329
|
+
:param float volume: The volume of liquid to load, in µL.
|
|
330
|
+
|
|
331
|
+
.. deprecated:: 2.22
|
|
332
|
+
Use :py:meth:`.Labware.load_liquid`, :py:meth:`.Labware.load_liquid_by_well`, or :py:meth:`.Labware.load_empty` instead.
|
|
333
|
+
|
|
334
|
+
"""
|
|
335
|
+
self._core.load_liquid(
|
|
336
|
+
liquid=liquid,
|
|
337
|
+
volume=volume,
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
@requires_version(2, 21)
|
|
341
|
+
def current_liquid_height(self) -> LiquidTrackingType:
|
|
342
|
+
"""Get the current liquid height in a well."""
|
|
343
|
+
return self._core.current_liquid_height()
|
|
344
|
+
|
|
345
|
+
@requires_version(2, 21)
|
|
346
|
+
def current_liquid_volume(self) -> LiquidTrackingType:
|
|
347
|
+
"""Get the current liquid volume in a well."""
|
|
348
|
+
return self._core.get_liquid_volume()
|
|
349
|
+
|
|
350
|
+
@requires_version(2, 24)
|
|
351
|
+
def volume_from_height(self, height: LiquidTrackingType) -> LiquidTrackingType:
|
|
352
|
+
"""Return the volume contained in a well at any height."""
|
|
353
|
+
return self._core.volume_from_height(height)
|
|
354
|
+
|
|
355
|
+
@requires_version(2, 24)
|
|
356
|
+
def height_from_volume(self, volume: LiquidTrackingType) -> LiquidTrackingType:
|
|
357
|
+
"""Return the height in a well corresponding to a given volume."""
|
|
358
|
+
return self._core.height_from_volume(volume)
|
|
359
|
+
|
|
360
|
+
@requires_version(2, 21)
|
|
361
|
+
def estimate_liquid_height_after_pipetting(
|
|
362
|
+
self,
|
|
363
|
+
mount: Mount | str,
|
|
364
|
+
operation_volume: float,
|
|
365
|
+
) -> LiquidTrackingType:
|
|
366
|
+
"""Check the height of the liquid within a well.
|
|
367
|
+
|
|
368
|
+
:returns: The height, in mm, of the liquid from the deck.
|
|
369
|
+
|
|
370
|
+
:meta private:
|
|
371
|
+
|
|
372
|
+
This is intended for Opentrons internal use only and is not a guaranteed API.
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
projected_final_height = self._core.estimate_liquid_height_after_pipetting(
|
|
376
|
+
operation_volume=operation_volume, mount=mount
|
|
377
|
+
)
|
|
378
|
+
return projected_final_height
|
|
379
|
+
|
|
380
|
+
def _from_center_cartesian(self, x: float, y: float, z: float) -> Point:
|
|
381
|
+
"""
|
|
382
|
+
Private version of from_center_cartesian. Present only for backward
|
|
383
|
+
compatibility.
|
|
384
|
+
"""
|
|
385
|
+
_log.warning(
|
|
386
|
+
"This method is deprecated. Please use 'from_center_cartesian' instead."
|
|
387
|
+
)
|
|
388
|
+
return self.from_center_cartesian(x, y, z)
|
|
389
|
+
|
|
390
|
+
def __repr__(self) -> str:
|
|
391
|
+
return self._core.get_display_name()
|
|
392
|
+
|
|
393
|
+
def __eq__(self, other: object) -> bool:
|
|
394
|
+
"""
|
|
395
|
+
Assuming that equality of wells in this system is having the same
|
|
396
|
+
absolute coordinates for the top.
|
|
397
|
+
"""
|
|
398
|
+
if not isinstance(other, Well):
|
|
399
|
+
return NotImplemented
|
|
400
|
+
return self.top().point == other.top().point
|
|
401
|
+
|
|
402
|
+
def __hash__(self) -> int:
|
|
403
|
+
return hash(self.top().point)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
class Labware:
|
|
407
|
+
"""
|
|
408
|
+
This class represents a piece of labware.
|
|
409
|
+
|
|
410
|
+
Labware available in the API generally fall under two categories.
|
|
411
|
+
|
|
412
|
+
- Consumable labware: well plates, tubes in racks, reservoirs, tip racks, etc.
|
|
413
|
+
- Adapters: durable items that hold other labware, either on modules or directly
|
|
414
|
+
on the deck.
|
|
415
|
+
|
|
416
|
+
The ``Labware`` class defines the physical geometry of the labware
|
|
417
|
+
and provides methods for :ref:`accessing wells <new-well-access>` within the labware.
|
|
418
|
+
|
|
419
|
+
Create ``Labware`` objects by calling the appropriate ``load_labware()`` method,
|
|
420
|
+
depending on where you are loading the labware. For example, to load labware on a
|
|
421
|
+
Thermocycler Module, use :py:meth:`.ThermocyclerContext.load_labware`. To load
|
|
422
|
+
labware directly on the deck, use :py:meth:`.ProtocolContext.load_labware`. See
|
|
423
|
+
:ref:`loading-labware`.
|
|
424
|
+
|
|
425
|
+
"""
|
|
426
|
+
|
|
427
|
+
def __init__(
|
|
428
|
+
self,
|
|
429
|
+
core: AbstractLabware[Any],
|
|
430
|
+
api_version: APIVersion,
|
|
431
|
+
protocol_core: ProtocolCore,
|
|
432
|
+
core_map: LoadedCoreMap,
|
|
433
|
+
) -> None:
|
|
434
|
+
"""
|
|
435
|
+
:param core: The class that implements the public interface
|
|
436
|
+
of the class.
|
|
437
|
+
:param APIVersion api_level: the API version to set for the instance.
|
|
438
|
+
The :py:class:`.Labware` will
|
|
439
|
+
conform to this level. If not specified,
|
|
440
|
+
defaults to
|
|
441
|
+
:py:attr:`.MAX_SUPPORTED_VERSION`.
|
|
442
|
+
"""
|
|
443
|
+
if api_version <= _IGNORE_API_VERSION_BREAKPOINT:
|
|
444
|
+
api_version = _IGNORE_API_VERSION_BREAKPOINT
|
|
445
|
+
|
|
446
|
+
self._api_version = api_version
|
|
447
|
+
self._core: LabwareCore = core
|
|
448
|
+
self._protocol_core = protocol_core
|
|
449
|
+
self._core_map = core_map
|
|
450
|
+
|
|
451
|
+
well_columns = core.get_well_columns()
|
|
452
|
+
self._well_grid = well_grid.create(columns=well_columns)
|
|
453
|
+
self._wells_by_name = {
|
|
454
|
+
well_name: Well(
|
|
455
|
+
parent=self, core=core.get_well_core(well_name), api_version=api_version
|
|
456
|
+
)
|
|
457
|
+
for column in well_columns
|
|
458
|
+
for well_name in column
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
@property
|
|
462
|
+
def separate_calibration(self) -> bool:
|
|
463
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
464
|
+
raise UnsupportedAPIError(
|
|
465
|
+
api_element="Labware.separate_calibration",
|
|
466
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
467
|
+
current_version=f"{self._api_version}",
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
_log.warning(
|
|
471
|
+
"Labware.separate_calibrations is a deprecated internal property."
|
|
472
|
+
" It no longer has meaning, but will always return `False`"
|
|
473
|
+
)
|
|
474
|
+
return False
|
|
475
|
+
|
|
476
|
+
@classmethod
|
|
477
|
+
def _builder_for_core_map(
|
|
478
|
+
cls,
|
|
479
|
+
api_version: APIVersion,
|
|
480
|
+
protocol_core: ProtocolCore,
|
|
481
|
+
core_map: LoadedCoreMap,
|
|
482
|
+
) -> Callable[[AbstractLabware[Any]], Labware]:
|
|
483
|
+
def _do_build(core: AbstractLabware[Any]) -> Labware:
|
|
484
|
+
return Labware(
|
|
485
|
+
core=core,
|
|
486
|
+
api_version=api_version,
|
|
487
|
+
protocol_core=protocol_core,
|
|
488
|
+
core_map=core_map,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
return _do_build
|
|
492
|
+
|
|
493
|
+
@property
|
|
494
|
+
@requires_version(2, 0)
|
|
495
|
+
def api_version(self) -> APIVersion:
|
|
496
|
+
"""See :py:obj:`.ProtocolContext.api_version`."""
|
|
497
|
+
return self._api_version
|
|
498
|
+
|
|
499
|
+
def __getitem__(self, key: str) -> Well:
|
|
500
|
+
return self.wells_by_name()[key]
|
|
501
|
+
|
|
502
|
+
@property
|
|
503
|
+
@requires_version(2, 0)
|
|
504
|
+
def uri(self) -> str:
|
|
505
|
+
"""A string fully identifying the labware.
|
|
506
|
+
|
|
507
|
+
The URI has three parts and follows the pattern ``"namespace/load_name/version"``.
|
|
508
|
+
For example, ``opentrons/corning_96_wellplate_360ul_flat/2``.
|
|
509
|
+
"""
|
|
510
|
+
return self._core.get_uri()
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
@requires_version(2, 0)
|
|
514
|
+
def parent(self) -> Union[str, Labware, ModuleTypes, OffDeckType]:
|
|
515
|
+
"""Where the labware is loaded.
|
|
516
|
+
|
|
517
|
+
This corresponds to the physical object that the labware *directly* rests upon.
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
If the labware is directly on the robot's deck, the ``str`` name of the deck slot,
|
|
521
|
+
like ``"D1"`` (Flex) or ``"1"`` (OT-2). See :ref:`deck-slots`.
|
|
522
|
+
|
|
523
|
+
If the labware is on a module, a module context.
|
|
524
|
+
|
|
525
|
+
If the labware is on a labware or adapter, a :py:class:`Labware`.
|
|
526
|
+
|
|
527
|
+
If the labware is off-deck, :py:obj:`OFF_DECK`.
|
|
528
|
+
|
|
529
|
+
.. versionchanged:: 2.14
|
|
530
|
+
Return type for module parent changed.
|
|
531
|
+
Formerly, the API returned an internal geometry interface.
|
|
532
|
+
.. versionchanged:: 2.15
|
|
533
|
+
Returns a :py:class:`Labware` if the labware is loaded onto a labware/adapter.
|
|
534
|
+
Returns :py:obj:`OFF_DECK` if the labware is off-deck.
|
|
535
|
+
Formerly, if the labware was removed by using ``del`` on :py:obj:`.deck`,
|
|
536
|
+
this would return where it was before its removal.
|
|
537
|
+
"""
|
|
538
|
+
if isinstance(self._core, LegacyLabwareCore):
|
|
539
|
+
# Type ignoring to preserve backwards compatibility
|
|
540
|
+
return self._core.get_geometry().parent.labware.object # type: ignore
|
|
541
|
+
|
|
542
|
+
assert self._protocol_core and self._core_map, "Labware initialized incorrectly"
|
|
543
|
+
|
|
544
|
+
labware_location = self._protocol_core.get_labware_location(self._core)
|
|
545
|
+
|
|
546
|
+
if isinstance(labware_location, (AbstractLabware, AbstractModuleCore)):
|
|
547
|
+
return self._core_map.get(labware_location)
|
|
548
|
+
|
|
549
|
+
return labware_location
|
|
550
|
+
|
|
551
|
+
@property
|
|
552
|
+
@requires_version(2, 0)
|
|
553
|
+
def name(self) -> str:
|
|
554
|
+
"""The display name of the labware.
|
|
555
|
+
|
|
556
|
+
If you specified a value for ``label`` when loading the labware, ``name`` is
|
|
557
|
+
that value.
|
|
558
|
+
|
|
559
|
+
Otherwise, it is the :py:obj:`~.Labware.load_name` of the labware.
|
|
560
|
+
"""
|
|
561
|
+
return self._core.get_name()
|
|
562
|
+
|
|
563
|
+
@name.setter
|
|
564
|
+
def name(self, new_name: str) -> None:
|
|
565
|
+
"""Set the labware name.
|
|
566
|
+
|
|
567
|
+
.. deprecated: 2.14
|
|
568
|
+
Set the name of labware in `load_labware` instead.
|
|
569
|
+
"""
|
|
570
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
571
|
+
raise UnsupportedAPIError(
|
|
572
|
+
api_element="Labware.name setter",
|
|
573
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
574
|
+
current_version=f"{self._api_version}",
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# TODO(mc, 2023-02-06): this assert should be enough for mypy
|
|
578
|
+
# investigate if upgrading mypy allows the `cast` to be removed
|
|
579
|
+
assert isinstance(self._core, LegacyLabwareCore)
|
|
580
|
+
cast(LegacyLabwareCore, self._core).set_name(new_name)
|
|
581
|
+
|
|
582
|
+
@property
|
|
583
|
+
@requires_version(2, 0)
|
|
584
|
+
def load_name(self) -> str:
|
|
585
|
+
"""The API load name of the labware definition."""
|
|
586
|
+
return self._core.load_name
|
|
587
|
+
|
|
588
|
+
@property
|
|
589
|
+
@requires_version(2, 0)
|
|
590
|
+
def parameters(self) -> "LabwareParameters2 | LabwareParameters3":
|
|
591
|
+
"""Internal properties of a labware including type and quirks."""
|
|
592
|
+
return self._core.get_parameters()
|
|
593
|
+
|
|
594
|
+
@property
|
|
595
|
+
@requires_version(2, 0)
|
|
596
|
+
def quirks(self) -> List[str]:
|
|
597
|
+
"""Quirks specific to this labware."""
|
|
598
|
+
return self._core.get_quirks()
|
|
599
|
+
|
|
600
|
+
# TODO(mm, 2023-02-08):
|
|
601
|
+
# Specify units and origin after we resolve RSS-110.
|
|
602
|
+
# Remove warning once we resolve RSS-109 more broadly.
|
|
603
|
+
@property
|
|
604
|
+
@requires_version(2, 0)
|
|
605
|
+
def magdeck_engage_height(self) -> Optional[float]:
|
|
606
|
+
"""Return the default magnet engage height that
|
|
607
|
+
:py:meth:`.MagneticModuleContext.engage` will use for this labware.
|
|
608
|
+
|
|
609
|
+
.. warning::
|
|
610
|
+
This currently returns confusing and unpredictable results that do not
|
|
611
|
+
necessarily match what :py:meth:`.MagneticModuleContext.engage` will
|
|
612
|
+
actually choose for its default height.
|
|
613
|
+
|
|
614
|
+
The confusion is related to how this height's units and origin point are
|
|
615
|
+
defined, and differences between Magnetic Module generations.
|
|
616
|
+
|
|
617
|
+
For now, we recommend you avoid accessing this property directly.
|
|
618
|
+
"""
|
|
619
|
+
# Return the raw value straight from the labware definition. For several
|
|
620
|
+
# reasons (see RSS-109), this may not match the actual default height chosen
|
|
621
|
+
# by MagneticModuleContext.engage().
|
|
622
|
+
p = self._core.get_parameters()
|
|
623
|
+
if not p["isMagneticModuleCompatible"]:
|
|
624
|
+
return None
|
|
625
|
+
else:
|
|
626
|
+
return p["magneticModuleEngageHeight"]
|
|
627
|
+
|
|
628
|
+
@property
|
|
629
|
+
@requires_version(2, 15)
|
|
630
|
+
def child(self) -> Optional[Labware]:
|
|
631
|
+
"""The labware (if any) present on this labware."""
|
|
632
|
+
labware_core = self._protocol_core.get_labware_on_labware(self._core)
|
|
633
|
+
return self._core_map.get(labware_core)
|
|
634
|
+
|
|
635
|
+
@requires_version(2, 15)
|
|
636
|
+
def load_labware(
|
|
637
|
+
self,
|
|
638
|
+
name: str,
|
|
639
|
+
label: Optional[str] = None,
|
|
640
|
+
lid: Optional[str] = None,
|
|
641
|
+
namespace: Optional[str] = None,
|
|
642
|
+
version: Optional[int] = None,
|
|
643
|
+
) -> Labware:
|
|
644
|
+
"""Load a compatible labware onto the labware using its load parameters.
|
|
645
|
+
|
|
646
|
+
The parameters of this function behave like those of
|
|
647
|
+
:py:obj:`ProtocolContext.load_labware` (which loads labware directly
|
|
648
|
+
onto the deck). Note that the parameter ``name`` here corresponds to
|
|
649
|
+
``load_name`` on the ``ProtocolContext`` function.
|
|
650
|
+
|
|
651
|
+
:returns: The initialized and loaded labware object.
|
|
652
|
+
"""
|
|
653
|
+
labware_core = self._protocol_core.load_labware(
|
|
654
|
+
load_name=name,
|
|
655
|
+
label=label,
|
|
656
|
+
namespace=namespace,
|
|
657
|
+
version=version,
|
|
658
|
+
location=self._core,
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
labware = Labware(
|
|
662
|
+
core=labware_core,
|
|
663
|
+
api_version=self._api_version,
|
|
664
|
+
protocol_core=self._protocol_core,
|
|
665
|
+
core_map=self._core_map,
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
self._core_map.add(labware_core, labware)
|
|
669
|
+
|
|
670
|
+
if lid is not None:
|
|
671
|
+
if self._api_version < validation.LID_STACK_VERSION_GATE:
|
|
672
|
+
raise APIVersionError(
|
|
673
|
+
api_element="Loading a Lid on a Labware",
|
|
674
|
+
until_version="2.23",
|
|
675
|
+
current_version=f"{self._api_version}",
|
|
676
|
+
)
|
|
677
|
+
self._protocol_core.load_lid(
|
|
678
|
+
load_name=lid,
|
|
679
|
+
location=labware_core,
|
|
680
|
+
namespace=namespace,
|
|
681
|
+
version=version,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
return labware
|
|
685
|
+
|
|
686
|
+
@requires_version(2, 15)
|
|
687
|
+
def load_labware_from_definition(
|
|
688
|
+
self, definition: LabwareDefinition, label: Optional[str] = None
|
|
689
|
+
) -> Labware:
|
|
690
|
+
"""Load a compatible labware onto the labware using an inline definition.
|
|
691
|
+
|
|
692
|
+
:param definition: The labware definition.
|
|
693
|
+
:param str label: An optional special name to give the labware. If specified,
|
|
694
|
+
this is how the labware will appear in the run log, Labware Position
|
|
695
|
+
Check, and elsewhere in the Opentrons App and on the touchscreen.
|
|
696
|
+
|
|
697
|
+
:returns: The initialized and loaded labware object.
|
|
698
|
+
"""
|
|
699
|
+
load_params = self._protocol_core.add_labware_definition(definition)
|
|
700
|
+
|
|
701
|
+
return self.load_labware(
|
|
702
|
+
name=load_params.load_name,
|
|
703
|
+
namespace=load_params.namespace,
|
|
704
|
+
version=load_params.version,
|
|
705
|
+
label=label,
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
@requires_version(2, 23)
|
|
709
|
+
def load_lid_stack(
|
|
710
|
+
self,
|
|
711
|
+
load_name: str,
|
|
712
|
+
quantity: int,
|
|
713
|
+
namespace: Optional[str] = None,
|
|
714
|
+
version: Optional[int] = None,
|
|
715
|
+
) -> Labware:
|
|
716
|
+
"""
|
|
717
|
+
Load a stack of Opentrons Tough Auto-Sealing Lids onto a valid deck location or adapter.
|
|
718
|
+
|
|
719
|
+
:param str load_name: A string to use for looking up a lid definition.
|
|
720
|
+
You can find the ``load_name`` for any standard lid on the Opentrons
|
|
721
|
+
`Labware Library <https://labware.opentrons.com>`_.
|
|
722
|
+
:param int quantity: The quantity of lids to be loaded in the stack.
|
|
723
|
+
:param str namespace: The namespace that the lid labware definition belongs to.
|
|
724
|
+
If unspecified, the API will automatically search two namespaces:
|
|
725
|
+
|
|
726
|
+
- ``"opentrons"``, to load standard Opentrons labware definitions.
|
|
727
|
+
- ``"custom_beta"``, to load custom labware definitions created with the
|
|
728
|
+
`Custom Labware Creator <https://labware.opentrons.com/create>`__.
|
|
729
|
+
|
|
730
|
+
You might need to specify an explicit ``namespace`` if you have a custom
|
|
731
|
+
definition whose ``load_name`` is the same as an Opentrons-verified
|
|
732
|
+
definition, and you want to explicitly choose one or the other.
|
|
733
|
+
|
|
734
|
+
:param version: The version of the labware definition. You should normally
|
|
735
|
+
leave this unspecified to let ``load_lid_stack()`` choose a version
|
|
736
|
+
automatically.
|
|
737
|
+
|
|
738
|
+
:return: The initialized and loaded labware object representing the lid stack.
|
|
739
|
+
"""
|
|
740
|
+
if self._api_version < validation.LID_STACK_VERSION_GATE:
|
|
741
|
+
raise APIVersionError(
|
|
742
|
+
api_element="Loading a Lid Stack",
|
|
743
|
+
until_version="2.23",
|
|
744
|
+
current_version=f"{self._api_version}",
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
load_location = self._core
|
|
748
|
+
|
|
749
|
+
load_name = validation.ensure_lowercase_name(load_name)
|
|
750
|
+
|
|
751
|
+
result = self._protocol_core.load_lid_stack(
|
|
752
|
+
load_name=load_name,
|
|
753
|
+
location=load_location,
|
|
754
|
+
quantity=quantity,
|
|
755
|
+
namespace=namespace,
|
|
756
|
+
version=version,
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
labware = Labware(
|
|
760
|
+
core=result,
|
|
761
|
+
api_version=self._api_version,
|
|
762
|
+
protocol_core=self._protocol_core,
|
|
763
|
+
core_map=self._core_map,
|
|
764
|
+
)
|
|
765
|
+
return labware
|
|
766
|
+
|
|
767
|
+
def set_calibration(self, delta: Point) -> None:
|
|
768
|
+
"""
|
|
769
|
+
An internal, deprecated method used for updating the labware offset.
|
|
770
|
+
|
|
771
|
+
.. deprecated:: 2.14
|
|
772
|
+
"""
|
|
773
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
774
|
+
raise UnsupportedAPIError(
|
|
775
|
+
api_element="Labware.set_calibration()",
|
|
776
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
777
|
+
current_version=f"{self._api_version}",
|
|
778
|
+
extra_message="Try using the Opentrons App's Labware Position Check.",
|
|
779
|
+
)
|
|
780
|
+
self._core.set_calibration(delta)
|
|
781
|
+
|
|
782
|
+
@requires_version(2, 12)
|
|
783
|
+
def set_offset(self, x: float, y: float, z: float) -> None:
|
|
784
|
+
"""Set the labware's position offset.
|
|
785
|
+
|
|
786
|
+
The offset is an x, y, z vector in deck coordinates
|
|
787
|
+
(see :ref:`protocol-api-deck-coords`).
|
|
788
|
+
|
|
789
|
+
How the motion system applies the offset depends on the API level of the protocol.
|
|
790
|
+
|
|
791
|
+
.. list-table::
|
|
792
|
+
:header-rows: 1
|
|
793
|
+
:widths: 1 5
|
|
794
|
+
|
|
795
|
+
* - API level
|
|
796
|
+
- Offset behavior
|
|
797
|
+
* - 2.12–2.13
|
|
798
|
+
- Offsets only apply to the exact :py:class:`.Labware` instance.
|
|
799
|
+
|
|
800
|
+
If your protocol has multiple instances of the same type of labware,
|
|
801
|
+
you must either use ``set_offset()`` on all of them or none of them.
|
|
802
|
+
* - 2.14–2.17
|
|
803
|
+
- ``set_offset()`` is not available, and the API raises an error.
|
|
804
|
+
* - 2.18--2.22
|
|
805
|
+
-
|
|
806
|
+
- Offsets apply to any labware of the same type, in the same on-deck location.
|
|
807
|
+
- Offsets can't be set on labware that is currently off-deck.
|
|
808
|
+
- Offsets do not follow a labware instance when using :py:meth:`.move_labware`.
|
|
809
|
+
* - 2.23 and newer
|
|
810
|
+
-
|
|
811
|
+
On Flex, offsets can apply to all labware of the same type, regardless of their on-deck location.
|
|
812
|
+
|
|
813
|
+
.. note::
|
|
814
|
+
|
|
815
|
+
Setting offsets with this method will override any labware offsets set
|
|
816
|
+
by running Labware Position Check in the Opentrons App.
|
|
817
|
+
|
|
818
|
+
This method is designed for use with mechanisms like
|
|
819
|
+
:obj:`opentrons.execute.get_protocol_api`, which lack an interactive way
|
|
820
|
+
to adjust labware offsets. (See :ref:`advanced-control`.)
|
|
821
|
+
|
|
822
|
+
.. versionchanged:: 2.14
|
|
823
|
+
Temporarily removed.
|
|
824
|
+
|
|
825
|
+
.. versionchanged:: 2.18
|
|
826
|
+
Restored, and now applies to labware type–location pairs.
|
|
827
|
+
"""
|
|
828
|
+
if (
|
|
829
|
+
self._api_version >= ENGINE_CORE_API_VERSION
|
|
830
|
+
and self._api_version < SET_OFFSET_RESTORED_API_VERSION
|
|
831
|
+
):
|
|
832
|
+
raise APIVersionError(
|
|
833
|
+
api_element="Labware.set_offset()",
|
|
834
|
+
until_version=f"{SET_OFFSET_RESTORED_API_VERSION}",
|
|
835
|
+
current_version=f"{self._api_version}",
|
|
836
|
+
extra_message="This feature not available in versions 2.14 thorugh 2.17. You can also use the Opentrons App's Labware Position Check.",
|
|
837
|
+
)
|
|
838
|
+
else:
|
|
839
|
+
self._core.set_calibration(Point(x=x, y=y, z=z))
|
|
840
|
+
|
|
841
|
+
@property
|
|
842
|
+
@requires_version(2, 0)
|
|
843
|
+
def calibrated_offset(self) -> Point:
|
|
844
|
+
"""The front-left-bottom corner of the labware, including its labware offset.
|
|
845
|
+
|
|
846
|
+
When running a protocol in the Opentrons App or on the touchscreen, Labware
|
|
847
|
+
Position Check sets the labware offset.
|
|
848
|
+
"""
|
|
849
|
+
return self._core.get_calibrated_offset()
|
|
850
|
+
|
|
851
|
+
@requires_version(2, 0)
|
|
852
|
+
def well(self, idx: Union[int, str]) -> Well:
|
|
853
|
+
"""Deprecated. Use result of :py:meth:`wells` or :py:meth:`wells_by_name`."""
|
|
854
|
+
if isinstance(idx, int):
|
|
855
|
+
return self.wells()[idx]
|
|
856
|
+
elif isinstance(idx, str):
|
|
857
|
+
return self.wells_by_name()[idx]
|
|
858
|
+
else:
|
|
859
|
+
raise TypeError(
|
|
860
|
+
f"`Labware.well` must be called with an `int` or `str`, but got {idx}"
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
@requires_version(2, 0)
|
|
864
|
+
def wells(self, *args: Union[str, int]) -> List[Well]:
|
|
865
|
+
"""
|
|
866
|
+
Accessor function to navigate a labware top to bottom, left to right.
|
|
867
|
+
|
|
868
|
+
i.e., this method returns a list ordered A1, B1, C1…A2, B2, C2….
|
|
869
|
+
|
|
870
|
+
Use indexing to access individual wells contained in the list.
|
|
871
|
+
For example, access well A1 with ``labware.wells()[0]``.
|
|
872
|
+
|
|
873
|
+
.. note::
|
|
874
|
+
Using args with this method is deprecated. Use indexing instead.
|
|
875
|
+
|
|
876
|
+
If your code uses args, they can be either strings or integers, but not a
|
|
877
|
+
mix of the two. For example, ``.wells(1, 4)`` or ``.wells("1", "4")`` is
|
|
878
|
+
valid, but ``.wells("1", 4)`` is not.
|
|
879
|
+
|
|
880
|
+
:return: Ordered list of all wells in a labware.
|
|
881
|
+
"""
|
|
882
|
+
if not args:
|
|
883
|
+
return list(self._wells_by_name.values())
|
|
884
|
+
|
|
885
|
+
elif validation.is_all_integers(args):
|
|
886
|
+
wells = self.wells()
|
|
887
|
+
return [wells[idx] for idx in args]
|
|
888
|
+
|
|
889
|
+
elif validation.is_all_strings(args):
|
|
890
|
+
wells_by_name = self.wells_by_name()
|
|
891
|
+
return [wells_by_name[idx] for idx in args]
|
|
892
|
+
|
|
893
|
+
else:
|
|
894
|
+
raise TypeError(
|
|
895
|
+
"`Labware.wells` must be called with all `int`'s or all `str`'s,"
|
|
896
|
+
f" but was called with {args}"
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
@requires_version(2, 0)
|
|
900
|
+
def wells_by_name(self) -> Dict[str, Well]:
|
|
901
|
+
"""
|
|
902
|
+
Accessor function used to navigate through a labware by well name.
|
|
903
|
+
|
|
904
|
+
Use indexing to access individual wells contained in the dictionary.
|
|
905
|
+
For example, access well A1 with ``labware.wells_by_name()["A1"]``.
|
|
906
|
+
|
|
907
|
+
:return: Dictionary of :py:class:`.Well` objects keyed by well name.
|
|
908
|
+
"""
|
|
909
|
+
return dict(self._wells_by_name)
|
|
910
|
+
|
|
911
|
+
@requires_version(2, 0)
|
|
912
|
+
def wells_by_index(self) -> Dict[str, Well]:
|
|
913
|
+
"""
|
|
914
|
+
.. deprecated:: 2.0
|
|
915
|
+
Use :py:meth:`wells_by_name` or dict access instead.
|
|
916
|
+
"""
|
|
917
|
+
_log.warning(
|
|
918
|
+
"wells_by_index is deprecated. Use wells_by_name or dict access instead."
|
|
919
|
+
)
|
|
920
|
+
return self.wells_by_name()
|
|
921
|
+
|
|
922
|
+
@requires_version(2, 0)
|
|
923
|
+
def rows(self, *args: Union[int, str]) -> List[List[Well]]:
|
|
924
|
+
"""
|
|
925
|
+
Accessor function to navigate through a labware by row.
|
|
926
|
+
|
|
927
|
+
Use indexing to access individual rows or wells contained in the nested list.
|
|
928
|
+
On a standard 96-well plate, this will output a list of :py:class:`.Well`
|
|
929
|
+
objects containing A1 through A12.
|
|
930
|
+
|
|
931
|
+
.. note::
|
|
932
|
+
Using args with this method is deprecated. Use indexing instead.
|
|
933
|
+
|
|
934
|
+
If your code uses args, they can be either strings or integers, but not a
|
|
935
|
+
mix of the two. For example, ``.rows(1, 4)`` or ``.rows("1", "4")`` is
|
|
936
|
+
valid, but ``.rows("1", 4)`` is not.
|
|
937
|
+
|
|
938
|
+
:return: A list of row lists.
|
|
939
|
+
"""
|
|
940
|
+
if not args:
|
|
941
|
+
return [
|
|
942
|
+
[self._wells_by_name[well_name] for well_name in row]
|
|
943
|
+
for row in self._well_grid.rows_by_name.values()
|
|
944
|
+
]
|
|
945
|
+
|
|
946
|
+
elif validation.is_all_integers(args):
|
|
947
|
+
rows = self.rows()
|
|
948
|
+
return [rows[idx] for idx in args]
|
|
949
|
+
|
|
950
|
+
elif validation.is_all_strings(args):
|
|
951
|
+
rows_by_name = self.rows_by_name()
|
|
952
|
+
return [rows_by_name[idx] for idx in args]
|
|
953
|
+
|
|
954
|
+
else:
|
|
955
|
+
raise TypeError(
|
|
956
|
+
"`Labware.rows` must be called with all `int`'s or all `str`'s,"
|
|
957
|
+
f" but was called with {args}"
|
|
958
|
+
)
|
|
959
|
+
|
|
960
|
+
@requires_version(2, 0)
|
|
961
|
+
def rows_by_name(self) -> Dict[str, List[Well]]:
|
|
962
|
+
"""
|
|
963
|
+
Accessor function to navigate through a labware by row name.
|
|
964
|
+
|
|
965
|
+
Use indexing to access individual rows or wells contained in the dictionary.
|
|
966
|
+
For example, access row A with ``labware.rows_by_name()["A"]``.
|
|
967
|
+
On a standard 96-well plate, this will output a list of :py:class:`.Well`
|
|
968
|
+
objects containing A1 through A12.
|
|
969
|
+
|
|
970
|
+
:return: Dictionary of :py:class:`.Well` lists keyed by row name.
|
|
971
|
+
"""
|
|
972
|
+
return {
|
|
973
|
+
row_name: [self._wells_by_name[well_name] for well_name in row]
|
|
974
|
+
for row_name, row in self._well_grid.rows_by_name.items()
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
@requires_version(2, 0)
|
|
978
|
+
def rows_by_index(self) -> Dict[str, List[Well]]:
|
|
979
|
+
"""
|
|
980
|
+
.. deprecated:: 2.0
|
|
981
|
+
Use :py:meth:`rows_by_name` instead.
|
|
982
|
+
"""
|
|
983
|
+
_log.warning("rows_by_index is deprecated. Use rows_by_name instead.")
|
|
984
|
+
return self.rows_by_name()
|
|
985
|
+
|
|
986
|
+
@requires_version(2, 0)
|
|
987
|
+
def columns(self, *args: Union[int, str]) -> List[List[Well]]:
|
|
988
|
+
"""
|
|
989
|
+
Accessor function to navigate through a labware by column.
|
|
990
|
+
|
|
991
|
+
Use indexing to access individual columns or wells contained in the nested list.
|
|
992
|
+
For example, access column 1 with ``labware.columns()[0]``.
|
|
993
|
+
On a standard 96-well plate, this will output a list of :py:class:`.Well`
|
|
994
|
+
objects containing A1 through H1.
|
|
995
|
+
|
|
996
|
+
.. note::
|
|
997
|
+
Using args with this method is deprecated. Use indexing instead.
|
|
998
|
+
|
|
999
|
+
If your code uses args, they can be either strings or integers, but not a
|
|
1000
|
+
mix of the two. For example, ``.columns(1, 4)`` or ``.columns("1", "4")`` is
|
|
1001
|
+
valid, but ``.columns("1", 4)`` is not.
|
|
1002
|
+
|
|
1003
|
+
:return: A list of column lists.
|
|
1004
|
+
"""
|
|
1005
|
+
if not args:
|
|
1006
|
+
return [
|
|
1007
|
+
[self._wells_by_name[well_name] for well_name in column]
|
|
1008
|
+
for column in self._well_grid.columns_by_name.values()
|
|
1009
|
+
]
|
|
1010
|
+
|
|
1011
|
+
elif validation.is_all_integers(args):
|
|
1012
|
+
columns = self.columns()
|
|
1013
|
+
return [columns[idx] for idx in args]
|
|
1014
|
+
|
|
1015
|
+
elif validation.is_all_strings(args):
|
|
1016
|
+
columns_by_name = self.columns_by_name()
|
|
1017
|
+
return [columns_by_name[idx] for idx in args]
|
|
1018
|
+
|
|
1019
|
+
else:
|
|
1020
|
+
raise TypeError(
|
|
1021
|
+
"`Labware.columns` must be called with all `int`'s or all `str`'s,"
|
|
1022
|
+
f" but was called with {args}"
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
@requires_version(2, 0)
|
|
1026
|
+
def columns_by_name(self) -> Dict[str, List[Well]]:
|
|
1027
|
+
"""
|
|
1028
|
+
Accessor function to navigate through a labware by column name.
|
|
1029
|
+
|
|
1030
|
+
Use indexing to access individual columns or wells contained in the dictionary.
|
|
1031
|
+
For example, access column 1 with ``labware.columns_by_name()["1"]``.
|
|
1032
|
+
On a standard 96-well plate, this will output a list of :py:class:`.Well`
|
|
1033
|
+
objects containing A1 through H1.
|
|
1034
|
+
|
|
1035
|
+
:return: Dictionary of :py:class:`.Well` lists keyed by column name.
|
|
1036
|
+
"""
|
|
1037
|
+
return {
|
|
1038
|
+
column_name: [self._wells_by_name[well_name] for well_name in column]
|
|
1039
|
+
for column_name, column in self._well_grid.columns_by_name.items()
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
@requires_version(2, 0)
|
|
1043
|
+
def columns_by_index(self) -> Dict[str, List[Well]]:
|
|
1044
|
+
"""
|
|
1045
|
+
.. deprecated:: 2.0
|
|
1046
|
+
Use :py:meth:`columns_by_name` instead.
|
|
1047
|
+
"""
|
|
1048
|
+
_log.warning("columns_by_index is deprecated. Use columns_by_name instead.")
|
|
1049
|
+
return self.columns_by_name()
|
|
1050
|
+
|
|
1051
|
+
@property
|
|
1052
|
+
@requires_version(2, 0)
|
|
1053
|
+
def highest_z(self) -> float:
|
|
1054
|
+
"""
|
|
1055
|
+
The z-coordinate of the highest single point anywhere on the labware.
|
|
1056
|
+
|
|
1057
|
+
This is taken from the ``zDimension`` property of the ``dimensions`` object in the
|
|
1058
|
+
labware definition and takes into account the labware offset.
|
|
1059
|
+
"""
|
|
1060
|
+
return self._core.highest_z
|
|
1061
|
+
|
|
1062
|
+
@property
|
|
1063
|
+
def _is_tiprack(self) -> bool:
|
|
1064
|
+
"""as is_tiprack but not subject to version checking for speed"""
|
|
1065
|
+
return self._core.is_tip_rack()
|
|
1066
|
+
|
|
1067
|
+
@property
|
|
1068
|
+
@requires_version(2, 0)
|
|
1069
|
+
def is_tiprack(self) -> bool:
|
|
1070
|
+
"""Whether the labware behaves as a tip rack.
|
|
1071
|
+
|
|
1072
|
+
Returns ``True`` if the labware definition specifies ``isTiprack`` as ``True``.
|
|
1073
|
+
"""
|
|
1074
|
+
return self._is_tiprack
|
|
1075
|
+
|
|
1076
|
+
@property
|
|
1077
|
+
@requires_version(2, 15)
|
|
1078
|
+
def is_adapter(self) -> bool:
|
|
1079
|
+
"""Whether the labware behaves as an adapter.
|
|
1080
|
+
|
|
1081
|
+
Returns ``True`` if the labware definition specifies ``adapter`` as one of the
|
|
1082
|
+
labware's ``allowedRoles``.
|
|
1083
|
+
"""
|
|
1084
|
+
return self._core.is_adapter()
|
|
1085
|
+
|
|
1086
|
+
@property
|
|
1087
|
+
@requires_version(2, 0)
|
|
1088
|
+
def tip_length(self) -> float:
|
|
1089
|
+
"""For a tip rack labware, the length of the tips it holds, in mm.
|
|
1090
|
+
|
|
1091
|
+
This is taken from the ``tipLength`` property of the ``parameters`` object in the labware definition.
|
|
1092
|
+
|
|
1093
|
+
This method will raise an exception if you call it on a labware that isn’t a tip rack.
|
|
1094
|
+
"""
|
|
1095
|
+
return self._core.get_tip_length()
|
|
1096
|
+
|
|
1097
|
+
@tip_length.setter
|
|
1098
|
+
def tip_length(self, length: float) -> None:
|
|
1099
|
+
"""
|
|
1100
|
+
Set the tip rack's tip length.
|
|
1101
|
+
|
|
1102
|
+
.. deprecated: 2.14
|
|
1103
|
+
Ensure tip length is set properly in your tip rack's definition
|
|
1104
|
+
and/or use the Opentrons App's tip length calibration feature.
|
|
1105
|
+
"""
|
|
1106
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
1107
|
+
raise UnsupportedAPIError(
|
|
1108
|
+
api_element="Labware.tip_length setter",
|
|
1109
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
1110
|
+
current_version=f"{self._api_version}",
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
# TODO(mc, 2023-02-06): this assert should be enough for mypy
|
|
1114
|
+
# investigate if upgrading mypy allows the `cast` to be removed
|
|
1115
|
+
assert isinstance(self._core, LegacyLabwareCore)
|
|
1116
|
+
cast(LegacyLabwareCore, self._core).set_tip_length(length)
|
|
1117
|
+
|
|
1118
|
+
# TODO(mc, 2022-11-09): implementation detail; deprecate public method
|
|
1119
|
+
def next_tip(
|
|
1120
|
+
self,
|
|
1121
|
+
num_tips: int = 1,
|
|
1122
|
+
starting_tip: Optional[Well] = None,
|
|
1123
|
+
*,
|
|
1124
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
1125
|
+
) -> Optional[Well]:
|
|
1126
|
+
"""
|
|
1127
|
+
Find the next valid well for pick-up.
|
|
1128
|
+
|
|
1129
|
+
Determines the next valid start tip from which to retrieve the
|
|
1130
|
+
specified number of tips. There must be at least `num_tips` sequential
|
|
1131
|
+
wells for which all wells have tips, in the same column.
|
|
1132
|
+
|
|
1133
|
+
:param num_tips: target number of sequential tips in the same column
|
|
1134
|
+
:type num_tips: int
|
|
1135
|
+
:param starting_tip: The :py:class:`.Well` from which to start search.
|
|
1136
|
+
for an available tip.
|
|
1137
|
+
:type starting_tip: :py:class:`.Well`
|
|
1138
|
+
:return: the :py:class:`.Well` meeting the target criteria, or None
|
|
1139
|
+
"""
|
|
1140
|
+
assert num_tips > 0, f"num_tips must be positive integer, but got {num_tips}"
|
|
1141
|
+
|
|
1142
|
+
well_name = self._core.get_next_tip(
|
|
1143
|
+
num_tips=num_tips,
|
|
1144
|
+
starting_tip=starting_tip._core if starting_tip else None,
|
|
1145
|
+
nozzle_map=nozzle_map,
|
|
1146
|
+
)
|
|
1147
|
+
|
|
1148
|
+
return self._wells_by_name[well_name] if well_name is not None else None
|
|
1149
|
+
|
|
1150
|
+
def use_tips(self, start_well: Well, num_channels: int = 1) -> None:
|
|
1151
|
+
"""
|
|
1152
|
+
Removes tips from the tip tracker.
|
|
1153
|
+
|
|
1154
|
+
This method should be called when a tip is picked up. Generally, it
|
|
1155
|
+
will be called with `num_channels=1` or `num_channels=8` for single-
|
|
1156
|
+
and multi-channel respectively. If picking up with more than one
|
|
1157
|
+
channel, this method will automatically determine which tips are used
|
|
1158
|
+
based on the start well, the number of channels, and the geometry of
|
|
1159
|
+
the tiprack.
|
|
1160
|
+
|
|
1161
|
+
:param start_well: The :py:class:`.Well` from which to pick up a tip.
|
|
1162
|
+
For a single-channel pipette, this is the well to
|
|
1163
|
+
send the pipette to. For a multi-channel pipette,
|
|
1164
|
+
this is the well to send the back-most nozzle of the
|
|
1165
|
+
pipette to.
|
|
1166
|
+
:type start_well: :py:class:`.Well`
|
|
1167
|
+
:param num_channels: The number of channels for the current pipette
|
|
1168
|
+
:type num_channels: int
|
|
1169
|
+
|
|
1170
|
+
.. deprecated:: 2.14
|
|
1171
|
+
Modification of tip tracking state outside :py:meth:`.reset` has been deprecated.
|
|
1172
|
+
"""
|
|
1173
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
1174
|
+
raise UnsupportedAPIError(
|
|
1175
|
+
api_element="Labware.use_tips",
|
|
1176
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
1177
|
+
current_version=f"{self._api_version}",
|
|
1178
|
+
extra_message="To modify tip state, use Labware.reset.",
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
assert num_channels > 0, "Bad call to use_tips: num_channels<=0"
|
|
1182
|
+
fail_if_full = self._api_version < APIVersion(2, 2)
|
|
1183
|
+
|
|
1184
|
+
# TODO(mc, 2023-02-13): this assert should be enough for mypy
|
|
1185
|
+
# investigate if upgrading mypy allows the `cast` to be removed
|
|
1186
|
+
assert isinstance(self._core, LegacyLabwareCore)
|
|
1187
|
+
cast(LegacyLabwareCore, self._core).get_tip_tracker().use_tips(
|
|
1188
|
+
start_well=start_well._core,
|
|
1189
|
+
num_channels=num_channels,
|
|
1190
|
+
fail_if_full=fail_if_full,
|
|
1191
|
+
)
|
|
1192
|
+
|
|
1193
|
+
def __repr__(self) -> str:
|
|
1194
|
+
return self._core.get_display_name()
|
|
1195
|
+
|
|
1196
|
+
def __eq__(self, other: object) -> bool:
|
|
1197
|
+
if not isinstance(other, Labware):
|
|
1198
|
+
return NotImplemented
|
|
1199
|
+
return self._core == other._core
|
|
1200
|
+
|
|
1201
|
+
def __hash__(self) -> int:
|
|
1202
|
+
return hash((self._core, self._api_version))
|
|
1203
|
+
|
|
1204
|
+
def previous_tip(self, num_tips: int = 1) -> Optional[Well]:
|
|
1205
|
+
"""
|
|
1206
|
+
Find the best well to drop a tip in.
|
|
1207
|
+
|
|
1208
|
+
This is the well from which the last tip was picked up, if there's
|
|
1209
|
+
room. It can be used to return tips to the tip tracker.
|
|
1210
|
+
|
|
1211
|
+
:param num_tips: target number of tips to return, sequential in a
|
|
1212
|
+
column
|
|
1213
|
+
:type num_tips: int
|
|
1214
|
+
:return: The :py:class:`.Well` meeting the target criteria, or ``None``
|
|
1215
|
+
|
|
1216
|
+
.. versionchanged:: 2.14
|
|
1217
|
+
This method has been removed.
|
|
1218
|
+
"""
|
|
1219
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
1220
|
+
raise UnsupportedAPIError(
|
|
1221
|
+
api_element="Labware.previous_tip",
|
|
1222
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
1223
|
+
current_version=f"{self._api_version}",
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
# This logic is the inverse of :py:meth:`next_tip`
|
|
1227
|
+
assert num_tips > 0, "Bad call to previous_tip: num_tips <= 0"
|
|
1228
|
+
# TODO(mc, 2023-02-13): this assert should be enough for mypy
|
|
1229
|
+
# investigate if upgrading mypy allows the `cast` to be removed
|
|
1230
|
+
assert isinstance(self._core, LegacyLabwareCore)
|
|
1231
|
+
well_core = (
|
|
1232
|
+
cast(LegacyLabwareCore, self._core)
|
|
1233
|
+
.get_tip_tracker()
|
|
1234
|
+
.previous_tip(num_tips=num_tips)
|
|
1235
|
+
)
|
|
1236
|
+
return self._wells_by_name[well_core.get_name()] if well_core else None
|
|
1237
|
+
|
|
1238
|
+
# TODO(mc, 2022-11-09): implementation detail; deprecate public method
|
|
1239
|
+
def return_tips(self, start_well: Well, num_channels: int = 1) -> None:
|
|
1240
|
+
"""
|
|
1241
|
+
Re-adds tips to the tip tracker
|
|
1242
|
+
|
|
1243
|
+
This method should be called when a tip is dropped in a tiprack. It
|
|
1244
|
+
should be called with ``num_channels=1`` or ``num_channels=8`` for
|
|
1245
|
+
single- and multi-channel respectively. If returning more than one
|
|
1246
|
+
channel, this method will automatically determine which tips are
|
|
1247
|
+
returned based on the start well, the number of channels,
|
|
1248
|
+
and the tiprack geometry.
|
|
1249
|
+
|
|
1250
|
+
Note that unlike :py:meth:`use_tips`, calling this method in a way
|
|
1251
|
+
that would drop tips into wells with tips in them will raise an
|
|
1252
|
+
exception; this should only be called on a valid return of
|
|
1253
|
+
:py:meth:`previous_tip`.
|
|
1254
|
+
|
|
1255
|
+
:param start_well: The :py:class:`.Well` into which to return a tip.
|
|
1256
|
+
:type start_well: :py:class:`.Well`
|
|
1257
|
+
:param num_channels: The number of channels for the current pipette
|
|
1258
|
+
:type num_channels: int
|
|
1259
|
+
|
|
1260
|
+
.. versionchanged:: 2.14
|
|
1261
|
+
This method has been removed. Use :py:meth:`.reset` instead.
|
|
1262
|
+
"""
|
|
1263
|
+
if self._api_version >= ENGINE_CORE_API_VERSION:
|
|
1264
|
+
raise UnsupportedAPIError(
|
|
1265
|
+
api_element="Labware.return_tips()",
|
|
1266
|
+
since_version=f"{ENGINE_CORE_API_VERSION}",
|
|
1267
|
+
current_version=f"{self._api_version}",
|
|
1268
|
+
extra_message="Use Labware.reset() instead.",
|
|
1269
|
+
)
|
|
1270
|
+
|
|
1271
|
+
# This logic is the inverse of :py:meth:`use_tips`
|
|
1272
|
+
assert num_channels > 0, "Bad call to return_tips: num_channels <= 0"
|
|
1273
|
+
|
|
1274
|
+
# TODO(mc, 2023-02-13): this assert should be enough for mypy
|
|
1275
|
+
# investigate if upgrading mypy allows the `cast` to be removed
|
|
1276
|
+
assert isinstance(self._core, LegacyLabwareCore)
|
|
1277
|
+
cast(LegacyLabwareCore, self._core).get_tip_tracker().return_tips(
|
|
1278
|
+
start_well=start_well._core, num_channels=num_channels
|
|
1279
|
+
)
|
|
1280
|
+
|
|
1281
|
+
@requires_version(2, 0)
|
|
1282
|
+
def reset(self) -> None:
|
|
1283
|
+
"""Reset tip tracking for a tip rack.
|
|
1284
|
+
|
|
1285
|
+
After resetting, the API treats all wells on the rack as if they contain unused tips.
|
|
1286
|
+
This is useful if you want to reuse tips after calling :py:meth:`.return_tip()`.
|
|
1287
|
+
|
|
1288
|
+
If you need to physically replace an empty tip rack in the middle of your protocol,
|
|
1289
|
+
use :py:meth:`.move_labware()` instead. See :ref:`off-deck-location` for an example.
|
|
1290
|
+
|
|
1291
|
+
.. versionchanged:: 2.14
|
|
1292
|
+
This method will raise an exception if you call it on a labware that isn't
|
|
1293
|
+
a tip rack. Formerly, it would do nothing.
|
|
1294
|
+
"""
|
|
1295
|
+
self._core.reset_tips()
|
|
1296
|
+
|
|
1297
|
+
@requires_version(2, 22)
|
|
1298
|
+
def load_liquid(
|
|
1299
|
+
self, wells: Sequence[Union[str, Well]], volume: float, liquid: Liquid
|
|
1300
|
+
) -> None:
|
|
1301
|
+
"""Mark several wells as containing the same amount of liquid.
|
|
1302
|
+
|
|
1303
|
+
This method should be called at the beginning of a protocol, soon after loading labware and before
|
|
1304
|
+
liquid handling operations begin. Loading liquids is required for liquid tracking functionality. If a well
|
|
1305
|
+
hasn't been assigned a starting volume with :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
|
|
1306
|
+
:py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
|
|
1307
|
+
|
|
1308
|
+
:param wells: The wells to load the liquid into.
|
|
1309
|
+
:type wells: List of string well names or list of :py:class:`.Well` objects (e.g., from :py:meth:`~Labware.wells`).
|
|
1310
|
+
|
|
1311
|
+
:param volume: The volume of liquid to load into each well.
|
|
1312
|
+
:type volume: float
|
|
1313
|
+
|
|
1314
|
+
:param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
|
|
1315
|
+
:type liquid: Liquid
|
|
1316
|
+
"""
|
|
1317
|
+
well_names: List[str] = []
|
|
1318
|
+
for well in wells:
|
|
1319
|
+
if isinstance(well, str):
|
|
1320
|
+
if well not in self.wells_by_name():
|
|
1321
|
+
raise KeyError(
|
|
1322
|
+
f"{well} is not a well in labware {self.name}. The elements of wells should name wells in this labware."
|
|
1323
|
+
)
|
|
1324
|
+
well_names.append(well)
|
|
1325
|
+
elif isinstance(well, Well):
|
|
1326
|
+
if well.parent is not self:
|
|
1327
|
+
raise KeyError(
|
|
1328
|
+
f"{well.well_name} is not a well in labware {self.name}. The elements of wells should be wells of this labware."
|
|
1329
|
+
)
|
|
1330
|
+
well_names.append(well.well_name)
|
|
1331
|
+
else:
|
|
1332
|
+
raise TypeError(
|
|
1333
|
+
f"Unexpected type for element {repr(well)}. The elements of wells should be Well instances or well names."
|
|
1334
|
+
)
|
|
1335
|
+
if not isinstance(volume, (float, int)):
|
|
1336
|
+
raise TypeError(
|
|
1337
|
+
f"Unexpected type for volume {repr(volume)}. Volume should be a number in microliters."
|
|
1338
|
+
)
|
|
1339
|
+
self._core.load_liquid({well_name: volume for well_name in well_names}, liquid)
|
|
1340
|
+
|
|
1341
|
+
@requires_version(2, 22)
|
|
1342
|
+
def load_liquid_by_well(
|
|
1343
|
+
self, volumes: Mapping[Union[str, Well], float], liquid: Liquid
|
|
1344
|
+
) -> None:
|
|
1345
|
+
"""Mark several wells as containing unique volumes of liquid.
|
|
1346
|
+
|
|
1347
|
+
This method should be called at the beginning of a protocol, soon after loading labware and before
|
|
1348
|
+
liquid handling begins. Loading liquids is required for liquid tracking functionality. If a well hasn't been assigned a starting volume with :py:meth:`~Labware.load_empty`, :py:meth:`~Labware.load_liquid`, or
|
|
1349
|
+
:py:meth:`~Labware.load_liquid_by_well`, the volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
|
|
1350
|
+
|
|
1351
|
+
:param volumes: A dictionary of well names (or :py:class:`Well` objects, for instance from ``labware['A1']``)
|
|
1352
|
+
:type wells: Dict[Union[str, Well], float]
|
|
1353
|
+
|
|
1354
|
+
:param liquid: The liquid to load into each well, previously defined by :py:meth:`~ProtocolContext.define_liquid`
|
|
1355
|
+
:type liquid: Liquid
|
|
1356
|
+
"""
|
|
1357
|
+
verified_volumes: Dict[str, float] = {}
|
|
1358
|
+
for well, volume in volumes.items():
|
|
1359
|
+
if isinstance(well, str):
|
|
1360
|
+
if well not in self.wells_by_name():
|
|
1361
|
+
raise KeyError(
|
|
1362
|
+
f"{well} is not a well in {self.name}. The keys of volumes should name wells in this labware"
|
|
1363
|
+
)
|
|
1364
|
+
verified_volumes[well] = volume
|
|
1365
|
+
elif isinstance(well, Well):
|
|
1366
|
+
if well.parent is not self:
|
|
1367
|
+
raise KeyError(
|
|
1368
|
+
f"{well.well_name} is not a well in {self.name}. The keys of volumes should be wells of this labware"
|
|
1369
|
+
)
|
|
1370
|
+
verified_volumes[well.well_name] = volume
|
|
1371
|
+
else:
|
|
1372
|
+
raise TypeError(
|
|
1373
|
+
f"Unexpected type for well name {repr(well)}. The keys of volumes should be Well instances or well names."
|
|
1374
|
+
)
|
|
1375
|
+
if not isinstance(volume, (float, int)):
|
|
1376
|
+
raise TypeError(
|
|
1377
|
+
f"Unexpected type for volume {repr(volume)}. The values of volumes should be numbers in microliters."
|
|
1378
|
+
)
|
|
1379
|
+
self._core.load_liquid(verified_volumes, liquid)
|
|
1380
|
+
|
|
1381
|
+
@requires_version(2, 22)
|
|
1382
|
+
def load_empty(self, wells: Sequence[Union[Well, str]]) -> None:
|
|
1383
|
+
"""Mark several wells as empty.
|
|
1384
|
+
|
|
1385
|
+
This method should be called at the beginning of a protocol, after loading the labware and before liquid handling
|
|
1386
|
+
begins. Loading liquids is required for liquid tracking functionality. If a well in a labware hasn't been assigned a starting volume with :py:meth:`Labware.load_empty`, :py:meth:`Labware.load_liquid`, or :py:meth:`Labware.load_liquid_by_well`, the
|
|
1387
|
+
volume it contains is unknown and the well's liquid will not be tracked throughout the protocol.
|
|
1388
|
+
|
|
1389
|
+
:param wells: The list of wells to mark empty. To mark all wells as empty, pass ``labware.wells()``. You can also specify
|
|
1390
|
+
wells by their names (for instance, ``labware.load_empty(['A1', 'A2'])``).
|
|
1391
|
+
:type wells: Union[List[Well], List[str]]
|
|
1392
|
+
"""
|
|
1393
|
+
well_names: List[str] = []
|
|
1394
|
+
for well in wells:
|
|
1395
|
+
if isinstance(well, str):
|
|
1396
|
+
if well not in self.wells_by_name():
|
|
1397
|
+
raise KeyError(
|
|
1398
|
+
f"{well} is not a well in {self.name}. The elements of wells should name wells in this labware."
|
|
1399
|
+
)
|
|
1400
|
+
well_names.append(well)
|
|
1401
|
+
elif isinstance(well, Well):
|
|
1402
|
+
if well.parent is not self:
|
|
1403
|
+
raise KeyError(
|
|
1404
|
+
f"{well.well_name} is not a well in {self.name}. The elements of wells should be wells of this labware."
|
|
1405
|
+
)
|
|
1406
|
+
well_names.append(well.well_name)
|
|
1407
|
+
else:
|
|
1408
|
+
raise TypeError(
|
|
1409
|
+
f"Unexpected type for well name {repr(well)}. The elements of wells should be Well instances or well names."
|
|
1410
|
+
)
|
|
1411
|
+
self._core.load_empty(well_names)
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
# TODO(mc, 2022-11-09): implementation detail, move to core
|
|
1415
|
+
def split_tipracks(tip_racks: List[Labware]) -> Tuple[Labware, List[Labware]]:
|
|
1416
|
+
try:
|
|
1417
|
+
rest = tip_racks[1:]
|
|
1418
|
+
except IndexError:
|
|
1419
|
+
rest = []
|
|
1420
|
+
return tip_racks[0], rest
|
|
1421
|
+
|
|
1422
|
+
|
|
1423
|
+
# TODO(mc, 2022-11-09): implementation detail, move to core
|
|
1424
|
+
def select_tiprack_from_list(
|
|
1425
|
+
tip_racks: List[Labware],
|
|
1426
|
+
num_channels: int,
|
|
1427
|
+
starting_point: Optional[Well] = None,
|
|
1428
|
+
*,
|
|
1429
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
1430
|
+
) -> Tuple[Labware, Well]:
|
|
1431
|
+
try:
|
|
1432
|
+
first, rest = split_tipracks(tip_racks)
|
|
1433
|
+
except IndexError:
|
|
1434
|
+
raise OutOfTipsError
|
|
1435
|
+
|
|
1436
|
+
if starting_point and starting_point.parent != first:
|
|
1437
|
+
raise TipSelectionError(
|
|
1438
|
+
f"The starting tip you selected does not exist in {first}"
|
|
1439
|
+
)
|
|
1440
|
+
elif starting_point:
|
|
1441
|
+
first_well = starting_point
|
|
1442
|
+
elif nozzle_map:
|
|
1443
|
+
first_well = None
|
|
1444
|
+
else:
|
|
1445
|
+
first_well = first.wells()[0]
|
|
1446
|
+
|
|
1447
|
+
next_tip = first.next_tip(num_channels, first_well, nozzle_map=nozzle_map)
|
|
1448
|
+
if next_tip:
|
|
1449
|
+
return first, next_tip
|
|
1450
|
+
else:
|
|
1451
|
+
return select_tiprack_from_list(rest, num_channels, None, nozzle_map=nozzle_map)
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
# TODO(mc, 2022-11-09): implementation detail, move to core
|
|
1455
|
+
def filter_tipracks_to_start(
|
|
1456
|
+
starting_point: Well, tipracks: List[Labware]
|
|
1457
|
+
) -> List[Labware]:
|
|
1458
|
+
return list(dropwhile(lambda tr: starting_point.parent != tr, tipracks))
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
# TODO(mc, 2022-11-09): implementation detail, move to core
|
|
1462
|
+
def next_available_tip(
|
|
1463
|
+
starting_tip: Optional[Well],
|
|
1464
|
+
tip_racks: List[Labware],
|
|
1465
|
+
channels: int,
|
|
1466
|
+
*,
|
|
1467
|
+
nozzle_map: Optional[NozzleMapInterface] = None,
|
|
1468
|
+
) -> Tuple[Labware, Well]:
|
|
1469
|
+
start = starting_tip
|
|
1470
|
+
if start is None:
|
|
1471
|
+
return select_tiprack_from_list(
|
|
1472
|
+
tip_racks, channels, None, nozzle_map=nozzle_map
|
|
1473
|
+
)
|
|
1474
|
+
else:
|
|
1475
|
+
return select_tiprack_from_list(
|
|
1476
|
+
filter_tipracks_to_start(start, tip_racks),
|
|
1477
|
+
channels,
|
|
1478
|
+
start,
|
|
1479
|
+
nozzle_map=nozzle_map,
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
# TODO(mc, 2022-11-09): implementation detail, move somewhere else
|
|
1484
|
+
# only used in old calibration flows by robot-server
|
|
1485
|
+
def load_from_definition(
|
|
1486
|
+
definition: "LabwareDefinition2",
|
|
1487
|
+
parent: Location,
|
|
1488
|
+
label: Optional[str] = None,
|
|
1489
|
+
api_level: Optional[APIVersion] = None,
|
|
1490
|
+
) -> Labware:
|
|
1491
|
+
"""
|
|
1492
|
+
Return a labware object constructed from a provided labware definition dict
|
|
1493
|
+
|
|
1494
|
+
:param definition: A dict representing all required data for a labware,
|
|
1495
|
+
including metadata such as the display name of the labware, a
|
|
1496
|
+
definition of the order to iterate over wells, the shape of wells
|
|
1497
|
+
(shape, physical dimensions, etc), and so on. The correct shape of
|
|
1498
|
+
this definition is governed by the "labware-designer" project in
|
|
1499
|
+
the Opentrons/opentrons repo.
|
|
1500
|
+
:param parent: A :py:class:`.Location` representing the location where
|
|
1501
|
+
the front and left most point of the outside of labware is
|
|
1502
|
+
(often the front-left corner of a slot on the deck).
|
|
1503
|
+
:param str label: An optional label that will override the labware's
|
|
1504
|
+
display name from its definition
|
|
1505
|
+
:param api_level: the API version to set for the loaded labware
|
|
1506
|
+
instance. The :py:class:`.Labware` will
|
|
1507
|
+
conform to this level. If not specified,
|
|
1508
|
+
defaults to ``APIVersion(2, 13)``.
|
|
1509
|
+
"""
|
|
1510
|
+
return Labware(
|
|
1511
|
+
core=LegacyLabwareCore(
|
|
1512
|
+
definition=definition,
|
|
1513
|
+
parent=parent,
|
|
1514
|
+
label=label,
|
|
1515
|
+
),
|
|
1516
|
+
api_version=api_level or APIVersion(2, 13),
|
|
1517
|
+
protocol_core=None, # type: ignore[arg-type]
|
|
1518
|
+
core_map=None, # type: ignore[arg-type]
|
|
1519
|
+
)
|
|
1520
|
+
|
|
1521
|
+
|
|
1522
|
+
# TODO(mc, 2022-11-09): implementation detail, move somewhere else
|
|
1523
|
+
# only used in old calibration flows by robot-server
|
|
1524
|
+
def load(
|
|
1525
|
+
load_name: str,
|
|
1526
|
+
parent: Location,
|
|
1527
|
+
label: Optional[str] = None,
|
|
1528
|
+
namespace: Optional[str] = None,
|
|
1529
|
+
version: int = 1,
|
|
1530
|
+
bundled_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
|
|
1531
|
+
extra_defs: Optional[Mapping[str, LabwareDefinition2]] = None,
|
|
1532
|
+
api_level: Optional[APIVersion] = None,
|
|
1533
|
+
) -> Labware:
|
|
1534
|
+
"""
|
|
1535
|
+
Return a labware object constructed from a labware definition dict looked
|
|
1536
|
+
up by name (definition must have been previously stored locally on the
|
|
1537
|
+
robot)
|
|
1538
|
+
|
|
1539
|
+
:param load_name: A string to use for looking up a labware definition
|
|
1540
|
+
previously saved to disc. The definition file must have been saved in a
|
|
1541
|
+
known location
|
|
1542
|
+
:param parent: A :py:class:`.Location` representing the location where
|
|
1543
|
+
the front and left most point of the outside of labware is
|
|
1544
|
+
(often the front-left corner of a slot on the deck).
|
|
1545
|
+
:param str label: An optional label that will override the labware's
|
|
1546
|
+
display name from its definition
|
|
1547
|
+
:param str namespace: The namespace the labware definition belongs to.
|
|
1548
|
+
If unspecified, will search 'opentrons' then 'custom_beta'
|
|
1549
|
+
:param int version: The version of the labware definition. If unspecified,
|
|
1550
|
+
will use version 1.
|
|
1551
|
+
:param bundled_defs: If specified, a mapping of labware names to labware
|
|
1552
|
+
definitions. Only the bundle will be searched for definitions.
|
|
1553
|
+
:param extra_defs: If specified, a mapping of labware names to labware
|
|
1554
|
+
definitions. If no bundle is passed, these definitions will also be
|
|
1555
|
+
searched.
|
|
1556
|
+
:param api_level: the API version to set for the loaded labware
|
|
1557
|
+
instance. The :py:class:`.Labware` will
|
|
1558
|
+
conform to this level. If not specified,
|
|
1559
|
+
defaults to ``APIVersion(2, 13)``.
|
|
1560
|
+
"""
|
|
1561
|
+
definition = get_labware_definition(
|
|
1562
|
+
load_name,
|
|
1563
|
+
namespace,
|
|
1564
|
+
version,
|
|
1565
|
+
bundled_defs=bundled_defs,
|
|
1566
|
+
extra_defs=extra_defs,
|
|
1567
|
+
)
|
|
1568
|
+
|
|
1569
|
+
# The legacy `load_from_definition()` function that we're calling only supports
|
|
1570
|
+
# schemaVersion==2 labware. Fortunately, when robot-server calls this function,
|
|
1571
|
+
# we only expect it to try to load schemaVersion==2 labware, so we never expect
|
|
1572
|
+
# this ValueError to be raised in practice.
|
|
1573
|
+
if definition["schemaVersion"] != 2:
|
|
1574
|
+
raise ValueError(
|
|
1575
|
+
f"{namespace}/{load_name}/{version} has schema {definition['schemaVersion']}."
|
|
1576
|
+
" Only schema 2 is supported."
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
return load_from_definition(definition, parent, label, api_level)
|