opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.0a0__py2.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.
- opentrons/cli/analyze.py +71 -7
- opentrons/config/__init__.py +9 -0
- opentrons/config/advanced_settings.py +22 -0
- opentrons/config/defaults_ot3.py +14 -36
- opentrons/config/feature_flags.py +4 -0
- opentrons/config/types.py +6 -17
- opentrons/drivers/absorbance_reader/abstract.py +27 -3
- opentrons/drivers/absorbance_reader/async_byonoy.py +207 -154
- opentrons/drivers/absorbance_reader/driver.py +24 -15
- opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
- opentrons/drivers/absorbance_reader/simulator.py +32 -6
- opentrons/drivers/types.py +23 -1
- opentrons/execute.py +2 -2
- opentrons/hardware_control/api.py +18 -10
- opentrons/hardware_control/backends/controller.py +3 -2
- opentrons/hardware_control/backends/flex_protocol.py +11 -5
- opentrons/hardware_control/backends/ot3controller.py +18 -50
- opentrons/hardware_control/backends/ot3simulator.py +7 -6
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
- opentrons/hardware_control/module_control.py +43 -2
- opentrons/hardware_control/modules/__init__.py +7 -1
- opentrons/hardware_control/modules/absorbance_reader.py +230 -83
- opentrons/hardware_control/modules/errors.py +7 -0
- opentrons/hardware_control/modules/heater_shaker.py +8 -3
- opentrons/hardware_control/modules/magdeck.py +12 -3
- opentrons/hardware_control/modules/mod_abc.py +27 -2
- opentrons/hardware_control/modules/tempdeck.py +15 -7
- opentrons/hardware_control/modules/thermocycler.py +69 -3
- opentrons/hardware_control/modules/types.py +11 -5
- opentrons/hardware_control/modules/update.py +11 -5
- opentrons/hardware_control/modules/utils.py +3 -1
- opentrons/hardware_control/ot3_calibration.py +6 -6
- opentrons/hardware_control/ot3api.py +126 -89
- opentrons/hardware_control/poller.py +15 -11
- opentrons/hardware_control/protocols/__init__.py +1 -7
- opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
- opentrons/hardware_control/protocols/liquid_handler.py +5 -0
- opentrons/motion_planning/__init__.py +2 -0
- opentrons/motion_planning/waypoints.py +32 -0
- opentrons/protocol_api/__init__.py +2 -1
- opentrons/protocol_api/_liquid.py +87 -1
- opentrons/protocol_api/_parameter_context.py +10 -1
- opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
- opentrons/protocol_api/core/engine/instrument.py +29 -25
- opentrons/protocol_api/core/engine/labware.py +10 -2
- opentrons/protocol_api/core/engine/module_core.py +129 -17
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +355 -0
- opentrons/protocol_api/core/engine/protocol.py +55 -2
- opentrons/protocol_api/core/instrument.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +5 -2
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
- opentrons/protocol_api/core/module.py +22 -4
- opentrons/protocol_api/core/protocol.py +5 -2
- opentrons/protocol_api/instrument_context.py +52 -20
- opentrons/protocol_api/labware.py +13 -1
- opentrons/protocol_api/module_contexts.py +68 -13
- opentrons/protocol_api/protocol_context.py +38 -4
- opentrons/protocol_api/validation.py +5 -3
- opentrons/protocol_engine/__init__.py +10 -9
- opentrons/protocol_engine/actions/__init__.py +5 -0
- opentrons/protocol_engine/actions/actions.py +42 -25
- opentrons/protocol_engine/actions/get_state_update.py +38 -0
- opentrons/protocol_engine/clients/sync_client.py +7 -1
- opentrons/protocol_engine/clients/transports.py +1 -1
- opentrons/protocol_engine/commands/__init__.py +0 -4
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +161 -0
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +53 -9
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +160 -0
- opentrons/protocol_engine/commands/absorbance_reader/read.py +196 -0
- opentrons/protocol_engine/commands/aspirate.py +29 -16
- opentrons/protocol_engine/commands/aspirate_in_place.py +32 -15
- opentrons/protocol_engine/commands/blow_out.py +63 -14
- opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
- opentrons/protocol_engine/commands/command.py +28 -17
- opentrons/protocol_engine/commands/command_unions.py +37 -24
- opentrons/protocol_engine/commands/comment.py +5 -3
- opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
- opentrons/protocol_engine/commands/custom.py +5 -3
- opentrons/protocol_engine/commands/dispense.py +42 -20
- opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
- opentrons/protocol_engine/commands/drop_tip.py +68 -15
- opentrons/protocol_engine/commands/drop_tip_in_place.py +52 -11
- opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/home.py +11 -5
- opentrons/protocol_engine/commands/liquid_probe.py +146 -88
- opentrons/protocol_engine/commands/load_labware.py +19 -5
- opentrons/protocol_engine/commands/load_liquid.py +18 -7
- opentrons/protocol_engine/commands/load_module.py +43 -6
- opentrons/protocol_engine/commands/load_pipette.py +18 -17
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
- opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
- opentrons/protocol_engine/commands/move_labware.py +106 -19
- opentrons/protocol_engine/commands/move_relative.py +15 -3
- opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
- opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
- opentrons/protocol_engine/commands/move_to_well.py +37 -10
- opentrons/protocol_engine/commands/pick_up_tip.py +50 -29
- opentrons/protocol_engine/commands/pipetting_common.py +39 -15
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
- opentrons/protocol_engine/commands/reload_labware.py +13 -4
- opentrons/protocol_engine/commands/retract_axis.py +6 -3
- opentrons/protocol_engine/commands/save_position.py +2 -3
- opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
- opentrons/protocol_engine/commands/set_status_bar.py +5 -3
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
- opentrons/protocol_engine/commands/touch_tip.py +19 -7
- opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +194 -0
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +75 -0
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -3
- opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
- opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
- opentrons/protocol_engine/create_protocol_engine.py +41 -8
- opentrons/protocol_engine/engine_support.py +2 -1
- opentrons/protocol_engine/error_recovery_policy.py +14 -3
- opentrons/protocol_engine/errors/__init__.py +18 -0
- opentrons/protocol_engine/errors/exceptions.py +114 -2
- opentrons/protocol_engine/execution/__init__.py +2 -0
- opentrons/protocol_engine/execution/command_executor.py +22 -13
- opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
- opentrons/protocol_engine/execution/door_watcher.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +2 -1
- opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
- opentrons/protocol_engine/execution/gantry_mover.py +4 -2
- opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
- opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
- opentrons/protocol_engine/execution/labware_movement.py +6 -3
- opentrons/protocol_engine/execution/movement.py +8 -3
- opentrons/protocol_engine/execution/pipetting.py +7 -4
- opentrons/protocol_engine/execution/queue_worker.py +6 -2
- opentrons/protocol_engine/execution/run_control.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
- opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
- opentrons/protocol_engine/execution/tip_handler.py +77 -43
- opentrons/protocol_engine/notes/__init__.py +14 -2
- opentrons/protocol_engine/notes/notes.py +18 -1
- opentrons/protocol_engine/plugins.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +54 -31
- opentrons/protocol_engine/resources/__init__.py +2 -0
- opentrons/protocol_engine/resources/deck_data_provider.py +58 -5
- opentrons/protocol_engine/resources/file_provider.py +157 -0
- opentrons/protocol_engine/resources/fixture_validation.py +5 -0
- opentrons/protocol_engine/resources/labware_validation.py +10 -0
- opentrons/protocol_engine/state/__init__.py +0 -70
- opentrons/protocol_engine/state/addressable_areas.py +1 -1
- opentrons/protocol_engine/state/command_history.py +21 -2
- opentrons/protocol_engine/state/commands.py +110 -31
- opentrons/protocol_engine/state/files.py +59 -0
- opentrons/protocol_engine/state/frustum_helpers.py +440 -0
- opentrons/protocol_engine/state/geometry.py +359 -15
- opentrons/protocol_engine/state/labware.py +166 -63
- opentrons/protocol_engine/state/liquids.py +1 -1
- opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +19 -3
- opentrons/protocol_engine/state/modules.py +167 -85
- opentrons/protocol_engine/state/motion.py +16 -9
- opentrons/protocol_engine/state/pipettes.py +157 -317
- opentrons/protocol_engine/state/state.py +30 -1
- opentrons/protocol_engine/state/state_summary.py +3 -0
- opentrons/protocol_engine/state/tips.py +69 -114
- opentrons/protocol_engine/state/update_types.py +408 -0
- opentrons/protocol_engine/state/wells.py +236 -0
- opentrons/protocol_engine/types.py +90 -0
- opentrons/protocol_reader/file_format_validator.py +83 -15
- opentrons/protocol_runner/json_translator.py +21 -5
- opentrons/protocol_runner/legacy_command_mapper.py +27 -6
- opentrons/protocol_runner/legacy_context_plugin.py +27 -71
- opentrons/protocol_runner/protocol_runner.py +6 -3
- opentrons/protocol_runner/run_orchestrator.py +26 -6
- opentrons/protocols/advanced_control/mix.py +3 -5
- opentrons/protocols/advanced_control/transfers.py +125 -56
- opentrons/protocols/api_support/constants.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/labware_like.py +4 -4
- opentrons/protocols/api_support/tip_tracker.py +2 -2
- opentrons/protocols/api_support/types.py +15 -2
- opentrons/protocols/api_support/util.py +30 -42
- opentrons/protocols/duration/errors.py +1 -1
- opentrons/protocols/duration/estimator.py +50 -29
- opentrons/protocols/execution/dev_types.py +2 -2
- opentrons/protocols/execution/execute_json_v4.py +15 -10
- opentrons/protocols/execution/execute_python.py +8 -3
- opentrons/protocols/geometry/planning.py +12 -12
- opentrons/protocols/labware.py +17 -33
- opentrons/simulate.py +3 -3
- opentrons/types.py +30 -3
- opentrons/util/logging_config.py +34 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +5 -4
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +227 -215
- opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
- opentrons/protocol_engine/commands/configuring_common.py +0 -26
- opentrons/protocol_runner/thread_async_queue.py +0 -174
- /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
- /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
|
@@ -143,7 +143,8 @@ from .backends.types import HWStopCondition
|
|
|
143
143
|
from .backends.flex_protocol import FlexBackend
|
|
144
144
|
from .backends.ot3simulator import OT3Simulator
|
|
145
145
|
from .backends.errors import SubsystemUpdating
|
|
146
|
-
|
|
146
|
+
from opentrons_hardware.firmware_bindings.constants import SensorId
|
|
147
|
+
from opentrons_hardware.sensors.types import SensorDataType
|
|
147
148
|
|
|
148
149
|
mod_log = logging.getLogger(__name__)
|
|
149
150
|
|
|
@@ -164,6 +165,7 @@ def _adjust_high_throughput_z_current(func: Wrapped) -> Wrapped:
|
|
|
164
165
|
A decorator that temproarily and conditionally changes the active current (based on the axis input)
|
|
165
166
|
before a function is executed and the cleans up afterwards
|
|
166
167
|
"""
|
|
168
|
+
|
|
167
169
|
# only home and retract should be wrappeed by this decorator
|
|
168
170
|
@wraps(func)
|
|
169
171
|
async def wrapper(self: Any, axis: Axis, *args: Any, **kwargs: Any) -> Any:
|
|
@@ -306,6 +308,12 @@ class OT3API(
|
|
|
306
308
|
async with self._backend.restore_system_constraints():
|
|
307
309
|
yield
|
|
308
310
|
|
|
311
|
+
@contextlib.asynccontextmanager
|
|
312
|
+
async def grab_pressure(self, mount: OT3Mount) -> AsyncIterator[None]:
|
|
313
|
+
instrument = self._pipette_handler.get_pipette(mount)
|
|
314
|
+
async with self._backend.grab_pressure(instrument.channels, mount):
|
|
315
|
+
yield
|
|
316
|
+
|
|
309
317
|
def _update_door_state(self, door_state: DoorState) -> None:
|
|
310
318
|
mod_log.info(f"Updating the window switch status: {door_state}")
|
|
311
319
|
self.door_state = door_state
|
|
@@ -933,7 +941,6 @@ class OT3API(
|
|
|
933
941
|
current_pos_float > self._config.safe_home_distance
|
|
934
942
|
and current_pos_float < max_distance
|
|
935
943
|
):
|
|
936
|
-
|
|
937
944
|
# move toward home until a safe distance
|
|
938
945
|
await self._backend.tip_action(
|
|
939
946
|
origin={Axis.Q: current_pos_float},
|
|
@@ -1811,7 +1818,8 @@ class OT3API(
|
|
|
1811
1818
|
increment: Optional[float] = None,
|
|
1812
1819
|
) -> None:
|
|
1813
1820
|
"""This is a slightly more barebones variation of pick_up_tip. This is only the motor routine
|
|
1814
|
-
directly involved in tip pickup, and leaves any state updates and plunger moves to the caller.
|
|
1821
|
+
directly involved in tip pickup, and leaves any state updates and plunger moves to the caller.
|
|
1822
|
+
"""
|
|
1815
1823
|
realmount = OT3Mount.from_mount(mount)
|
|
1816
1824
|
instrument = self._pipette_handler.get_pipette(realmount)
|
|
1817
1825
|
|
|
@@ -1878,12 +1886,10 @@ class OT3API(
|
|
|
1878
1886
|
|
|
1879
1887
|
With wet tips, the primary concern is leftover droplets inside the tip.
|
|
1880
1888
|
These droplets ideally only move down and out of the tip, not up into the tip.
|
|
1881
|
-
Therefore, it is preferable to use the "
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
Assume all tips are wet, because we do not differentiate between wet/dry tips.
|
|
1889
|
+
Therefore, it is preferable to use the slower "aspirate" speed when
|
|
1890
|
+
moving the plunger up after a blow-out.
|
|
1885
1891
|
|
|
1886
|
-
|
|
1892
|
+
All other situations, moving at the max speed is preferable, to save time.
|
|
1887
1893
|
"""
|
|
1888
1894
|
checked_mount = OT3Mount.from_mount(mount)
|
|
1889
1895
|
instrument = self._pipette_handler.get_pipette(checked_mount)
|
|
@@ -1896,21 +1902,22 @@ class OT3API(
|
|
|
1896
1902
|
self._current_position,
|
|
1897
1903
|
)
|
|
1898
1904
|
pip_ax = Axis.of_main_tool_actuator(checked_mount)
|
|
1899
|
-
#
|
|
1900
|
-
|
|
1905
|
+
# save time while moving down by using max speed
|
|
1906
|
+
max_speeds = self.config.motion_settings.default_max_speed
|
|
1907
|
+
speed_down = max_speeds[self.gantry_load][OT3AxisKind.P]
|
|
1908
|
+
# upward moves can be max speed, or aspirate speed
|
|
1909
|
+
# use the (slower) aspirate if there is a tip and we're following a blow-out
|
|
1910
|
+
plunger_is_below_bottom_pos = (
|
|
1911
|
+
self._current_position[pip_ax] > instrument.plunger_positions.bottom
|
|
1912
|
+
)
|
|
1913
|
+
if instrument.has_tip_length and plunger_is_below_bottom_pos:
|
|
1901
1914
|
# using slower aspirate flow-rate, to avoid pulling droplets up
|
|
1902
1915
|
speed_up = self._pipette_handler.plunger_speed(
|
|
1903
1916
|
instrument, instrument.aspirate_flow_rate, "aspirate"
|
|
1904
1917
|
)
|
|
1905
|
-
# use blow-out flow-rate, so we can push droplets out
|
|
1906
|
-
speed_down = self._pipette_handler.plunger_speed(
|
|
1907
|
-
instrument, instrument.blow_out_flow_rate, "dispense"
|
|
1908
|
-
)
|
|
1909
1918
|
else:
|
|
1910
|
-
#
|
|
1911
|
-
max_speeds = self.config.motion_settings.default_max_speed
|
|
1919
|
+
# either no tip, or plunger just homed, so tip is dry
|
|
1912
1920
|
speed_up = max_speeds[self.gantry_load][OT3AxisKind.P]
|
|
1913
|
-
speed_down = speed_up
|
|
1914
1921
|
# IMPORTANT: Here is our backlash compensation.
|
|
1915
1922
|
# The plunger is pre-loaded in the "aspirate" direction
|
|
1916
1923
|
backlash_pos = target_pos.copy()
|
|
@@ -2229,15 +2236,6 @@ class OT3API(
|
|
|
2229
2236
|
)
|
|
2230
2237
|
await self.home_gear_motors()
|
|
2231
2238
|
|
|
2232
|
-
def cache_tip(
|
|
2233
|
-
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
|
|
2234
|
-
) -> None:
|
|
2235
|
-
realmount = OT3Mount.from_mount(mount)
|
|
2236
|
-
instrument = self._pipette_handler.get_pipette(realmount)
|
|
2237
|
-
|
|
2238
|
-
instrument.add_tip(tip_length=tip_length)
|
|
2239
|
-
instrument.set_current_volume(0)
|
|
2240
|
-
|
|
2241
2239
|
async def pick_up_tip(
|
|
2242
2240
|
self,
|
|
2243
2241
|
mount: Union[top_types.Mount, OT3Mount],
|
|
@@ -2283,17 +2281,10 @@ class OT3API(
|
|
|
2283
2281
|
)
|
|
2284
2282
|
instrument.working_volume = tip_volume
|
|
2285
2283
|
|
|
2286
|
-
async def
|
|
2284
|
+
async def tip_drop_moves(
|
|
2287
2285
|
self, mount: Union[top_types.Mount, OT3Mount], home_after: bool = False
|
|
2288
2286
|
) -> None:
|
|
2289
|
-
"""Drop tip at the current location."""
|
|
2290
2287
|
realmount = OT3Mount.from_mount(mount)
|
|
2291
|
-
instrument = self._pipette_handler.get_pipette(realmount)
|
|
2292
|
-
|
|
2293
|
-
def _remove_tips() -> None:
|
|
2294
|
-
instrument.set_current_volume(0)
|
|
2295
|
-
instrument.current_tiprack_diameter = 0.0
|
|
2296
|
-
instrument.remove_tip()
|
|
2297
2288
|
|
|
2298
2289
|
await self._move_to_plunger_bottom(realmount, rate=1.0, check_current_vol=False)
|
|
2299
2290
|
|
|
@@ -2320,11 +2311,27 @@ class OT3API(
|
|
|
2320
2311
|
if home_after:
|
|
2321
2312
|
await self._home([Axis.by_mount(mount)])
|
|
2322
2313
|
|
|
2323
|
-
|
|
2324
|
-
# call this in case we're simulating
|
|
2314
|
+
# call this in case we're simulating:
|
|
2325
2315
|
if isinstance(self._backend, OT3Simulator):
|
|
2326
2316
|
self._backend._update_tip_state(realmount, False)
|
|
2327
2317
|
|
|
2318
|
+
async def drop_tip(
|
|
2319
|
+
self, mount: Union[top_types.Mount, OT3Mount], home_after: bool = False
|
|
2320
|
+
) -> None:
|
|
2321
|
+
"""Drop tip at the current location."""
|
|
2322
|
+
await self.tip_drop_moves(mount=mount, home_after=home_after)
|
|
2323
|
+
|
|
2324
|
+
# todo(mm, 2024-10-17): Ideally, callers would be able to replicate the behavior
|
|
2325
|
+
# of this method via self.drop_tip_moves() plus other public methods. This
|
|
2326
|
+
# currently prevents that: there is no public equivalent for
|
|
2327
|
+
# instrument.set_current_volume().
|
|
2328
|
+
realmount = OT3Mount.from_mount(mount)
|
|
2329
|
+
instrument = self._pipette_handler.get_pipette(realmount)
|
|
2330
|
+
instrument.set_current_volume(0)
|
|
2331
|
+
|
|
2332
|
+
self.set_current_tiprack_diameter(mount, 0.0)
|
|
2333
|
+
self.remove_tip(mount)
|
|
2334
|
+
|
|
2328
2335
|
async def clean_up(self) -> None:
|
|
2329
2336
|
"""Get the API ready to stop cleanly."""
|
|
2330
2337
|
await self._backend.clean_up()
|
|
@@ -2592,13 +2599,18 @@ class OT3API(
|
|
|
2592
2599
|
starting_nozzle,
|
|
2593
2600
|
)
|
|
2594
2601
|
|
|
2595
|
-
|
|
2602
|
+
def add_tip(
|
|
2603
|
+
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
|
|
2604
|
+
) -> None:
|
|
2605
|
+
self._pipette_handler.add_tip(OT3Mount.from_mount(mount), tip_length)
|
|
2606
|
+
|
|
2607
|
+
def cache_tip(
|
|
2596
2608
|
self, mount: Union[top_types.Mount, OT3Mount], tip_length: float
|
|
2597
2609
|
) -> None:
|
|
2598
|
-
|
|
2610
|
+
self._pipette_handler.cache_tip(OT3Mount.from_mount(mount), tip_length)
|
|
2599
2611
|
|
|
2600
|
-
|
|
2601
|
-
|
|
2612
|
+
def remove_tip(self, mount: Union[top_types.Mount, OT3Mount]) -> None:
|
|
2613
|
+
self._pipette_handler.remove_tip(OT3Mount.from_mount(mount))
|
|
2602
2614
|
|
|
2603
2615
|
def add_gripper_probe(self, probe: GripperProbe) -> None:
|
|
2604
2616
|
self._gripper_handler.add_probe(probe)
|
|
@@ -2607,17 +2619,17 @@ class OT3API(
|
|
|
2607
2619
|
self._gripper_handler.remove_probe()
|
|
2608
2620
|
|
|
2609
2621
|
@staticmethod
|
|
2610
|
-
def liquid_probe_non_responsive_z_distance(
|
|
2622
|
+
def liquid_probe_non_responsive_z_distance(
|
|
2623
|
+
z_speed: float, samples_for_baselining: int, sample_time_sec: float
|
|
2624
|
+
) -> float:
|
|
2611
2625
|
"""Calculate the Z distance travelled where the LLD pass will be unresponsive."""
|
|
2612
2626
|
# NOTE: (sigler) Here lye some magic numbers.
|
|
2613
2627
|
# The Z axis probing motion uses the first 20 samples to calculate
|
|
2614
2628
|
# a baseline for all following samples, making the very beginning of
|
|
2615
2629
|
# that Z motion unable to detect liquid. The sensor is configured for
|
|
2616
2630
|
# 4ms sample readings, and so we then assume it takes ~80ms to complete.
|
|
2617
|
-
# If the Z is moving at 5mm/sec, then ~80ms equates to ~0.
|
|
2618
|
-
|
|
2619
|
-
sample_time_sec = 0.004 # FIXME: (sigler) shouldn't be defined here?
|
|
2620
|
-
baseline_duration_sec = baseline_during_z_sample_num * sample_time_sec
|
|
2631
|
+
# If the Z is moving at 5mm/sec, then ~80ms equates to ~0.4mm
|
|
2632
|
+
baseline_duration_sec = samples_for_baselining * sample_time_sec
|
|
2621
2633
|
non_responsive_z_mm = baseline_duration_sec * z_speed
|
|
2622
2634
|
return non_responsive_z_mm
|
|
2623
2635
|
|
|
@@ -2628,6 +2640,9 @@ class OT3API(
|
|
|
2628
2640
|
probe: InstrumentProbeType,
|
|
2629
2641
|
p_travel: float,
|
|
2630
2642
|
force_both_sensors: bool = False,
|
|
2643
|
+
response_queue: Optional[
|
|
2644
|
+
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
2645
|
+
] = None,
|
|
2631
2646
|
) -> float:
|
|
2632
2647
|
plunger_direction = -1 if probe_settings.aspirate_while_sensing else 1
|
|
2633
2648
|
end_z = await self._backend.liquid_probe(
|
|
@@ -2637,10 +2652,10 @@ class OT3API(
|
|
|
2637
2652
|
(probe_settings.plunger_speed * plunger_direction),
|
|
2638
2653
|
probe_settings.sensor_threshold_pascals,
|
|
2639
2654
|
probe_settings.plunger_impulse_time,
|
|
2640
|
-
probe_settings.
|
|
2641
|
-
probe_settings.data_files,
|
|
2655
|
+
probe_settings.samples_for_baselining,
|
|
2642
2656
|
probe=probe,
|
|
2643
2657
|
force_both_sensors=force_both_sensors,
|
|
2658
|
+
response_queue=response_queue,
|
|
2644
2659
|
)
|
|
2645
2660
|
machine_pos = await self._backend.update_position()
|
|
2646
2661
|
machine_pos[Axis.by_mount(mount)] = end_z
|
|
@@ -2654,13 +2669,16 @@ class OT3API(
|
|
|
2654
2669
|
cp = self.critical_point_for(mount, None)
|
|
2655
2670
|
return deck_end_z + offset.z + cp.z
|
|
2656
2671
|
|
|
2657
|
-
async def liquid_probe(
|
|
2672
|
+
async def liquid_probe( # noqa: C901
|
|
2658
2673
|
self,
|
|
2659
2674
|
mount: Union[top_types.Mount, OT3Mount],
|
|
2660
2675
|
max_z_dist: float,
|
|
2661
2676
|
probe_settings: Optional[LiquidProbeSettings] = None,
|
|
2662
2677
|
probe: Optional[InstrumentProbeType] = None,
|
|
2663
2678
|
force_both_sensors: bool = False,
|
|
2679
|
+
response_queue: Optional[
|
|
2680
|
+
asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
|
|
2681
|
+
] = None,
|
|
2664
2682
|
) -> float:
|
|
2665
2683
|
"""Search for and return liquid level height.
|
|
2666
2684
|
|
|
@@ -2697,7 +2715,6 @@ class OT3API(
|
|
|
2697
2715
|
probe_settings = deepcopy(self.config.liquid_sense)
|
|
2698
2716
|
|
|
2699
2717
|
# We need to significatly slow down the 96 channel liquid probe
|
|
2700
|
-
# TODO: (sigler) add LLD plunger-speed to pipette definitions
|
|
2701
2718
|
if self.gantry_load == GantryLoad.HIGH_THROUGHPUT:
|
|
2702
2719
|
max_plunger_speed = self.config.motion_settings.max_speed_discontinuity[
|
|
2703
2720
|
GantryLoad.HIGH_THROUGHPUT
|
|
@@ -2706,59 +2723,79 @@ class OT3API(
|
|
|
2706
2723
|
max_plunger_speed, probe_settings.plunger_speed
|
|
2707
2724
|
)
|
|
2708
2725
|
|
|
2709
|
-
|
|
2726
|
+
starting_position = await self.gantry_position(checked_mount, refresh=True)
|
|
2710
2727
|
|
|
2711
|
-
|
|
2712
|
-
# FIXME: logic for how plunger moves is divided between here and tool_sensors.py
|
|
2713
|
-
p_impulse_mm = (
|
|
2728
|
+
sensor_baseline_plunger_move_mm = (
|
|
2714
2729
|
probe_settings.plunger_impulse_time * probe_settings.plunger_speed
|
|
2715
2730
|
)
|
|
2716
|
-
|
|
2731
|
+
total_plunger_axis_mm = (
|
|
2717
2732
|
instrument.plunger_positions.bottom - instrument.plunger_positions.top
|
|
2718
2733
|
)
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2734
|
+
max_allowed_plunger_distance_mm = total_plunger_axis_mm - (
|
|
2735
|
+
instrument.backlash_distance + sensor_baseline_plunger_move_mm
|
|
2736
|
+
)
|
|
2722
2737
|
# height where probe action will begin
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
probe_settings.
|
|
2738
|
+
sensor_baseline_z_move_mm = OT3API.liquid_probe_non_responsive_z_distance(
|
|
2739
|
+
probe_settings.mount_speed,
|
|
2740
|
+
probe_settings.samples_for_baselining,
|
|
2741
|
+
probe_settings.sample_time_sec,
|
|
2742
|
+
)
|
|
2743
|
+
z_offset_per_pass = (
|
|
2744
|
+
sensor_baseline_z_move_mm + probe_settings.z_overlap_between_passes_mm
|
|
2727
2745
|
)
|
|
2728
|
-
probe_pass_z_offset_mm = non_responsive_z_mm + probe_pass_overlap_mm
|
|
2729
2746
|
|
|
2730
2747
|
# height that is considered safe to reset the plunger without disturbing liquid
|
|
2731
2748
|
# this usually needs to at least 1-2mm from liquid, to avoid splashes from air
|
|
2732
|
-
|
|
2733
|
-
|
|
2749
|
+
z_offset_for_plunger_prep = max(
|
|
2750
|
+
probe_settings.plunger_reset_offset, z_offset_per_pass
|
|
2751
|
+
)
|
|
2752
|
+
|
|
2753
|
+
async def prep_plunger_for_probe_move(
|
|
2754
|
+
position: top_types.Point, aspirate_while_sensing: bool
|
|
2755
|
+
) -> None:
|
|
2756
|
+
# safe distance so we don't accidentally aspirate liquid if we're already close to liquid
|
|
2757
|
+
mount_pos_for_plunger_prep = top_types.Point(
|
|
2758
|
+
position.x,
|
|
2759
|
+
position.y,
|
|
2760
|
+
position.z + z_offset_for_plunger_prep,
|
|
2761
|
+
)
|
|
2762
|
+
# Prep the plunger
|
|
2763
|
+
await self.move_to(checked_mount, mount_pos_for_plunger_prep)
|
|
2764
|
+
if aspirate_while_sensing:
|
|
2765
|
+
await self._move_to_plunger_bottom(checked_mount, rate=1)
|
|
2766
|
+
else:
|
|
2767
|
+
await self._move_to_plunger_top_for_liquid_probe(checked_mount, rate=1)
|
|
2734
2768
|
|
|
2735
2769
|
error: Optional[PipetteLiquidNotFoundError] = None
|
|
2736
|
-
|
|
2737
|
-
#
|
|
2770
|
+
current_position = await self.gantry_position(checked_mount, refresh=True)
|
|
2771
|
+
# starting_position.z + z_distance of pass - pos.z should be < max_z_dist
|
|
2738
2772
|
# due to rounding errors this can get caught in an infinite loop when the distance is almost equal
|
|
2739
2773
|
# so we check to see if they're within 0.01 which is 1/5th the minimum movement distance from move_utils.py
|
|
2740
|
-
while (
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2774
|
+
while (starting_position.z - current_position.z) < (max_z_dist - 0.01):
|
|
2775
|
+
await prep_plunger_for_probe_move(
|
|
2776
|
+
position=current_position,
|
|
2777
|
+
aspirate_while_sensing=probe_settings.aspirate_while_sensing,
|
|
2744
2778
|
)
|
|
2779
|
+
|
|
2745
2780
|
# overlap amount we want to use between passes
|
|
2746
2781
|
pass_start_pos = top_types.Point(
|
|
2747
|
-
|
|
2782
|
+
current_position.x,
|
|
2783
|
+
current_position.y,
|
|
2784
|
+
current_position.z + z_offset_per_pass,
|
|
2748
2785
|
)
|
|
2749
|
-
max_z_time = (
|
|
2750
|
-
max_z_dist - probe_start_pos.z + pass_start_pos.z
|
|
2751
|
-
) / probe_settings.mount_speed
|
|
2752
|
-
p_travel_required_for_z = max_z_time * probe_settings.plunger_speed
|
|
2753
|
-
p_pass_travel = min(p_travel_required_for_z, p_working_mm)
|
|
2754
|
-
# Prep the plunger
|
|
2755
|
-
await self.move_to(checked_mount, safe_plunger_pos)
|
|
2756
|
-
if probe_settings.aspirate_while_sensing:
|
|
2757
|
-
# TODO(cm, 7/8/24): remove p_prep_speed from the rate at some point
|
|
2758
|
-
await self._move_to_plunger_bottom(checked_mount, rate=1)
|
|
2759
|
-
else:
|
|
2760
|
-
await self._move_to_plunger_top_for_liquid_probe(checked_mount, rate=1)
|
|
2761
2786
|
|
|
2787
|
+
total_remaining_z_dist = pass_start_pos.z - (
|
|
2788
|
+
starting_position.z - max_z_dist
|
|
2789
|
+
)
|
|
2790
|
+
finish_probe_move_duration = (
|
|
2791
|
+
total_remaining_z_dist / probe_settings.mount_speed
|
|
2792
|
+
)
|
|
2793
|
+
finish_probe_plunger_distance_mm = (
|
|
2794
|
+
finish_probe_move_duration * probe_settings.plunger_speed
|
|
2795
|
+
)
|
|
2796
|
+
plunger_travel_mm = min(
|
|
2797
|
+
finish_probe_plunger_distance_mm, max_allowed_plunger_distance_mm
|
|
2798
|
+
)
|
|
2762
2799
|
try:
|
|
2763
2800
|
# move to where we want to start a pass and run a pass
|
|
2764
2801
|
await self.move_to(checked_mount, pass_start_pos)
|
|
@@ -2766,17 +2803,19 @@ class OT3API(
|
|
|
2766
2803
|
checked_mount,
|
|
2767
2804
|
probe_settings,
|
|
2768
2805
|
checked_probe,
|
|
2769
|
-
|
|
2806
|
+
plunger_travel_mm + sensor_baseline_plunger_move_mm,
|
|
2807
|
+
force_both_sensors,
|
|
2808
|
+
response_queue,
|
|
2770
2809
|
)
|
|
2771
2810
|
# if we made it here without an error we found the liquid
|
|
2772
2811
|
error = None
|
|
2773
2812
|
break
|
|
2774
2813
|
except PipetteLiquidNotFoundError as lnfe:
|
|
2775
2814
|
error = lnfe
|
|
2776
|
-
|
|
2777
|
-
await self.move_to(checked_mount,
|
|
2815
|
+
current_position = await self.gantry_position(checked_mount, refresh=True)
|
|
2816
|
+
await self.move_to(checked_mount, starting_position + top_types.Point(z=2))
|
|
2778
2817
|
await self.prepare_for_aspirate(checked_mount)
|
|
2779
|
-
await self.move_to(checked_mount,
|
|
2818
|
+
await self.move_to(checked_mount, starting_position)
|
|
2780
2819
|
if error is not None:
|
|
2781
2820
|
# if we never found liquid raise an error
|
|
2782
2821
|
raise error
|
|
@@ -2835,8 +2874,6 @@ class OT3API(
|
|
|
2835
2874
|
pass_settings.speed_mm_per_s,
|
|
2836
2875
|
pass_settings.sensor_threshold_pf,
|
|
2837
2876
|
probe,
|
|
2838
|
-
pass_settings.output_option,
|
|
2839
|
-
pass_settings.data_files,
|
|
2840
2877
|
)
|
|
2841
2878
|
end_pos = await self.gantry_position(mount, refresh=True)
|
|
2842
2879
|
if retract_after:
|
|
@@ -3,6 +3,7 @@ import contextlib
|
|
|
3
3
|
import logging
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from typing import AsyncGenerator, List, Optional
|
|
6
|
+
from opentrons.hardware_control.modules.errors import AbsorbanceReaderDisconnectedError
|
|
6
7
|
from opentrons_shared_data.errors.exceptions import ModuleCommunicationError
|
|
7
8
|
|
|
8
9
|
|
|
@@ -36,21 +37,20 @@ class Poller:
|
|
|
36
37
|
self._poll_forever_task: Optional["asyncio.Task[None]"] = None
|
|
37
38
|
|
|
38
39
|
async def start(self) -> None:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
if self._poll_forever_task is None:
|
|
41
|
+
self._poll_forever_task = asyncio.create_task(self._poll_forever())
|
|
42
|
+
await self.wait_next_poll()
|
|
42
43
|
|
|
43
44
|
async def stop(self) -> None:
|
|
44
45
|
"""Stop polling."""
|
|
45
46
|
task = self._poll_forever_task
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
waiter.cancel(msg="Module was removed")
|
|
47
|
+
if task is not None:
|
|
48
|
+
async with self._use_read_lock():
|
|
49
|
+
task.cancel()
|
|
50
|
+
await asyncio.gather(task, return_exceptions=True)
|
|
51
|
+
for waiter in self._poll_waiters:
|
|
52
|
+
waiter.cancel(msg="Module was removed")
|
|
53
|
+
self._poll_forever_task = None
|
|
54
54
|
|
|
55
55
|
async def wait_next_poll(self) -> None:
|
|
56
56
|
"""Wait for the next poll to complete.
|
|
@@ -98,6 +98,10 @@ class Poller:
|
|
|
98
98
|
await self._reader.read()
|
|
99
99
|
except asyncio.CancelledError:
|
|
100
100
|
raise
|
|
101
|
+
except AbsorbanceReaderDisconnectedError as e:
|
|
102
|
+
for waiter in previous_waiters:
|
|
103
|
+
Poller._set_waiter_complete(waiter, None)
|
|
104
|
+
self._reader.on_error(e)
|
|
101
105
|
except Exception as e:
|
|
102
106
|
log.exception("Polling exception")
|
|
103
107
|
self._reader.on_error(e)
|
|
@@ -58,9 +58,6 @@ class HardwareControlInterface(
|
|
|
58
58
|
def get_robot_type(self) -> Type[OT2RobotType]:
|
|
59
59
|
return OT2RobotType
|
|
60
60
|
|
|
61
|
-
def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
|
|
62
|
-
...
|
|
63
|
-
|
|
64
61
|
|
|
65
62
|
class FlexHardwareControlInterface(
|
|
66
63
|
PositionEstimator,
|
|
@@ -87,12 +84,9 @@ class FlexHardwareControlInterface(
|
|
|
87
84
|
def get_robot_type(self) -> Type[FlexRobotType]:
|
|
88
85
|
return FlexRobotType
|
|
89
86
|
|
|
90
|
-
def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
|
|
91
|
-
...
|
|
92
|
-
|
|
93
87
|
|
|
94
88
|
__all__ = [
|
|
95
|
-
"
|
|
89
|
+
"HardwareControlInterface",
|
|
96
90
|
"FlexHardwareControlInterface",
|
|
97
91
|
"Simulatable",
|
|
98
92
|
"Stoppable",
|
|
@@ -142,15 +142,27 @@ class InstrumentConfigurer(Protocol[MountArgType]):
|
|
|
142
142
|
"""
|
|
143
143
|
...
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
# todo(mm, 2024-10-17): Consider deleting this in favor of cache_tip()
|
|
146
|
+
# if we can do so without breaking anything.
|
|
147
|
+
def add_tip(self, mount: MountArgType, tip_length: float) -> None:
|
|
146
148
|
"""Inform the hardware that a tip is now attached to a pipette.
|
|
147
149
|
|
|
150
|
+
If a tip is already attached, this no-ops.
|
|
151
|
+
|
|
148
152
|
This changes the critical point of the pipette to make sure that
|
|
149
153
|
the end of the tip is what moves around, and allows liquid handling.
|
|
150
154
|
"""
|
|
151
155
|
...
|
|
152
156
|
|
|
153
|
-
|
|
157
|
+
def cache_tip(self, mount: MountArgType, tip_length: float) -> None:
|
|
158
|
+
"""Inform the hardware that a tip is now attached to a pipette.
|
|
159
|
+
|
|
160
|
+
This is like `add_tip()`, except that if a tip is already attached,
|
|
161
|
+
this replaces it instead of no-opping.
|
|
162
|
+
"""
|
|
163
|
+
...
|
|
164
|
+
|
|
165
|
+
def remove_tip(self, mount: MountArgType) -> None:
|
|
154
166
|
"""Inform the hardware that a tip is no longer attached to a pipette.
|
|
155
167
|
|
|
156
168
|
This changes the critical point of the system to the end of the
|
|
@@ -6,6 +6,7 @@ from .waypoints import (
|
|
|
6
6
|
MINIMUM_Z_MARGIN,
|
|
7
7
|
get_waypoints,
|
|
8
8
|
get_gripper_labware_movement_waypoints,
|
|
9
|
+
get_gripper_labware_placement_waypoints,
|
|
9
10
|
)
|
|
10
11
|
|
|
11
12
|
from .types import Waypoint, MoveType
|
|
@@ -27,4 +28,5 @@ __all__ = [
|
|
|
27
28
|
"ArcOutOfBoundsError",
|
|
28
29
|
"get_waypoints",
|
|
29
30
|
"get_gripper_labware_movement_waypoints",
|
|
31
|
+
"get_gripper_labware_placement_waypoints",
|
|
30
32
|
]
|
|
@@ -181,3 +181,35 @@ def get_gripper_labware_movement_waypoints(
|
|
|
181
181
|
)
|
|
182
182
|
)
|
|
183
183
|
return waypoints_with_jaw_status
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_gripper_labware_placement_waypoints(
|
|
187
|
+
to_labware_center: Point,
|
|
188
|
+
gripper_home_z: float,
|
|
189
|
+
drop_offset: Optional[Point],
|
|
190
|
+
) -> List[GripperMovementWaypointsWithJawStatus]:
|
|
191
|
+
"""Get waypoints for placing labware using a gripper."""
|
|
192
|
+
drop_offset = drop_offset or Point()
|
|
193
|
+
|
|
194
|
+
drop_location = to_labware_center + Point(
|
|
195
|
+
drop_offset.x, drop_offset.y, drop_offset.z
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
post_drop_home_pos = Point(drop_location.x, drop_location.y, gripper_home_z)
|
|
199
|
+
|
|
200
|
+
return [
|
|
201
|
+
GripperMovementWaypointsWithJawStatus(
|
|
202
|
+
position=Point(drop_location.x, drop_location.y, gripper_home_z),
|
|
203
|
+
jaw_open=False,
|
|
204
|
+
dropping=False,
|
|
205
|
+
),
|
|
206
|
+
GripperMovementWaypointsWithJawStatus(
|
|
207
|
+
position=drop_location, jaw_open=False, dropping=False
|
|
208
|
+
),
|
|
209
|
+
# Gripper ungrips here
|
|
210
|
+
GripperMovementWaypointsWithJawStatus(
|
|
211
|
+
position=post_drop_home_pos,
|
|
212
|
+
jaw_open=True,
|
|
213
|
+
dropping=True,
|
|
214
|
+
),
|
|
215
|
+
]
|
|
@@ -29,7 +29,7 @@ from .module_contexts import (
|
|
|
29
29
|
AbsorbanceReaderContext,
|
|
30
30
|
)
|
|
31
31
|
from .disposal_locations import TrashBin, WasteChute
|
|
32
|
-
from ._liquid import Liquid
|
|
32
|
+
from ._liquid import Liquid, LiquidClass
|
|
33
33
|
from ._types import OFF_DECK
|
|
34
34
|
from ._nozzle_layout import (
|
|
35
35
|
COLUMN,
|
|
@@ -67,6 +67,7 @@ __all__ = [
|
|
|
67
67
|
"WasteChute",
|
|
68
68
|
"Well",
|
|
69
69
|
"Liquid",
|
|
70
|
+
"LiquidClass",
|
|
70
71
|
"Parameters",
|
|
71
72
|
"COLUMN",
|
|
72
73
|
"PARTIAL_COLUMN",
|