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,1153 @@
|
|
|
1
|
+
"""ProtocolEngine-based Protocol API core implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from opentrons_shared_data.liquid_classes import LiquidClassDefinitionDoesNotExist
|
|
7
|
+
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
8
|
+
from opentrons_shared_data.labware.labware_definition import (
|
|
9
|
+
labware_definition_type_adapter,
|
|
10
|
+
)
|
|
11
|
+
from opentrons_shared_data.labware.types import LabwareDefinition as LabwareDefDict
|
|
12
|
+
from opentrons_shared_data import liquid_classes
|
|
13
|
+
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
14
|
+
LiquidClassSchemaV1,
|
|
15
|
+
)
|
|
16
|
+
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
17
|
+
from opentrons_shared_data.robot.types import RobotType
|
|
18
|
+
|
|
19
|
+
from opentrons.types import (
|
|
20
|
+
DeckSlotName,
|
|
21
|
+
Location,
|
|
22
|
+
Mount,
|
|
23
|
+
MountType,
|
|
24
|
+
Point,
|
|
25
|
+
StagingSlotName,
|
|
26
|
+
)
|
|
27
|
+
from opentrons.hardware_control import SyncHardwareAPI, SynchronousAdapter
|
|
28
|
+
from opentrons.hardware_control.modules import AbstractModule
|
|
29
|
+
from opentrons.hardware_control.modules.types import ModuleModel, ModuleType
|
|
30
|
+
from opentrons.hardware_control.types import DoorState
|
|
31
|
+
from opentrons.protocols.api_support.util import AxisMaxSpeeds
|
|
32
|
+
from opentrons.protocols.api_support.types import APIVersion
|
|
33
|
+
|
|
34
|
+
from opentrons.protocol_engine import commands as cmd
|
|
35
|
+
from opentrons.protocol_engine.commands import LoadModuleResult
|
|
36
|
+
from opentrons.protocol_engine import (
|
|
37
|
+
DeckSlotLocation,
|
|
38
|
+
AddressableAreaLocation,
|
|
39
|
+
ModuleLocation,
|
|
40
|
+
OnLabwareLocation,
|
|
41
|
+
ModuleModel as EngineModuleModel,
|
|
42
|
+
LabwareMovementStrategy,
|
|
43
|
+
LabwareOffsetVector,
|
|
44
|
+
LoadedLabware,
|
|
45
|
+
LoadedModule,
|
|
46
|
+
)
|
|
47
|
+
from opentrons.protocol_engine.types import (
|
|
48
|
+
ModuleModel as ProtocolEngineModuleModel,
|
|
49
|
+
OFF_DECK_LOCATION,
|
|
50
|
+
SYSTEM_LOCATION,
|
|
51
|
+
LoadableLabwareLocation,
|
|
52
|
+
NonStackedLocation,
|
|
53
|
+
)
|
|
54
|
+
from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
|
|
55
|
+
from opentrons.protocol_engine.errors import (
|
|
56
|
+
LabwareNotLoadedOnModuleError,
|
|
57
|
+
LabwareNotLoadedOnLabwareError,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
from ... import validation
|
|
61
|
+
from ..._types import OffDeckType
|
|
62
|
+
from ..._liquid import Liquid, LiquidClass
|
|
63
|
+
from ...disposal_locations import TrashBin, WasteChute
|
|
64
|
+
from ..protocol import AbstractProtocol
|
|
65
|
+
from ..labware import LabwareLoadParams
|
|
66
|
+
from .labware import LabwareCore
|
|
67
|
+
from .instrument import InstrumentCore
|
|
68
|
+
from .robot import RobotCore
|
|
69
|
+
from .module_core import (
|
|
70
|
+
ModuleCore,
|
|
71
|
+
TemperatureModuleCore,
|
|
72
|
+
MagneticModuleCore,
|
|
73
|
+
ThermocyclerModuleCore,
|
|
74
|
+
HeaterShakerModuleCore,
|
|
75
|
+
NonConnectedModuleCore,
|
|
76
|
+
MagneticBlockCore,
|
|
77
|
+
AbsorbanceReaderCore,
|
|
78
|
+
FlexStackerCore,
|
|
79
|
+
)
|
|
80
|
+
from .exceptions import InvalidModuleLocationError
|
|
81
|
+
from . import load_labware_params, deck_conflict, overlap_versions
|
|
82
|
+
from opentrons.protocol_engine.resources import labware_validation
|
|
83
|
+
|
|
84
|
+
if TYPE_CHECKING:
|
|
85
|
+
from ...labware import Labware
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ProtocolCore(
|
|
89
|
+
AbstractProtocol[
|
|
90
|
+
InstrumentCore,
|
|
91
|
+
LabwareCore,
|
|
92
|
+
Union[ModuleCore, NonConnectedModuleCore],
|
|
93
|
+
]
|
|
94
|
+
):
|
|
95
|
+
"""Protocol API core using a ProtocolEngine.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
engine_client: A client to the ProtocolEngine that is executing the protocol.
|
|
99
|
+
api_version: The Python Protocol API versionat which this core is operating.
|
|
100
|
+
sync_hardware: A SynchronousAdapter-wrapped Hardware Control API.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
engine_client: ProtocolEngineClient,
|
|
106
|
+
api_version: APIVersion,
|
|
107
|
+
sync_hardware: SyncHardwareAPI,
|
|
108
|
+
) -> None:
|
|
109
|
+
self._engine_client = engine_client
|
|
110
|
+
self._api_version = api_version
|
|
111
|
+
self._sync_hardware = sync_hardware
|
|
112
|
+
self._last_location: Optional[Union[Location, TrashBin, WasteChute]] = None
|
|
113
|
+
self._last_mount: Optional[Mount] = None
|
|
114
|
+
self._labware_cores_by_id: Dict[str, LabwareCore] = {}
|
|
115
|
+
self._module_cores_by_id: Dict[
|
|
116
|
+
str, Union[ModuleCore, NonConnectedModuleCore]
|
|
117
|
+
] = {}
|
|
118
|
+
self._disposal_locations: List[Union[Labware, TrashBin, WasteChute]] = []
|
|
119
|
+
self._liquid_class_def_cache: Dict[Tuple[str, int], LiquidClassSchemaV1] = {}
|
|
120
|
+
self._load_fixed_trash()
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def api_version(self) -> APIVersion:
|
|
124
|
+
"""Get the api version protocol target."""
|
|
125
|
+
return self._api_version
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def robot_type(self) -> RobotType:
|
|
129
|
+
return self._engine_client.state.config.robot_type
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def fixed_trash(self) -> Optional[LabwareCore]:
|
|
133
|
+
"""Get the fixed trash labware."""
|
|
134
|
+
trash_id = self._engine_client.state.labware.get_fixed_trash_id()
|
|
135
|
+
if trash_id is not None and self._api_version < APIVersion(2, 16):
|
|
136
|
+
return self._labware_cores_by_id[trash_id]
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
def _load_fixed_trash(self) -> None:
|
|
140
|
+
if self.robot_type == "OT-2 Standard" or self._api_version < APIVersion(2, 16):
|
|
141
|
+
trash_id = self._engine_client.state.labware.get_fixed_trash_id()
|
|
142
|
+
if trash_id is not None:
|
|
143
|
+
self._labware_cores_by_id[trash_id] = LabwareCore(
|
|
144
|
+
labware_id=trash_id,
|
|
145
|
+
engine_client=self._engine_client,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def append_disposal_location(
|
|
149
|
+
self,
|
|
150
|
+
disposal_location: Union[Labware, TrashBin, WasteChute],
|
|
151
|
+
) -> None:
|
|
152
|
+
"""Append a disposal location object to the core."""
|
|
153
|
+
self._disposal_locations.append(disposal_location)
|
|
154
|
+
|
|
155
|
+
def _add_disposal_location_to_engine(
|
|
156
|
+
self, disposal_location: Union[TrashBin, WasteChute]
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Verify and add disposal location to engine store and append it to the core."""
|
|
159
|
+
self._engine_client.state.addressable_areas.raise_if_area_not_in_deck_configuration(
|
|
160
|
+
disposal_location.area_name
|
|
161
|
+
)
|
|
162
|
+
if isinstance(disposal_location, TrashBin):
|
|
163
|
+
deck_conflict.check(
|
|
164
|
+
engine_state=self._engine_client.state,
|
|
165
|
+
new_trash_bin=disposal_location,
|
|
166
|
+
existing_disposal_locations=self._disposal_locations,
|
|
167
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
168
|
+
# See comment in self.load_labware().
|
|
169
|
+
#
|
|
170
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
171
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
172
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
173
|
+
)
|
|
174
|
+
self._engine_client.add_addressable_area(disposal_location.area_name)
|
|
175
|
+
self.append_disposal_location(disposal_location)
|
|
176
|
+
|
|
177
|
+
def get_disposal_locations(self) -> List[Union[Labware, TrashBin, WasteChute]]:
|
|
178
|
+
"""Get disposal locations."""
|
|
179
|
+
return self._disposal_locations
|
|
180
|
+
|
|
181
|
+
def get_max_speeds(self) -> AxisMaxSpeeds:
|
|
182
|
+
"""Get a control interface for maximum move speeds."""
|
|
183
|
+
raise NotImplementedError("ProtocolCore.get_max_speeds not implemented")
|
|
184
|
+
|
|
185
|
+
def get_hardware(self) -> SyncHardwareAPI:
|
|
186
|
+
"""Get direct access to a hardware control interface."""
|
|
187
|
+
return self._sync_hardware
|
|
188
|
+
|
|
189
|
+
def is_simulating(self) -> bool:
|
|
190
|
+
"""Get whether the protocol is being analyzed or actually run."""
|
|
191
|
+
return self._engine_client.state.config.ignore_pause
|
|
192
|
+
|
|
193
|
+
def add_labware_definition(
|
|
194
|
+
self,
|
|
195
|
+
definition: LabwareDefDict,
|
|
196
|
+
) -> LabwareLoadParams:
|
|
197
|
+
"""Add a labware definition to the set of loadable definitions."""
|
|
198
|
+
uri = self._engine_client.add_labware_definition(
|
|
199
|
+
labware_definition_type_adapter.validate_python(definition)
|
|
200
|
+
)
|
|
201
|
+
return LabwareLoadParams.from_uri(uri)
|
|
202
|
+
|
|
203
|
+
def load_labware(
|
|
204
|
+
self,
|
|
205
|
+
load_name: str,
|
|
206
|
+
location: Union[
|
|
207
|
+
DeckSlotName,
|
|
208
|
+
StagingSlotName,
|
|
209
|
+
LabwareCore,
|
|
210
|
+
ModuleCore,
|
|
211
|
+
NonConnectedModuleCore,
|
|
212
|
+
OffDeckType,
|
|
213
|
+
],
|
|
214
|
+
label: Optional[str],
|
|
215
|
+
namespace: Optional[str],
|
|
216
|
+
version: Optional[int],
|
|
217
|
+
) -> LabwareCore:
|
|
218
|
+
"""Load a labware using its identifying parameters."""
|
|
219
|
+
load_location = self._convert_labware_location(location=location)
|
|
220
|
+
|
|
221
|
+
custom_labware_params = (
|
|
222
|
+
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
223
|
+
)
|
|
224
|
+
namespace, version = load_labware_params.resolve(
|
|
225
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
load_result = self._engine_client.execute_command_without_recovery(
|
|
229
|
+
cmd.LoadLabwareParams(
|
|
230
|
+
loadName=load_name,
|
|
231
|
+
location=load_location,
|
|
232
|
+
namespace=namespace,
|
|
233
|
+
version=version,
|
|
234
|
+
displayName=label,
|
|
235
|
+
)
|
|
236
|
+
)
|
|
237
|
+
# FIXME(jbl, 2023-08-14) validating after loading the object issue
|
|
238
|
+
validation.ensure_definition_is_labware(load_result.definition)
|
|
239
|
+
validation.ensure_definition_is_not_lid_after_api_version(
|
|
240
|
+
self.api_version, load_result.definition
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# FIXME(mm, 2023-02-21):
|
|
244
|
+
#
|
|
245
|
+
# We're wrongly checking for deck conflicts *after* we've already loaded the
|
|
246
|
+
# labware into the ProtocolEngine. If it turns out there is a conflict,
|
|
247
|
+
# and this check raises, it will leave this object and its ProtocolEngine
|
|
248
|
+
# in a confusing inconsistent state.
|
|
249
|
+
#
|
|
250
|
+
# I expect we can get away with this in practice a lot of the time because
|
|
251
|
+
# exceptions in Python protocols are mostly treated as fatal, anyway.
|
|
252
|
+
# Users rarely catch them.
|
|
253
|
+
deck_conflict.check(
|
|
254
|
+
engine_state=self._engine_client.state,
|
|
255
|
+
new_labware_id=load_result.labwareId,
|
|
256
|
+
existing_disposal_locations=self._disposal_locations,
|
|
257
|
+
# TODO (spp, 2023-11-27): We've been using IDs from _labware_cores_by_id
|
|
258
|
+
# and _module_cores_by_id instead of getting the lists directly from engine
|
|
259
|
+
# because of the chance of engine carrying labware IDs from LPC too.
|
|
260
|
+
# But with https://github.com/Opentrons/opentrons/pull/13943,
|
|
261
|
+
# & LPC in maintenance runs, we can now rely on engine state for these IDs too.
|
|
262
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
263
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
264
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
labware_core = LabwareCore(
|
|
268
|
+
labware_id=load_result.labwareId,
|
|
269
|
+
engine_client=self._engine_client,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
self._labware_cores_by_id[labware_core.labware_id] = labware_core
|
|
273
|
+
|
|
274
|
+
return labware_core
|
|
275
|
+
|
|
276
|
+
def load_adapter(
|
|
277
|
+
self,
|
|
278
|
+
load_name: str,
|
|
279
|
+
location: Union[
|
|
280
|
+
DeckSlotName,
|
|
281
|
+
StagingSlotName,
|
|
282
|
+
ModuleCore,
|
|
283
|
+
NonConnectedModuleCore,
|
|
284
|
+
OffDeckType,
|
|
285
|
+
],
|
|
286
|
+
namespace: Optional[str],
|
|
287
|
+
version: Optional[int],
|
|
288
|
+
) -> LabwareCore:
|
|
289
|
+
"""Load an adapter using its identifying parameters"""
|
|
290
|
+
load_location = self._get_non_stacked_location(location=location)
|
|
291
|
+
|
|
292
|
+
custom_labware_params = (
|
|
293
|
+
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
294
|
+
)
|
|
295
|
+
namespace, version = load_labware_params.resolve(
|
|
296
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
297
|
+
)
|
|
298
|
+
load_result = self._engine_client.execute_command_without_recovery(
|
|
299
|
+
cmd.LoadLabwareParams(
|
|
300
|
+
loadName=load_name,
|
|
301
|
+
location=load_location,
|
|
302
|
+
namespace=namespace,
|
|
303
|
+
version=version,
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
# FIXME(jbl, 2023-08-14) validating after loading the object issue
|
|
307
|
+
validation.ensure_definition_is_adapter(load_result.definition)
|
|
308
|
+
|
|
309
|
+
# FIXME(jbl, 2023-06-23) read fixme above:
|
|
310
|
+
deck_conflict.check(
|
|
311
|
+
engine_state=self._engine_client.state,
|
|
312
|
+
new_labware_id=load_result.labwareId,
|
|
313
|
+
existing_disposal_locations=self._disposal_locations,
|
|
314
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
315
|
+
# See comment in self.load_labware().
|
|
316
|
+
#
|
|
317
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
318
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
319
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
labware_core = LabwareCore(
|
|
323
|
+
labware_id=load_result.labwareId,
|
|
324
|
+
engine_client=self._engine_client,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
self._labware_cores_by_id[labware_core.labware_id] = labware_core
|
|
328
|
+
|
|
329
|
+
return labware_core
|
|
330
|
+
|
|
331
|
+
def load_lid(
|
|
332
|
+
self,
|
|
333
|
+
load_name: str,
|
|
334
|
+
location: LabwareCore,
|
|
335
|
+
namespace: Optional[str],
|
|
336
|
+
version: Optional[int],
|
|
337
|
+
) -> LabwareCore:
|
|
338
|
+
"""Load an individual lid using its identifying parameters. Must be loaded on an existing Labware."""
|
|
339
|
+
load_location = self._convert_labware_location(location=location)
|
|
340
|
+
custom_labware_params = (
|
|
341
|
+
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
342
|
+
)
|
|
343
|
+
namespace, version = load_labware_params.resolve(
|
|
344
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
345
|
+
)
|
|
346
|
+
load_result = self._engine_client.execute_command_without_recovery(
|
|
347
|
+
cmd.LoadLidParams(
|
|
348
|
+
loadName=load_name,
|
|
349
|
+
location=load_location,
|
|
350
|
+
namespace=namespace,
|
|
351
|
+
version=version,
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
# FIXME(chb, 2024-12-06) validating after loading the object issue
|
|
355
|
+
validation.ensure_definition_is_lid(load_result.definition)
|
|
356
|
+
|
|
357
|
+
deck_conflict.check(
|
|
358
|
+
engine_state=self._engine_client.state,
|
|
359
|
+
new_labware_id=load_result.labwareId,
|
|
360
|
+
existing_disposal_locations=self._disposal_locations,
|
|
361
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
362
|
+
# See comment in self.load_labware().
|
|
363
|
+
#
|
|
364
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
365
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
366
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
labware_core = LabwareCore(
|
|
370
|
+
labware_id=load_result.labwareId,
|
|
371
|
+
engine_client=self._engine_client,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
self._labware_cores_by_id[labware_core.labware_id] = labware_core
|
|
375
|
+
return labware_core
|
|
376
|
+
|
|
377
|
+
def move_labware(
|
|
378
|
+
self,
|
|
379
|
+
labware_core: LabwareCore,
|
|
380
|
+
new_location: Union[
|
|
381
|
+
DeckSlotName,
|
|
382
|
+
StagingSlotName,
|
|
383
|
+
LabwareCore,
|
|
384
|
+
ModuleCore,
|
|
385
|
+
NonConnectedModuleCore,
|
|
386
|
+
OffDeckType,
|
|
387
|
+
WasteChute,
|
|
388
|
+
TrashBin,
|
|
389
|
+
],
|
|
390
|
+
use_gripper: bool,
|
|
391
|
+
pause_for_manual_move: bool,
|
|
392
|
+
pick_up_offset: Optional[Tuple[float, float, float]],
|
|
393
|
+
drop_offset: Optional[Tuple[float, float, float]],
|
|
394
|
+
) -> None:
|
|
395
|
+
"""Move the given labware to a new location."""
|
|
396
|
+
if use_gripper:
|
|
397
|
+
strategy = LabwareMovementStrategy.USING_GRIPPER
|
|
398
|
+
elif pause_for_manual_move:
|
|
399
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE
|
|
400
|
+
else:
|
|
401
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE
|
|
402
|
+
|
|
403
|
+
_pick_up_offset = (
|
|
404
|
+
LabwareOffsetVector(
|
|
405
|
+
x=pick_up_offset[0], y=pick_up_offset[1], z=pick_up_offset[2]
|
|
406
|
+
)
|
|
407
|
+
if pick_up_offset
|
|
408
|
+
else None
|
|
409
|
+
)
|
|
410
|
+
_drop_offset = (
|
|
411
|
+
LabwareOffsetVector(x=drop_offset[0], y=drop_offset[1], z=drop_offset[2])
|
|
412
|
+
if drop_offset
|
|
413
|
+
else None
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
to_location = self._convert_labware_location(location=new_location)
|
|
417
|
+
|
|
418
|
+
self._engine_client.execute_command(
|
|
419
|
+
cmd.MoveLabwareParams(
|
|
420
|
+
labwareId=labware_core.labware_id,
|
|
421
|
+
newLocation=to_location,
|
|
422
|
+
strategy=strategy,
|
|
423
|
+
pickUpOffset=_pick_up_offset,
|
|
424
|
+
dropOffset=_drop_offset,
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
if strategy == LabwareMovementStrategy.USING_GRIPPER:
|
|
429
|
+
# Clear out last location since it is not relevant to pipetting
|
|
430
|
+
# and we only use last location for in-place pipetting commands
|
|
431
|
+
self.set_last_location(location=None, mount=Mount.EXTENSION)
|
|
432
|
+
|
|
433
|
+
# FIXME(jbl, 2024-01-04) deck conflict after execution logic issue, read notes in load_labware for more info:
|
|
434
|
+
deck_conflict.check(
|
|
435
|
+
engine_state=self._engine_client.state,
|
|
436
|
+
new_labware_id=labware_core.labware_id,
|
|
437
|
+
existing_disposal_locations=self._disposal_locations,
|
|
438
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
439
|
+
# See comment in self.load_labware().
|
|
440
|
+
existing_labware_ids=[
|
|
441
|
+
labware_id
|
|
442
|
+
for labware_id in self._labware_cores_by_id
|
|
443
|
+
if labware_id != labware_core.labware_id
|
|
444
|
+
],
|
|
445
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
def move_lid( # noqa: C901
|
|
449
|
+
self,
|
|
450
|
+
source_location: Union[DeckSlotName, StagingSlotName, LabwareCore],
|
|
451
|
+
new_location: Union[
|
|
452
|
+
DeckSlotName,
|
|
453
|
+
StagingSlotName,
|
|
454
|
+
LabwareCore,
|
|
455
|
+
OffDeckType,
|
|
456
|
+
WasteChute,
|
|
457
|
+
TrashBin,
|
|
458
|
+
],
|
|
459
|
+
use_gripper: bool,
|
|
460
|
+
pause_for_manual_move: bool,
|
|
461
|
+
pick_up_offset: Optional[Tuple[float, float, float]],
|
|
462
|
+
drop_offset: Optional[Tuple[float, float, float]],
|
|
463
|
+
) -> LabwareCore | None:
|
|
464
|
+
"""Move the given lid to a new location."""
|
|
465
|
+
if use_gripper:
|
|
466
|
+
strategy = LabwareMovementStrategy.USING_GRIPPER
|
|
467
|
+
elif pause_for_manual_move:
|
|
468
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE
|
|
469
|
+
else:
|
|
470
|
+
strategy = LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE
|
|
471
|
+
|
|
472
|
+
if isinstance(source_location, DeckSlotName) or isinstance(
|
|
473
|
+
source_location, StagingSlotName
|
|
474
|
+
):
|
|
475
|
+
# Find the source labware at the provided deck slot
|
|
476
|
+
labware_in_slot = self._engine_client.state.labware.get_by_slot(
|
|
477
|
+
source_location
|
|
478
|
+
)
|
|
479
|
+
if labware_in_slot is None:
|
|
480
|
+
raise LabwareNotLoadedOnLabwareError(
|
|
481
|
+
"Lid cannot be loaded on non-labware position."
|
|
482
|
+
)
|
|
483
|
+
else:
|
|
484
|
+
labware = LabwareCore(labware_in_slot.id, self._engine_client)
|
|
485
|
+
else:
|
|
486
|
+
labware = source_location
|
|
487
|
+
|
|
488
|
+
# if this is a labware stack, we need to find the labware at the top of the stack
|
|
489
|
+
if labware_validation.is_lid_stack(labware.load_name):
|
|
490
|
+
lid_id = self._engine_client.state.labware.get_highest_child_labware(
|
|
491
|
+
labware.labware_id
|
|
492
|
+
)
|
|
493
|
+
# if this is a labware with a lid, we just need to find its lid_id
|
|
494
|
+
else:
|
|
495
|
+
lid = self._engine_client.state.labware.get_lid_by_labware_id(
|
|
496
|
+
labware.labware_id
|
|
497
|
+
)
|
|
498
|
+
if lid is not None:
|
|
499
|
+
lid_id = lid.id
|
|
500
|
+
else:
|
|
501
|
+
raise ValueError("Cannot move a lid off of a labware with no lid.")
|
|
502
|
+
|
|
503
|
+
_pick_up_offset = (
|
|
504
|
+
LabwareOffsetVector(
|
|
505
|
+
x=pick_up_offset[0], y=pick_up_offset[1], z=pick_up_offset[2]
|
|
506
|
+
)
|
|
507
|
+
if pick_up_offset
|
|
508
|
+
else None
|
|
509
|
+
)
|
|
510
|
+
_drop_offset = (
|
|
511
|
+
LabwareOffsetVector(x=drop_offset[0], y=drop_offset[1], z=drop_offset[2])
|
|
512
|
+
if drop_offset
|
|
513
|
+
else None
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
create_new_lid_stack = False
|
|
517
|
+
|
|
518
|
+
if isinstance(new_location, DeckSlotName) or isinstance(
|
|
519
|
+
new_location, StagingSlotName
|
|
520
|
+
):
|
|
521
|
+
# Find the destination labware at the provided deck slot
|
|
522
|
+
destination_labware_in_slot = self._engine_client.state.labware.get_by_slot(
|
|
523
|
+
new_location
|
|
524
|
+
)
|
|
525
|
+
if destination_labware_in_slot is None:
|
|
526
|
+
to_location = self._convert_labware_location(location=new_location)
|
|
527
|
+
# absolutely must make a new lid stack
|
|
528
|
+
create_new_lid_stack = True
|
|
529
|
+
else:
|
|
530
|
+
highest_child_location = (
|
|
531
|
+
self._engine_client.state.labware.get_highest_child_labware(
|
|
532
|
+
destination_labware_in_slot.id
|
|
533
|
+
)
|
|
534
|
+
)
|
|
535
|
+
if labware_validation.validate_definition_is_adapter(
|
|
536
|
+
self._engine_client.state.labware.get_definition(
|
|
537
|
+
highest_child_location
|
|
538
|
+
)
|
|
539
|
+
):
|
|
540
|
+
# absolutely must make a new lid stack
|
|
541
|
+
create_new_lid_stack = True
|
|
542
|
+
|
|
543
|
+
to_location = self._convert_labware_location(
|
|
544
|
+
location=LabwareCore(highest_child_location, self._engine_client)
|
|
545
|
+
)
|
|
546
|
+
elif isinstance(new_location, LabwareCore):
|
|
547
|
+
highest_child_location = (
|
|
548
|
+
self._engine_client.state.labware.get_highest_child_labware(
|
|
549
|
+
new_location.labware_id
|
|
550
|
+
)
|
|
551
|
+
)
|
|
552
|
+
if labware_validation.validate_definition_is_adapter(
|
|
553
|
+
self._engine_client.state.labware.get_definition(highest_child_location)
|
|
554
|
+
):
|
|
555
|
+
# absolutely must make a new lid stack
|
|
556
|
+
create_new_lid_stack = True
|
|
557
|
+
to_location = self._convert_labware_location(
|
|
558
|
+
location=LabwareCore(highest_child_location, self._engine_client)
|
|
559
|
+
)
|
|
560
|
+
else:
|
|
561
|
+
to_location = self._convert_labware_location(location=new_location)
|
|
562
|
+
|
|
563
|
+
output_result = None
|
|
564
|
+
if create_new_lid_stack:
|
|
565
|
+
# Make a new lid stack object that is empty
|
|
566
|
+
result = self._engine_client.execute_command_without_recovery(
|
|
567
|
+
cmd.LoadLidStackParams(
|
|
568
|
+
location=SYSTEM_LOCATION,
|
|
569
|
+
loadName="empty",
|
|
570
|
+
version=1,
|
|
571
|
+
namespace="empty",
|
|
572
|
+
quantity=0,
|
|
573
|
+
)
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# Move the lid stack object from the SYSTEM_LOCATION space to the desired deck location
|
|
577
|
+
self._engine_client.execute_command(
|
|
578
|
+
cmd.MoveLabwareParams(
|
|
579
|
+
labwareId=result.stackLabwareId,
|
|
580
|
+
newLocation=to_location,
|
|
581
|
+
strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE,
|
|
582
|
+
pickUpOffset=None,
|
|
583
|
+
dropOffset=None,
|
|
584
|
+
)
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
output_result = LabwareCore(
|
|
588
|
+
labware_id=result.stackLabwareId, engine_client=self._engine_client
|
|
589
|
+
)
|
|
590
|
+
destination = self._convert_labware_location(location=output_result)
|
|
591
|
+
else:
|
|
592
|
+
destination = to_location
|
|
593
|
+
|
|
594
|
+
self._engine_client.execute_command(
|
|
595
|
+
cmd.MoveLabwareParams(
|
|
596
|
+
labwareId=lid_id,
|
|
597
|
+
newLocation=destination,
|
|
598
|
+
strategy=strategy,
|
|
599
|
+
pickUpOffset=_pick_up_offset,
|
|
600
|
+
dropOffset=_drop_offset,
|
|
601
|
+
)
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
# Handle leftover empty lid stack if there is one
|
|
605
|
+
if (
|
|
606
|
+
labware_validation.is_lid_stack(labware.load_name)
|
|
607
|
+
and self._engine_client.state.labware.get_highest_child_labware(
|
|
608
|
+
labware_id=labware.labware_id
|
|
609
|
+
)
|
|
610
|
+
== labware.labware_id
|
|
611
|
+
):
|
|
612
|
+
# The originating lid stack is now empty, so we need to move it to the SYSTEM_LOCATION
|
|
613
|
+
self._engine_client.execute_command(
|
|
614
|
+
cmd.MoveLabwareParams(
|
|
615
|
+
labwareId=labware.labware_id,
|
|
616
|
+
newLocation=SYSTEM_LOCATION,
|
|
617
|
+
strategy=LabwareMovementStrategy.MANUAL_MOVE_WITHOUT_PAUSE,
|
|
618
|
+
pickUpOffset=None,
|
|
619
|
+
dropOffset=None,
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
if strategy == LabwareMovementStrategy.USING_GRIPPER:
|
|
624
|
+
# Clear out last location since it is not relevant to pipetting
|
|
625
|
+
# and we only use last location for in-place pipetting commands
|
|
626
|
+
self.set_last_location(location=None, mount=Mount.EXTENSION)
|
|
627
|
+
|
|
628
|
+
# FIXME(jbl, 2024-01-04) deck conflict after execution logic issue, read notes in load_labware for more info:
|
|
629
|
+
deck_conflict.check(
|
|
630
|
+
engine_state=self._engine_client.state,
|
|
631
|
+
new_labware_id=lid_id,
|
|
632
|
+
existing_disposal_locations=self._disposal_locations,
|
|
633
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
634
|
+
# See comment in self.load_labware().
|
|
635
|
+
existing_labware_ids=[
|
|
636
|
+
labware_id
|
|
637
|
+
for labware_id in self._labware_cores_by_id
|
|
638
|
+
if labware_id != labware_id
|
|
639
|
+
],
|
|
640
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
return output_result
|
|
644
|
+
|
|
645
|
+
def _resolve_module_hardware(
|
|
646
|
+
self, serial_number: str, model: ModuleModel
|
|
647
|
+
) -> AbstractModule:
|
|
648
|
+
"""Resolve a module serial number to module hardware API."""
|
|
649
|
+
if self.is_simulating():
|
|
650
|
+
return self._sync_hardware.create_simulating_module(model) # type: ignore[no-any-return]
|
|
651
|
+
|
|
652
|
+
for module_hardware in self._sync_hardware.attached_modules:
|
|
653
|
+
if serial_number == module_hardware.device_info["serial"]:
|
|
654
|
+
return module_hardware # type: ignore[no-any-return]
|
|
655
|
+
|
|
656
|
+
raise RuntimeError(f"Could not find specified module: {model.value}")
|
|
657
|
+
|
|
658
|
+
def load_module(
|
|
659
|
+
self,
|
|
660
|
+
model: ModuleModel,
|
|
661
|
+
deck_slot: Optional[DeckSlotName],
|
|
662
|
+
configuration: Optional[str],
|
|
663
|
+
) -> Union[ModuleCore, NonConnectedModuleCore]:
|
|
664
|
+
"""Load a module into the protocol."""
|
|
665
|
+
assert configuration is None, "Module `configuration` is deprecated"
|
|
666
|
+
|
|
667
|
+
module_type = ModuleType.from_model(model)
|
|
668
|
+
# TODO(mc, 2022-10-20): move to public ProtocolContext
|
|
669
|
+
# once `Deck` and `ProtocolEngine` play nicely together
|
|
670
|
+
if deck_slot is None:
|
|
671
|
+
module_type = ModuleType.from_model(model)
|
|
672
|
+
if module_type == ModuleType.THERMOCYCLER:
|
|
673
|
+
deck_slot = DeckSlotName.SLOT_7
|
|
674
|
+
else:
|
|
675
|
+
raise InvalidModuleLocationError(deck_slot, model.name)
|
|
676
|
+
|
|
677
|
+
robot_type = self._engine_client.state.config.robot_type
|
|
678
|
+
# todo(mm, 2024-12-03): This might be possible to remove:
|
|
679
|
+
# Protocol Engine will normalize the deck slot itself.
|
|
680
|
+
normalized_deck_slot = deck_slot.to_equivalent_for_robot_type(robot_type)
|
|
681
|
+
|
|
682
|
+
result = self._engine_client.execute_command_without_recovery(
|
|
683
|
+
cmd.LoadModuleParams(
|
|
684
|
+
model=EngineModuleModel(model),
|
|
685
|
+
location=DeckSlotLocation(slotName=normalized_deck_slot),
|
|
686
|
+
)
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
module_core = self._get_module_core(load_module_result=result, model=model)
|
|
690
|
+
|
|
691
|
+
# FIXME(mm, 2023-02-21):
|
|
692
|
+
# We're wrongly doing this conflict check *after* we've already loaded the
|
|
693
|
+
# module into the ProtocolEngine. See FIXME comment in self.load_labware().
|
|
694
|
+
deck_conflict.check(
|
|
695
|
+
engine_state=self._engine_client.state,
|
|
696
|
+
new_module_id=result.moduleId,
|
|
697
|
+
existing_disposal_locations=self._disposal_locations,
|
|
698
|
+
# TODO: We can now fetch these IDs from engine too.
|
|
699
|
+
# See comment in self.load_labware().
|
|
700
|
+
#
|
|
701
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
702
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
703
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
self._module_cores_by_id[module_core.module_id] = module_core
|
|
707
|
+
|
|
708
|
+
return module_core
|
|
709
|
+
|
|
710
|
+
def _create_non_connected_module_core(
|
|
711
|
+
self, load_module_result: LoadModuleResult
|
|
712
|
+
) -> NonConnectedModuleCore:
|
|
713
|
+
return MagneticBlockCore(
|
|
714
|
+
module_id=load_module_result.moduleId,
|
|
715
|
+
engine_client=self._engine_client,
|
|
716
|
+
api_version=self.api_version,
|
|
717
|
+
protocol_core=self,
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
def _create_module_core(
|
|
721
|
+
self, load_module_result: LoadModuleResult, model: ModuleModel
|
|
722
|
+
) -> ModuleCore:
|
|
723
|
+
module_core_cls: Type[ModuleCore] = ModuleCore
|
|
724
|
+
|
|
725
|
+
type_lookup: Dict[ModuleType, Type[ModuleCore]] = {
|
|
726
|
+
ModuleType.TEMPERATURE: TemperatureModuleCore,
|
|
727
|
+
ModuleType.MAGNETIC: MagneticModuleCore,
|
|
728
|
+
ModuleType.THERMOCYCLER: ThermocyclerModuleCore,
|
|
729
|
+
ModuleType.HEATER_SHAKER: HeaterShakerModuleCore,
|
|
730
|
+
ModuleType.ABSORBANCE_READER: AbsorbanceReaderCore,
|
|
731
|
+
ModuleType.FLEX_STACKER: FlexStackerCore,
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
module_type = load_module_result.model.as_type()
|
|
735
|
+
|
|
736
|
+
module_core_cls = type_lookup[module_type]
|
|
737
|
+
|
|
738
|
+
assert (
|
|
739
|
+
load_module_result.serialNumber is not None
|
|
740
|
+
), "Expected a connected module but did not get a serial number."
|
|
741
|
+
selected_hardware = self._resolve_module_hardware(
|
|
742
|
+
load_module_result.serialNumber, model
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
return module_core_cls(
|
|
746
|
+
module_id=load_module_result.moduleId,
|
|
747
|
+
engine_client=self._engine_client,
|
|
748
|
+
api_version=self.api_version,
|
|
749
|
+
sync_module_hardware=SynchronousAdapter(selected_hardware),
|
|
750
|
+
protocol_core=self,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
def _get_module_core(
|
|
754
|
+
self, load_module_result: LoadModuleResult, model: ModuleModel
|
|
755
|
+
) -> Union[ModuleCore, NonConnectedModuleCore]:
|
|
756
|
+
if ProtocolEngineModuleModel.is_magnetic_block(load_module_result.model):
|
|
757
|
+
return self._create_non_connected_module_core(load_module_result)
|
|
758
|
+
else:
|
|
759
|
+
return self._create_module_core(
|
|
760
|
+
load_module_result=load_module_result, model=model
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
def add_or_get_labware_core(self, labware_id: str) -> LabwareCore:
|
|
764
|
+
"""Create a LabwareCore and add it to the map or return one if it exists."""
|
|
765
|
+
if labware_id in self._labware_cores_by_id:
|
|
766
|
+
return self._labware_cores_by_id[labware_id]
|
|
767
|
+
else:
|
|
768
|
+
core = LabwareCore(labware_id, self._engine_client)
|
|
769
|
+
self._labware_cores_by_id[labware_id] = core
|
|
770
|
+
return core
|
|
771
|
+
|
|
772
|
+
def load_robot(self) -> RobotCore:
|
|
773
|
+
"""Load a robot core into the RobotContext."""
|
|
774
|
+
return RobotCore(
|
|
775
|
+
engine_client=self._engine_client, sync_hardware_api=self._sync_hardware
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
def load_instrument(
|
|
779
|
+
self,
|
|
780
|
+
instrument_name: PipetteNameType,
|
|
781
|
+
mount: Mount,
|
|
782
|
+
liquid_presence_detection: bool = False,
|
|
783
|
+
) -> InstrumentCore:
|
|
784
|
+
"""Load an instrument into the protocol.
|
|
785
|
+
|
|
786
|
+
Args:
|
|
787
|
+
instrument_name: Load name of the instrument.
|
|
788
|
+
mount: Mount the instrument is attached to.
|
|
789
|
+
|
|
790
|
+
Returns:
|
|
791
|
+
An instrument core configured to use the requested instrument.
|
|
792
|
+
"""
|
|
793
|
+
engine_mount = MountType[mount.name]
|
|
794
|
+
load_result = self._engine_client.execute_command_without_recovery(
|
|
795
|
+
cmd.LoadPipetteParams(
|
|
796
|
+
pipetteName=instrument_name,
|
|
797
|
+
mount=engine_mount,
|
|
798
|
+
tipOverlapNotAfterVersion=overlap_versions.overlap_for_api_version(
|
|
799
|
+
self._api_version
|
|
800
|
+
),
|
|
801
|
+
liquidPresenceDetection=liquid_presence_detection,
|
|
802
|
+
)
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
return InstrumentCore(
|
|
806
|
+
pipette_id=load_result.pipetteId,
|
|
807
|
+
engine_client=self._engine_client,
|
|
808
|
+
sync_hardware_api=self._sync_hardware,
|
|
809
|
+
protocol_core=self,
|
|
810
|
+
# TODO(mm, 2022-11-10): Deduplicate "400" with legacy core.
|
|
811
|
+
default_movement_speed=400,
|
|
812
|
+
)
|
|
813
|
+
|
|
814
|
+
def load_trash_bin(self, slot_name: DeckSlotName, area_name: str) -> TrashBin:
|
|
815
|
+
"""Load a deck configuration based trash bin.
|
|
816
|
+
|
|
817
|
+
Args:
|
|
818
|
+
slot_name: the slot the trash is being loaded into.
|
|
819
|
+
area_name: the addressable area name of the trash.
|
|
820
|
+
|
|
821
|
+
Returns:
|
|
822
|
+
A trash bin object.
|
|
823
|
+
"""
|
|
824
|
+
trash_bin = TrashBin(
|
|
825
|
+
location=slot_name,
|
|
826
|
+
addressable_area_name=area_name,
|
|
827
|
+
api_version=self._api_version,
|
|
828
|
+
engine_client=self._engine_client,
|
|
829
|
+
)
|
|
830
|
+
self._add_disposal_location_to_engine(trash_bin)
|
|
831
|
+
return trash_bin
|
|
832
|
+
|
|
833
|
+
def load_ot2_fixed_trash_bin(self) -> None:
|
|
834
|
+
"""Load a deck configured OT-2 fixed trash in Slot 12."""
|
|
835
|
+
_fixed_trash_trash_bin = TrashBin(
|
|
836
|
+
location=DeckSlotName.FIXED_TRASH,
|
|
837
|
+
addressable_area_name="fixedTrash",
|
|
838
|
+
api_version=self._api_version,
|
|
839
|
+
engine_client=self._engine_client,
|
|
840
|
+
)
|
|
841
|
+
# We are just appending the fixed trash to the core's internal list here, not adding it to the engine via
|
|
842
|
+
# the core, since that method works through the SyncClient and if called from here, will cause protocols
|
|
843
|
+
# to deadlock. Instead, that method is called in protocol engine directly in create_protocol_context after
|
|
844
|
+
# ProtocolContext is initialized.
|
|
845
|
+
self.append_disposal_location(_fixed_trash_trash_bin)
|
|
846
|
+
|
|
847
|
+
def load_waste_chute(self) -> WasteChute:
|
|
848
|
+
"""Load a deck configured waste chute into Slot D3.
|
|
849
|
+
|
|
850
|
+
Returns:
|
|
851
|
+
A waste chute object.
|
|
852
|
+
"""
|
|
853
|
+
waste_chute = WasteChute(
|
|
854
|
+
engine_client=self._engine_client, api_version=self._api_version
|
|
855
|
+
)
|
|
856
|
+
self._add_disposal_location_to_engine(waste_chute)
|
|
857
|
+
return waste_chute
|
|
858
|
+
|
|
859
|
+
def pause(self, msg: Optional[str]) -> None:
|
|
860
|
+
"""Pause the protocol."""
|
|
861
|
+
self._engine_client.execute_command(cmd.WaitForResumeParams(message=msg))
|
|
862
|
+
|
|
863
|
+
def comment(self, msg: str) -> None:
|
|
864
|
+
"""Create a comment in the protocol to be shown in the log."""
|
|
865
|
+
self._engine_client.execute_command(cmd.CommentParams(message=msg))
|
|
866
|
+
|
|
867
|
+
def delay(self, seconds: float, msg: Optional[str]) -> None:
|
|
868
|
+
"""Wait for a period of time before proceeding."""
|
|
869
|
+
self._engine_client.execute_command(
|
|
870
|
+
cmd.WaitForDurationParams(seconds=seconds, message=msg)
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
def home(self) -> None:
|
|
874
|
+
"""Move all axes to their home positions."""
|
|
875
|
+
self._engine_client.execute_command(cmd.HomeParams(axes=None))
|
|
876
|
+
|
|
877
|
+
def set_rail_lights(self, on: bool) -> None:
|
|
878
|
+
"""Set the device's rail lights."""
|
|
879
|
+
self._engine_client.execute_command(cmd.SetRailLightsParams(on=on))
|
|
880
|
+
|
|
881
|
+
def get_rail_lights_on(self) -> bool:
|
|
882
|
+
"""Get whether the device's rail lights are on."""
|
|
883
|
+
return self._sync_hardware.get_lights()["rails"] # type: ignore[no-any-return]
|
|
884
|
+
|
|
885
|
+
def door_closed(self) -> bool:
|
|
886
|
+
"""Get whether the device's front door is closed."""
|
|
887
|
+
return self._sync_hardware.door_state == DoorState.CLOSED # type: ignore[no-any-return]
|
|
888
|
+
|
|
889
|
+
def get_last_location(
|
|
890
|
+
self,
|
|
891
|
+
mount: Optional[Mount] = None,
|
|
892
|
+
) -> Optional[Union[Location, TrashBin, WasteChute]]:
|
|
893
|
+
"""Get the last accessed location."""
|
|
894
|
+
if mount is None or mount == self._last_mount:
|
|
895
|
+
return self._last_location
|
|
896
|
+
|
|
897
|
+
return None
|
|
898
|
+
|
|
899
|
+
def set_last_location(
|
|
900
|
+
self,
|
|
901
|
+
location: Optional[Union[Location, TrashBin, WasteChute]],
|
|
902
|
+
mount: Optional[Mount] = None,
|
|
903
|
+
) -> None:
|
|
904
|
+
"""Set the last accessed location."""
|
|
905
|
+
self._last_location = location
|
|
906
|
+
self._last_mount = mount
|
|
907
|
+
|
|
908
|
+
def load_lid_stack(
|
|
909
|
+
self,
|
|
910
|
+
load_name: str,
|
|
911
|
+
location: Union[DeckSlotName, StagingSlotName, LabwareCore],
|
|
912
|
+
quantity: int,
|
|
913
|
+
namespace: Optional[str],
|
|
914
|
+
version: Optional[int],
|
|
915
|
+
) -> LabwareCore:
|
|
916
|
+
"""Load a Stack of Lids to a given location, creating a Lid Stack."""
|
|
917
|
+
if quantity < 1:
|
|
918
|
+
raise ValueError(
|
|
919
|
+
"When loading a lid stack quantity cannot be less than one."
|
|
920
|
+
)
|
|
921
|
+
if isinstance(location, DeckSlotName) or isinstance(location, StagingSlotName):
|
|
922
|
+
load_location = self._convert_labware_location(location=location)
|
|
923
|
+
else:
|
|
924
|
+
if isinstance(location, LabwareCore):
|
|
925
|
+
load_location = self._convert_labware_location(location=location)
|
|
926
|
+
else:
|
|
927
|
+
raise ValueError(
|
|
928
|
+
"Expected type of Labware Location for lid stack must be Labware, not Legacy Labware or Well."
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
custom_labware_params = (
|
|
932
|
+
self._engine_client.state.labware.find_custom_labware_load_params()
|
|
933
|
+
)
|
|
934
|
+
namespace, version = load_labware_params.resolve(
|
|
935
|
+
load_name, namespace, version, custom_labware_params, self._api_version
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
load_result = self._engine_client.execute_command_without_recovery(
|
|
939
|
+
cmd.LoadLidStackParams(
|
|
940
|
+
loadName=load_name,
|
|
941
|
+
location=load_location,
|
|
942
|
+
namespace=namespace,
|
|
943
|
+
version=version,
|
|
944
|
+
quantity=quantity,
|
|
945
|
+
)
|
|
946
|
+
)
|
|
947
|
+
|
|
948
|
+
# FIXME(CHB, 2024-12-04) just like load labware and load adapter we have a validating after loading the object issue
|
|
949
|
+
assert load_result.definition is not None
|
|
950
|
+
validation.ensure_definition_is_lid(load_result.definition)
|
|
951
|
+
|
|
952
|
+
deck_conflict.check(
|
|
953
|
+
engine_state=self._engine_client.state,
|
|
954
|
+
new_labware_id=load_result.stackLabwareId,
|
|
955
|
+
existing_disposal_locations=self._disposal_locations,
|
|
956
|
+
# TODO (spp, 2023-11-27): We've been using IDs from _labware_cores_by_id
|
|
957
|
+
# and _module_cores_by_id instead of getting the lists directly from engine
|
|
958
|
+
# because of the chance of engine carrying labware IDs from LPC too.
|
|
959
|
+
# But with https://github.com/Opentrons/opentrons/pull/13943,
|
|
960
|
+
# & LPC in maintenance runs, we can now rely on engine state for these IDs too.
|
|
961
|
+
# Wrapping .keys() in list() is just to make Decoy verification easier.
|
|
962
|
+
existing_labware_ids=list(self._labware_cores_by_id.keys()),
|
|
963
|
+
existing_module_ids=list(self._module_cores_by_id.keys()),
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
labware_core = LabwareCore(
|
|
967
|
+
labware_id=load_result.stackLabwareId,
|
|
968
|
+
engine_client=self._engine_client,
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
self._labware_cores_by_id[labware_core.labware_id] = labware_core
|
|
972
|
+
|
|
973
|
+
return labware_core
|
|
974
|
+
|
|
975
|
+
def get_deck_definition(self) -> DeckDefinitionV5:
|
|
976
|
+
"""Get the geometry definition of the robot's deck."""
|
|
977
|
+
return self._engine_client.state.labware.get_deck_definition()
|
|
978
|
+
|
|
979
|
+
def get_slot_definition(
|
|
980
|
+
self, slot: Union[DeckSlotName, StagingSlotName]
|
|
981
|
+
) -> SlotDefV3:
|
|
982
|
+
"""Get the slot definition from the robot's deck."""
|
|
983
|
+
return self._engine_client.state.addressable_areas.get_slot_definition(slot.id)
|
|
984
|
+
|
|
985
|
+
def get_slot_definitions(self) -> Dict[str, SlotDefV3]:
|
|
986
|
+
"""Get all standard slot definitions available in the deck definition."""
|
|
987
|
+
return self._engine_client.state.addressable_areas.get_deck_slot_definitions()
|
|
988
|
+
|
|
989
|
+
def get_staging_slot_definitions(self) -> Dict[str, SlotDefV3]:
|
|
990
|
+
"""Get all staging slot definitions available in the deck definition."""
|
|
991
|
+
return (
|
|
992
|
+
self._engine_client.state.addressable_areas.get_staging_slot_definitions()
|
|
993
|
+
)
|
|
994
|
+
|
|
995
|
+
def get_slot_item(
|
|
996
|
+
self, slot_name: Union[DeckSlotName, StagingSlotName]
|
|
997
|
+
) -> Union[LabwareCore, ModuleCore, NonConnectedModuleCore, None]:
|
|
998
|
+
"""Get the contents of a given slot, if any."""
|
|
999
|
+
loaded_item = self._engine_client.state.geometry.get_slot_item(
|
|
1000
|
+
slot_name=slot_name
|
|
1001
|
+
)
|
|
1002
|
+
|
|
1003
|
+
if isinstance(loaded_item, LoadedLabware):
|
|
1004
|
+
return self._labware_cores_by_id[loaded_item.id]
|
|
1005
|
+
|
|
1006
|
+
if isinstance(loaded_item, LoadedModule):
|
|
1007
|
+
return self._module_cores_by_id[loaded_item.id]
|
|
1008
|
+
|
|
1009
|
+
return None
|
|
1010
|
+
|
|
1011
|
+
def get_labware_on_module(
|
|
1012
|
+
self, module_core: Union[ModuleCore, NonConnectedModuleCore]
|
|
1013
|
+
) -> Optional[LabwareCore]:
|
|
1014
|
+
"""Get the item on top of a given module, if any."""
|
|
1015
|
+
try:
|
|
1016
|
+
labware_id = self._engine_client.state.labware.get_id_by_module(
|
|
1017
|
+
module_core.module_id
|
|
1018
|
+
)
|
|
1019
|
+
except LabwareNotLoadedOnModuleError:
|
|
1020
|
+
return None
|
|
1021
|
+
return self.add_or_get_labware_core(labware_id)
|
|
1022
|
+
|
|
1023
|
+
def get_labware_on_labware(
|
|
1024
|
+
self, labware_core: LabwareCore
|
|
1025
|
+
) -> Optional[LabwareCore]:
|
|
1026
|
+
"""Get the item on top of a given labware, if any."""
|
|
1027
|
+
try:
|
|
1028
|
+
labware_id = self._engine_client.state.labware.get_id_by_labware(
|
|
1029
|
+
labware_core.labware_id
|
|
1030
|
+
)
|
|
1031
|
+
except LabwareNotLoadedOnLabwareError:
|
|
1032
|
+
return None
|
|
1033
|
+
return self.add_or_get_labware_core(labware_id)
|
|
1034
|
+
|
|
1035
|
+
def get_slot_center(self, slot_name: Union[DeckSlotName, StagingSlotName]) -> Point:
|
|
1036
|
+
"""Get the absolute coordinate of a slot's center."""
|
|
1037
|
+
return self._engine_client.state.addressable_areas.get_addressable_area_center(
|
|
1038
|
+
slot_name.id
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
def get_highest_z(self) -> float:
|
|
1042
|
+
"""Get the highest Z point of all deck items."""
|
|
1043
|
+
return self._engine_client.state.geometry.get_all_obstacle_highest_z()
|
|
1044
|
+
|
|
1045
|
+
def get_labware_cores(self) -> List[LabwareCore]:
|
|
1046
|
+
"""Get all loaded labware cores."""
|
|
1047
|
+
return list(self._labware_cores_by_id.values())
|
|
1048
|
+
|
|
1049
|
+
def get_module_cores(self) -> List[Union[ModuleCore, NonConnectedModuleCore]]:
|
|
1050
|
+
"""Get all loaded module cores."""
|
|
1051
|
+
return list(self._module_cores_by_id.values())
|
|
1052
|
+
|
|
1053
|
+
def define_liquid(
|
|
1054
|
+
self,
|
|
1055
|
+
name: str,
|
|
1056
|
+
description: Optional[str],
|
|
1057
|
+
display_color: Optional[str],
|
|
1058
|
+
) -> Liquid:
|
|
1059
|
+
"""Define a liquid to load into a well."""
|
|
1060
|
+
liquid = self._engine_client.add_liquid(
|
|
1061
|
+
name=name, description=description, color=display_color
|
|
1062
|
+
)
|
|
1063
|
+
|
|
1064
|
+
return Liquid(
|
|
1065
|
+
_id=liquid.id,
|
|
1066
|
+
name=liquid.displayName,
|
|
1067
|
+
description=liquid.description,
|
|
1068
|
+
display_color=(liquid.displayColor.root if liquid.displayColor else None),
|
|
1069
|
+
)
|
|
1070
|
+
|
|
1071
|
+
def get_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
1072
|
+
"""Get an instance of a built-in liquid class."""
|
|
1073
|
+
try:
|
|
1074
|
+
# Check if we have already loaded this liquid class' definition
|
|
1075
|
+
liquid_class_def = self._liquid_class_def_cache[(name, version)]
|
|
1076
|
+
except KeyError:
|
|
1077
|
+
try:
|
|
1078
|
+
# Fetching the liquid class data from file and parsing it
|
|
1079
|
+
# is an expensive operation and should be avoided.
|
|
1080
|
+
# Calling this often will degrade protocol execution performance.
|
|
1081
|
+
liquid_class_def = liquid_classes.load_definition(name, version=version)
|
|
1082
|
+
self._liquid_class_def_cache[(name, version)] = liquid_class_def
|
|
1083
|
+
except LiquidClassDefinitionDoesNotExist:
|
|
1084
|
+
raise ValueError(
|
|
1085
|
+
f"Liquid class definition not found for '{name}' version {version}."
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
return LiquidClass.create(liquid_class_def)
|
|
1089
|
+
|
|
1090
|
+
def get_labware_location(
|
|
1091
|
+
self, labware_core: LabwareCore
|
|
1092
|
+
) -> Union[str, LabwareCore, ModuleCore, NonConnectedModuleCore, OffDeckType]:
|
|
1093
|
+
"""Get labware parent location."""
|
|
1094
|
+
labware_location = self._engine_client.state.labware.get_location(
|
|
1095
|
+
labware_core.labware_id
|
|
1096
|
+
)
|
|
1097
|
+
if isinstance(labware_location, DeckSlotLocation):
|
|
1098
|
+
return validation.internal_slot_to_public_string(
|
|
1099
|
+
labware_location.slotName, self._engine_client.state.config.robot_type
|
|
1100
|
+
)
|
|
1101
|
+
elif isinstance(labware_location, AddressableAreaLocation):
|
|
1102
|
+
# This will only ever be a robot accurate deck slot name or Flex staging slot name
|
|
1103
|
+
return labware_location.addressableAreaName
|
|
1104
|
+
elif isinstance(labware_location, ModuleLocation):
|
|
1105
|
+
return self._module_cores_by_id[labware_location.moduleId]
|
|
1106
|
+
elif isinstance(labware_location, OnLabwareLocation):
|
|
1107
|
+
return self._labware_cores_by_id[labware_location.labwareId]
|
|
1108
|
+
|
|
1109
|
+
return OffDeckType.OFF_DECK
|
|
1110
|
+
|
|
1111
|
+
def _convert_labware_location(
|
|
1112
|
+
self,
|
|
1113
|
+
location: Union[
|
|
1114
|
+
DeckSlotName,
|
|
1115
|
+
StagingSlotName,
|
|
1116
|
+
LabwareCore,
|
|
1117
|
+
ModuleCore,
|
|
1118
|
+
NonConnectedModuleCore,
|
|
1119
|
+
OffDeckType,
|
|
1120
|
+
WasteChute,
|
|
1121
|
+
TrashBin,
|
|
1122
|
+
],
|
|
1123
|
+
) -> LoadableLabwareLocation:
|
|
1124
|
+
if isinstance(location, LabwareCore):
|
|
1125
|
+
return OnLabwareLocation(labwareId=location.labware_id)
|
|
1126
|
+
else:
|
|
1127
|
+
return self._get_non_stacked_location(location)
|
|
1128
|
+
|
|
1129
|
+
@staticmethod
|
|
1130
|
+
def _get_non_stacked_location(
|
|
1131
|
+
location: Union[
|
|
1132
|
+
DeckSlotName,
|
|
1133
|
+
StagingSlotName,
|
|
1134
|
+
ModuleCore,
|
|
1135
|
+
NonConnectedModuleCore,
|
|
1136
|
+
OffDeckType,
|
|
1137
|
+
WasteChute,
|
|
1138
|
+
TrashBin,
|
|
1139
|
+
],
|
|
1140
|
+
) -> NonStackedLocation:
|
|
1141
|
+
if isinstance(location, (ModuleCore, NonConnectedModuleCore)):
|
|
1142
|
+
return ModuleLocation(moduleId=location.module_id)
|
|
1143
|
+
elif location is OffDeckType.OFF_DECK:
|
|
1144
|
+
return OFF_DECK_LOCATION
|
|
1145
|
+
elif isinstance(location, DeckSlotName):
|
|
1146
|
+
return DeckSlotLocation(slotName=location)
|
|
1147
|
+
elif isinstance(location, StagingSlotName):
|
|
1148
|
+
return AddressableAreaLocation(addressableAreaName=location.id)
|
|
1149
|
+
elif isinstance(location, WasteChute):
|
|
1150
|
+
# TODO(mm, 2023-12-06) This will need to determine the appropriate Waste Chute to return, but only move_labware uses this for now
|
|
1151
|
+
return AddressableAreaLocation(addressableAreaName="gripperWasteChute")
|
|
1152
|
+
elif isinstance(location, TrashBin):
|
|
1153
|
+
return AddressableAreaLocation(addressableAreaName=location.area_name)
|