opentrons 8.4.0a12__py2.py3-none-any.whl → 8.5.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/config/defaults_ot3.py +1 -1
- opentrons/legacy_commands/commands.py +16 -4
- opentrons/legacy_commands/robot_commands.py +51 -0
- opentrons/legacy_commands/types.py +91 -2
- opentrons/protocol_api/_liquid.py +60 -15
- opentrons/protocol_api/_liquid_properties.py +137 -90
- opentrons/protocol_api/_transfer_liquid_validation.py +10 -6
- opentrons/protocol_api/core/engine/instrument.py +172 -75
- opentrons/protocol_api/core/engine/protocol.py +13 -14
- opentrons/protocol_api/core/engine/robot.py +2 -2
- opentrons/protocol_api/core/engine/transfer_components_executor.py +157 -126
- opentrons/protocol_api/core/engine/well.py +16 -0
- opentrons/protocol_api/core/instrument.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +1 -1
- opentrons/protocol_api/core/legacy/legacy_well_core.py +8 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -2
- opentrons/protocol_api/core/protocol.py +2 -2
- opentrons/protocol_api/core/well.py +8 -0
- opentrons/protocol_api/instrument_context.py +377 -86
- opentrons/protocol_api/labware.py +10 -0
- opentrons/protocol_api/protocol_context.py +79 -4
- opentrons/protocol_api/robot_context.py +48 -6
- opentrons/protocol_api/validation.py +15 -8
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +0 -1
- opentrons/protocol_engine/commands/command_unions.py +10 -10
- opentrons/protocol_engine/commands/generate_command_schema.py +1 -1
- opentrons/protocol_engine/commands/get_next_tip.py +2 -2
- opentrons/protocol_engine/commands/pick_up_tip.py +9 -3
- opentrons/protocol_engine/commands/robot/__init__.py +20 -20
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +34 -24
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +29 -20
- opentrons/protocol_engine/commands/seal_pipette_to_tip.py +1 -1
- opentrons/protocol_engine/commands/unsafe/__init__.py +17 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +1 -2
- opentrons/protocol_engine/execution/labware_movement.py +9 -2
- opentrons/protocol_engine/execution/movement.py +12 -9
- opentrons/protocol_engine/execution/queue_worker.py +8 -1
- opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +52 -19
- opentrons/protocol_engine/state/_well_math.py +2 -2
- opentrons/protocol_engine/state/commands.py +14 -28
- opentrons/protocol_engine/state/frustum_helpers.py +11 -7
- opentrons/protocol_engine/state/modules.py +1 -1
- opentrons/protocol_engine/state/pipettes.py +8 -0
- opentrons/protocol_engine/state/tips.py +46 -83
- opentrons/protocol_engine/state/update_types.py +8 -23
- opentrons/protocol_runner/legacy_command_mapper.py +11 -4
- opentrons/protocol_runner/run_orchestrator.py +1 -1
- opentrons/protocols/advanced_control/transfers/common.py +54 -11
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/types.py +6 -6
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/RECORD +58 -57
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a12.dist-info → opentrons-8.5.0a0.dist-info}/top_level.txt +0 -0
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
from contextlib import contextmanager
|
|
5
5
|
from itertools import dropwhile
|
|
6
|
+
from copy import deepcopy
|
|
6
7
|
from typing import (
|
|
7
8
|
Optional,
|
|
8
9
|
TYPE_CHECKING,
|
|
9
10
|
cast,
|
|
10
11
|
Union,
|
|
11
12
|
List,
|
|
13
|
+
Sequence,
|
|
12
14
|
Tuple,
|
|
13
15
|
NamedTuple,
|
|
14
16
|
Generator,
|
|
@@ -88,6 +90,7 @@ if TYPE_CHECKING:
|
|
|
88
90
|
from opentrons.protocol_api._liquid_properties import (
|
|
89
91
|
TransferProperties,
|
|
90
92
|
MultiDispenseProperties,
|
|
93
|
+
SingleDispenseProperties,
|
|
91
94
|
)
|
|
92
95
|
|
|
93
96
|
_DISPENSE_VOLUME_VALIDATION_ADDED_IN = APIVersion(2, 17)
|
|
@@ -389,12 +392,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
389
392
|
)
|
|
390
393
|
)
|
|
391
394
|
|
|
392
|
-
|
|
393
|
-
self._protocol_core.set_last_location(location=None, mount=self.get_mount())
|
|
394
|
-
else:
|
|
395
|
-
self._protocol_core.set_last_location(
|
|
396
|
-
location=location, mount=self.get_mount()
|
|
397
|
-
)
|
|
395
|
+
self._protocol_core.set_last_location(location=location, mount=self.get_mount())
|
|
398
396
|
|
|
399
397
|
def blow_out(
|
|
400
398
|
self,
|
|
@@ -470,12 +468,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
470
468
|
)
|
|
471
469
|
)
|
|
472
470
|
|
|
473
|
-
|
|
474
|
-
self._protocol_core.set_last_location(location=None, mount=self.get_mount())
|
|
475
|
-
else:
|
|
476
|
-
self._protocol_core.set_last_location(
|
|
477
|
-
location=location, mount=self.get_mount()
|
|
478
|
-
)
|
|
471
|
+
self._protocol_core.set_last_location(location=location, mount=self.get_mount())
|
|
479
472
|
|
|
480
473
|
def touch_tip(
|
|
481
474
|
self,
|
|
@@ -803,12 +796,8 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
803
796
|
speed=speed,
|
|
804
797
|
)
|
|
805
798
|
)
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
else:
|
|
809
|
-
self._protocol_core.set_last_location(
|
|
810
|
-
location=location, mount=self.get_mount()
|
|
811
|
-
)
|
|
799
|
+
|
|
800
|
+
self._protocol_core.set_last_location(location=location, mount=self.get_mount())
|
|
812
801
|
|
|
813
802
|
def resin_tip_seal(
|
|
814
803
|
self, location: Location, well_core: WellCore, in_place: Optional[bool] = False
|
|
@@ -1010,15 +999,15 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1010
999
|
return self._sync_hardware_api.get_attached_instrument(self.get_mount()) # type: ignore[no-any-return]
|
|
1011
1000
|
|
|
1012
1001
|
def get_channels(self) -> int:
|
|
1013
|
-
return self._engine_client.state.
|
|
1002
|
+
return self._engine_client.state.pipettes.get_channels(self._pipette_id)
|
|
1014
1003
|
|
|
1015
1004
|
def get_active_channels(self) -> int:
|
|
1016
|
-
return self._engine_client.state.
|
|
1017
|
-
self._pipette_id
|
|
1018
|
-
)
|
|
1005
|
+
return self._engine_client.state.pipettes.get_active_channels(self._pipette_id)
|
|
1019
1006
|
|
|
1020
1007
|
def get_nozzle_map(self) -> NozzleMapInterface:
|
|
1021
|
-
return self._engine_client.state.
|
|
1008
|
+
return self._engine_client.state.pipettes.get_nozzle_configuration(
|
|
1009
|
+
self._pipette_id
|
|
1010
|
+
)
|
|
1022
1011
|
|
|
1023
1012
|
def has_tip(self) -> bool:
|
|
1024
1013
|
return (
|
|
@@ -1210,7 +1199,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1210
1199
|
liquid_class: LiquidClass,
|
|
1211
1200
|
volume: float,
|
|
1212
1201
|
source: List[Tuple[Location, WellCore]],
|
|
1213
|
-
dest: List[Tuple[Location, WellCore]],
|
|
1202
|
+
dest: Union[List[Tuple[Location, WellCore]], TrashBin, WasteChute],
|
|
1214
1203
|
new_tip: TransferTipPolicyV2,
|
|
1215
1204
|
tip_racks: List[Tuple[Location, LabwareCore]],
|
|
1216
1205
|
starting_tip: Optional[WellCore],
|
|
@@ -1256,18 +1245,30 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1256
1245
|
tiprack_uri=tiprack_uri_for_transfer_props,
|
|
1257
1246
|
)
|
|
1258
1247
|
|
|
1248
|
+
target_destinations: Sequence[
|
|
1249
|
+
Union[Tuple[Location, WellCore], TrashBin, WasteChute]
|
|
1250
|
+
]
|
|
1251
|
+
if isinstance(dest, (TrashBin, WasteChute)):
|
|
1252
|
+
target_destinations = [dest] * len(source)
|
|
1253
|
+
else:
|
|
1254
|
+
target_destinations = dest
|
|
1255
|
+
|
|
1256
|
+
max_volume = min(
|
|
1257
|
+
self.get_max_volume(),
|
|
1258
|
+
self._engine_client.state.geometry.get_nominal_tip_geometry(
|
|
1259
|
+
pipette_id=self.pipette_id,
|
|
1260
|
+
labware_id=tip_racks[0][1].labware_id,
|
|
1261
|
+
well_name=None,
|
|
1262
|
+
).volume,
|
|
1263
|
+
)
|
|
1264
|
+
|
|
1265
|
+
aspirate_air_gap_by_volume = transfer_props.aspirate.retract.air_gap_by_volume
|
|
1259
1266
|
source_dest_per_volume_step = (
|
|
1260
1267
|
tx_commons.expand_for_volume_constraints_for_liquid_classes(
|
|
1261
1268
|
volumes=[volume for _ in range(len(source))],
|
|
1262
|
-
targets=zip(source,
|
|
1263
|
-
max_volume=
|
|
1264
|
-
|
|
1265
|
-
self._engine_client.state.geometry.get_nominal_tip_geometry(
|
|
1266
|
-
pipette_id=self.pipette_id,
|
|
1267
|
-
labware_id=tip_racks[0][1].labware_id,
|
|
1268
|
-
well_name=None,
|
|
1269
|
-
).volume,
|
|
1270
|
-
),
|
|
1269
|
+
targets=zip(source, target_destinations),
|
|
1270
|
+
max_volume=max_volume,
|
|
1271
|
+
air_gap=aspirate_air_gap_by_volume,
|
|
1271
1272
|
)
|
|
1272
1273
|
)
|
|
1273
1274
|
|
|
@@ -1328,6 +1329,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1328
1329
|
last_tip_picked_up_from = _pick_up_tip()
|
|
1329
1330
|
|
|
1330
1331
|
prev_src: Optional[Tuple[Location, WellCore]] = None
|
|
1332
|
+
prev_dest: Optional[
|
|
1333
|
+
Union[Tuple[Location, WellCore], TrashBin, WasteChute]
|
|
1334
|
+
] = None
|
|
1331
1335
|
post_disp_tip_contents = [
|
|
1332
1336
|
tx_comps_executor.LiquidAndAirGapPair(
|
|
1333
1337
|
liquid=0,
|
|
@@ -1347,10 +1351,18 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1347
1351
|
except StopIteration:
|
|
1348
1352
|
is_last_step = True
|
|
1349
1353
|
|
|
1350
|
-
if
|
|
1351
|
-
new_tip == TransferTipPolicyV2.
|
|
1354
|
+
if (
|
|
1355
|
+
new_tip == TransferTipPolicyV2.ALWAYS
|
|
1356
|
+
or (
|
|
1357
|
+
new_tip == TransferTipPolicyV2.PER_SOURCE
|
|
1358
|
+
and step_source != prev_src
|
|
1359
|
+
)
|
|
1360
|
+
or (
|
|
1361
|
+
new_tip == TransferTipPolicyV2.PER_DESTINATION
|
|
1362
|
+
and step_destination != prev_dest
|
|
1363
|
+
)
|
|
1352
1364
|
):
|
|
1353
|
-
if prev_src is not None:
|
|
1365
|
+
if prev_src is not None and prev_dest is not None:
|
|
1354
1366
|
_drop_tip()
|
|
1355
1367
|
last_tip_picked_up_from = _pick_up_tip()
|
|
1356
1368
|
post_disp_tip_contents = [
|
|
@@ -1399,6 +1411,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1399
1411
|
trash_location=trash_location,
|
|
1400
1412
|
)
|
|
1401
1413
|
prev_src = step_source
|
|
1414
|
+
prev_dest = step_destination
|
|
1402
1415
|
if new_tip != TransferTipPolicyV2.NEVER:
|
|
1403
1416
|
_drop_tip()
|
|
1404
1417
|
|
|
@@ -1507,6 +1520,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1507
1520
|
tiprack_uri=tiprack_uri_for_transfer_props,
|
|
1508
1521
|
)
|
|
1509
1522
|
|
|
1523
|
+
aspirate_air_gap_by_volume = transfer_props.aspirate.retract.air_gap_by_volume
|
|
1524
|
+
disposal_vol_by_volume = transfer_props.multi_dispense.disposal_by_volume
|
|
1525
|
+
conditioning_vol_by_volume = (
|
|
1526
|
+
transfer_props.multi_dispense.conditioning_by_volume
|
|
1527
|
+
)
|
|
1510
1528
|
# This will return a generator that provides pairs of destination well and
|
|
1511
1529
|
# the volume to dispense into it
|
|
1512
1530
|
dest_per_volume_step = (
|
|
@@ -1514,6 +1532,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1514
1532
|
volumes=[volume for _ in range(len(dest))],
|
|
1515
1533
|
targets=dest,
|
|
1516
1534
|
max_volume=working_volume,
|
|
1535
|
+
air_gap=aspirate_air_gap_by_volume,
|
|
1536
|
+
disposal_vol=disposal_vol_by_volume,
|
|
1537
|
+
conditioning_vol=conditioning_vol_by_volume,
|
|
1517
1538
|
)
|
|
1518
1539
|
)
|
|
1519
1540
|
|
|
@@ -1726,7 +1747,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1726
1747
|
liquid_class: LiquidClass,
|
|
1727
1748
|
volume: float,
|
|
1728
1749
|
source: List[Tuple[Location, WellCore]],
|
|
1729
|
-
dest: Tuple[Location, WellCore],
|
|
1750
|
+
dest: Union[Tuple[Location, WellCore], TrashBin, WasteChute],
|
|
1730
1751
|
new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
|
|
1731
1752
|
tip_racks: List[Tuple[Location, LabwareCore]],
|
|
1732
1753
|
starting_tip: Optional[WellCore],
|
|
@@ -1776,11 +1797,13 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1776
1797
|
).volume,
|
|
1777
1798
|
)
|
|
1778
1799
|
|
|
1800
|
+
aspirate_air_gap_by_volume = transfer_props.aspirate.retract.air_gap_by_volume
|
|
1779
1801
|
source_per_volume_step = (
|
|
1780
1802
|
tx_commons.expand_for_volume_constraints_for_liquid_classes(
|
|
1781
1803
|
volumes=[volume for _ in range(len(source))],
|
|
1782
1804
|
targets=source,
|
|
1783
1805
|
max_volume=max_volume,
|
|
1806
|
+
air_gap=aspirate_air_gap_by_volume,
|
|
1784
1807
|
)
|
|
1785
1808
|
)
|
|
1786
1809
|
|
|
@@ -1852,12 +1875,16 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1852
1875
|
while not is_last_step:
|
|
1853
1876
|
total_dispense_volume = 0.0
|
|
1854
1877
|
vol_aspirate_combo = []
|
|
1878
|
+
air_gap = aspirate_air_gap_by_volume.get_for_volume(next_step_volume)
|
|
1855
1879
|
# Take air gap into account because there will be a final air gap before the dispense
|
|
1856
|
-
while total_dispense_volume + next_step_volume <= max_volume:
|
|
1880
|
+
while total_dispense_volume + next_step_volume <= max_volume - air_gap:
|
|
1857
1881
|
total_dispense_volume += next_step_volume
|
|
1858
1882
|
vol_aspirate_combo.append((next_step_volume, next_source))
|
|
1859
1883
|
try:
|
|
1860
1884
|
next_step_volume, next_source = next(source_per_volume_step)
|
|
1885
|
+
air_gap = aspirate_air_gap_by_volume.get_for_volume(
|
|
1886
|
+
next_step_volume + total_dispense_volume
|
|
1887
|
+
)
|
|
1861
1888
|
except StopIteration:
|
|
1862
1889
|
is_last_step = True
|
|
1863
1890
|
break
|
|
@@ -1871,6 +1898,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1871
1898
|
else:
|
|
1872
1899
|
enable_lpd = False
|
|
1873
1900
|
|
|
1901
|
+
total_aspirated_volume = 0.0
|
|
1874
1902
|
for step_num, (step_volume, step_source) in enumerate(vol_aspirate_combo):
|
|
1875
1903
|
with self.lpd_for_transfer(enable=enable_lpd):
|
|
1876
1904
|
tip_contents = self.aspirate_liquid_class(
|
|
@@ -1882,7 +1910,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1882
1910
|
volume_for_pipette_mode_configuration=(
|
|
1883
1911
|
total_dispense_volume if step_num == 0 else None
|
|
1884
1912
|
),
|
|
1913
|
+
current_volume=total_aspirated_volume,
|
|
1885
1914
|
)
|
|
1915
|
+
total_aspirated_volume += step_volume
|
|
1886
1916
|
is_first_step = False
|
|
1887
1917
|
enable_lpd = False
|
|
1888
1918
|
tip_contents = self.dispense_liquid_class(
|
|
@@ -1931,6 +1961,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1931
1961
|
tip_contents: List[tx_comps_executor.LiquidAndAirGapPair],
|
|
1932
1962
|
volume_for_pipette_mode_configuration: Optional[float],
|
|
1933
1963
|
conditioning_volume: Optional[float] = None,
|
|
1964
|
+
current_volume: float = 0.0,
|
|
1934
1965
|
) -> List[tx_comps_executor.LiquidAndAirGapPair]:
|
|
1935
1966
|
"""Execute aspiration steps.
|
|
1936
1967
|
|
|
@@ -1944,33 +1975,64 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1944
1975
|
Return: List of liquid and air gap pairs in tip.
|
|
1945
1976
|
"""
|
|
1946
1977
|
aspirate_props = transfer_properties.aspirate
|
|
1978
|
+
volume_for_air_gap = aspirate_props.retract.air_gap_by_volume.get_for_volume(
|
|
1979
|
+
volume + current_volume
|
|
1980
|
+
)
|
|
1947
1981
|
tx_commons.check_valid_liquid_class_volume_parameters(
|
|
1948
1982
|
aspirate_volume=volume,
|
|
1949
|
-
air_gap=
|
|
1950
|
-
aspirate_props.retract.air_gap_by_volume.get_for_volume(volume)
|
|
1951
|
-
if conditioning_volume is None
|
|
1952
|
-
else 0
|
|
1953
|
-
),
|
|
1954
|
-
disposal_volume=0, # Disposal volume is accounted for in aspirate vol
|
|
1983
|
+
air_gap=volume_for_air_gap if conditioning_volume is None else 0,
|
|
1955
1984
|
max_volume=self.get_working_volume(),
|
|
1985
|
+
current_volume=current_volume,
|
|
1956
1986
|
)
|
|
1957
1987
|
source_loc, source_well = source
|
|
1958
|
-
aspirate_point = (
|
|
1959
|
-
tx_comps_executor.absolute_point_from_position_reference_and_offset(
|
|
1960
|
-
well=source_well,
|
|
1961
|
-
position_reference=aspirate_props.position_reference,
|
|
1962
|
-
offset=aspirate_props.offset,
|
|
1963
|
-
)
|
|
1964
|
-
)
|
|
1965
|
-
aspirate_location = Location(aspirate_point, labware=source_loc.labware)
|
|
1966
1988
|
last_liquid_and_airgap_in_tip = (
|
|
1967
|
-
tip_contents[-1]
|
|
1989
|
+
deepcopy(tip_contents[-1]) # don't modify caller's object
|
|
1968
1990
|
if tip_contents
|
|
1969
1991
|
else tx_comps_executor.LiquidAndAirGapPair(
|
|
1970
1992
|
liquid=0,
|
|
1971
1993
|
air_gap=0,
|
|
1972
1994
|
)
|
|
1973
1995
|
)
|
|
1996
|
+
if volume_for_pipette_mode_configuration is not None:
|
|
1997
|
+
prep_location = Location(
|
|
1998
|
+
point=source_well.get_top(LIQUID_PROBE_START_OFFSET_FROM_WELL_TOP.z),
|
|
1999
|
+
labware=source_loc.labware,
|
|
2000
|
+
)
|
|
2001
|
+
self.move_to(
|
|
2002
|
+
location=prep_location,
|
|
2003
|
+
well_core=source_well,
|
|
2004
|
+
force_direct=False,
|
|
2005
|
+
minimum_z_height=None,
|
|
2006
|
+
speed=None,
|
|
2007
|
+
)
|
|
2008
|
+
self.remove_air_gap_during_transfer_with_liquid_class(
|
|
2009
|
+
last_air_gap=last_liquid_and_airgap_in_tip.air_gap,
|
|
2010
|
+
dispense_props=transfer_properties.dispense,
|
|
2011
|
+
location=prep_location,
|
|
2012
|
+
)
|
|
2013
|
+
last_liquid_and_airgap_in_tip.air_gap = 0
|
|
2014
|
+
if (
|
|
2015
|
+
transfer_type != tx_comps_executor.TransferType.MANY_TO_ONE
|
|
2016
|
+
and self.get_liquid_presence_detection()
|
|
2017
|
+
):
|
|
2018
|
+
self.liquid_probe_with_recovery(
|
|
2019
|
+
well_core=source_well, loc=prep_location
|
|
2020
|
+
)
|
|
2021
|
+
# TODO: do volume configuration + prepare for aspirate only if the mode needs to be changed
|
|
2022
|
+
self.configure_for_volume(volume_for_pipette_mode_configuration)
|
|
2023
|
+
self.prepare_to_aspirate()
|
|
2024
|
+
|
|
2025
|
+
aspirate_point = (
|
|
2026
|
+
tx_comps_executor.absolute_point_from_position_reference_and_offset(
|
|
2027
|
+
well=source_well,
|
|
2028
|
+
well_volume_difference=-volume,
|
|
2029
|
+
position_reference=aspirate_props.aspirate_position.position_reference,
|
|
2030
|
+
offset=aspirate_props.aspirate_position.offset,
|
|
2031
|
+
mount=self.get_mount(),
|
|
2032
|
+
)
|
|
2033
|
+
)
|
|
2034
|
+
aspirate_location = Location(aspirate_point, labware=source_loc.labware)
|
|
2035
|
+
|
|
1974
2036
|
components_executor = tx_comps_executor.TransferComponentsExecutor(
|
|
1975
2037
|
instrument_core=self,
|
|
1976
2038
|
transfer_properties=transfer_properties,
|
|
@@ -1982,9 +2044,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1982
2044
|
),
|
|
1983
2045
|
)
|
|
1984
2046
|
components_executor.submerge(
|
|
1985
|
-
submerge_properties=aspirate_props.submerge,
|
|
1986
|
-
post_submerge_action="aspirate",
|
|
1987
|
-
volume_for_pipette_mode_configuration=volume_for_pipette_mode_configuration,
|
|
2047
|
+
submerge_properties=aspirate_props.submerge, post_submerge_action="aspirate"
|
|
1988
2048
|
)
|
|
1989
2049
|
# Do not do a pre-aspirate mix or pre-wet if consolidating
|
|
1990
2050
|
if transfer_type != tx_comps_executor.TransferType.MANY_TO_ONE:
|
|
@@ -2023,10 +2083,42 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2023
2083
|
new_tip_contents = tip_contents[0:-1] + [last_contents]
|
|
2024
2084
|
return new_tip_contents
|
|
2025
2085
|
|
|
2086
|
+
def remove_air_gap_during_transfer_with_liquid_class(
|
|
2087
|
+
self,
|
|
2088
|
+
last_air_gap: float,
|
|
2089
|
+
dispense_props: SingleDispenseProperties,
|
|
2090
|
+
location: Union[Location, TrashBin, WasteChute],
|
|
2091
|
+
) -> None:
|
|
2092
|
+
"""Remove an air gap that was previously added during a transfer."""
|
|
2093
|
+
if last_air_gap == 0:
|
|
2094
|
+
return
|
|
2095
|
+
|
|
2096
|
+
correction_volume = dispense_props.correction_by_volume.get_for_volume(
|
|
2097
|
+
last_air_gap
|
|
2098
|
+
)
|
|
2099
|
+
# The minimum flow rate should be air_gap_volume per second
|
|
2100
|
+
flow_rate = max(
|
|
2101
|
+
dispense_props.flow_rate_by_volume.get_for_volume(last_air_gap),
|
|
2102
|
+
last_air_gap,
|
|
2103
|
+
)
|
|
2104
|
+
self.dispense(
|
|
2105
|
+
location=location,
|
|
2106
|
+
well_core=None,
|
|
2107
|
+
volume=last_air_gap,
|
|
2108
|
+
rate=1,
|
|
2109
|
+
flow_rate=flow_rate,
|
|
2110
|
+
in_place=True,
|
|
2111
|
+
push_out=0,
|
|
2112
|
+
correction_volume=correction_volume,
|
|
2113
|
+
)
|
|
2114
|
+
dispense_delay = dispense_props.delay
|
|
2115
|
+
if dispense_delay.enabled and dispense_delay.duration:
|
|
2116
|
+
self.delay(dispense_delay.duration)
|
|
2117
|
+
|
|
2026
2118
|
def dispense_liquid_class(
|
|
2027
2119
|
self,
|
|
2028
2120
|
volume: float,
|
|
2029
|
-
dest: Tuple[Location, WellCore],
|
|
2121
|
+
dest: Union[Tuple[Location, WellCore], TrashBin, WasteChute],
|
|
2030
2122
|
source: Optional[Tuple[Location, WellCore]],
|
|
2031
2123
|
transfer_properties: TransferProperties,
|
|
2032
2124
|
transfer_type: tx_comps_executor.TransferType,
|
|
@@ -2065,15 +2157,21 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2065
2157
|
List of liquid and air gap pairs in tip.
|
|
2066
2158
|
"""
|
|
2067
2159
|
dispense_props = transfer_properties.dispense
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2160
|
+
dispense_location: Union[Location, TrashBin, WasteChute]
|
|
2161
|
+
if isinstance(dest, tuple):
|
|
2162
|
+
dest_loc, dest_well = dest
|
|
2163
|
+
dispense_point = tx_comps_executor.absolute_point_from_position_reference_and_offset(
|
|
2071
2164
|
well=dest_well,
|
|
2072
|
-
|
|
2073
|
-
|
|
2165
|
+
well_volume_difference=volume,
|
|
2166
|
+
position_reference=dispense_props.dispense_position.position_reference,
|
|
2167
|
+
offset=dispense_props.dispense_position.offset,
|
|
2168
|
+
mount=self.get_mount(),
|
|
2074
2169
|
)
|
|
2075
|
-
|
|
2076
|
-
|
|
2170
|
+
dispense_location = Location(dispense_point, labware=dest_loc.labware)
|
|
2171
|
+
else:
|
|
2172
|
+
dispense_location = dest
|
|
2173
|
+
dest_well = None
|
|
2174
|
+
|
|
2077
2175
|
last_liquid_and_airgap_in_tip = (
|
|
2078
2176
|
tip_contents[-1]
|
|
2079
2177
|
if tip_contents
|
|
@@ -2093,9 +2191,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2093
2191
|
),
|
|
2094
2192
|
)
|
|
2095
2193
|
components_executor.submerge(
|
|
2096
|
-
submerge_properties=dispense_props.submerge,
|
|
2097
|
-
post_submerge_action="dispense",
|
|
2098
|
-
volume_for_pipette_mode_configuration=None,
|
|
2194
|
+
submerge_properties=dispense_props.submerge, post_submerge_action="dispense"
|
|
2099
2195
|
)
|
|
2100
2196
|
push_out_vol = (
|
|
2101
2197
|
0.0
|
|
@@ -2151,8 +2247,10 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2151
2247
|
dispense_point = (
|
|
2152
2248
|
tx_comps_executor.absolute_point_from_position_reference_and_offset(
|
|
2153
2249
|
well=dest_well,
|
|
2154
|
-
|
|
2155
|
-
|
|
2250
|
+
well_volume_difference=volume,
|
|
2251
|
+
position_reference=dispense_props.dispense_position.position_reference,
|
|
2252
|
+
offset=dispense_props.dispense_position.offset,
|
|
2253
|
+
mount=self.get_mount(),
|
|
2156
2254
|
)
|
|
2157
2255
|
)
|
|
2158
2256
|
dispense_location = Location(dispense_point, labware=dest_loc.labware)
|
|
@@ -2175,9 +2273,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2175
2273
|
),
|
|
2176
2274
|
)
|
|
2177
2275
|
components_executor.submerge(
|
|
2178
|
-
submerge_properties=dispense_props.submerge,
|
|
2179
|
-
post_submerge_action="dispense",
|
|
2180
|
-
volume_for_pipette_mode_configuration=None,
|
|
2276
|
+
submerge_properties=dispense_props.submerge, post_submerge_action="dispense"
|
|
2181
2277
|
)
|
|
2182
2278
|
tip_starting_volume = self.get_current_volume()
|
|
2183
2279
|
is_last_dispense_without_disposal_vol = (
|
|
@@ -2220,8 +2316,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2220
2316
|
def detect_liquid_presence(self, well_core: WellCore, loc: Location) -> bool:
|
|
2221
2317
|
labware_id = well_core.labware_id
|
|
2222
2318
|
well_name = well_core.get_name()
|
|
2319
|
+
offset = LIQUID_PROBE_START_OFFSET_FROM_WELL_TOP
|
|
2223
2320
|
well_location = WellLocation(
|
|
2224
|
-
origin=WellOrigin.TOP, offset=WellOffset(x=
|
|
2321
|
+
origin=WellOrigin.TOP, offset=WellOffset(x=offset.x, y=offset.y, z=offset.z)
|
|
2225
2322
|
)
|
|
2226
2323
|
|
|
2227
2324
|
# The error handling here is a bit nuanced and also a bit broken:
|
|
@@ -2301,14 +2398,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
2301
2398
|
|
|
2302
2399
|
self._protocol_core.set_last_location(location=loc, mount=self.get_mount())
|
|
2303
2400
|
|
|
2304
|
-
# TODO(cm, 3.4.25): decide whether to allow users to try and do math on a potential SimulatedProbeResult
|
|
2305
2401
|
def liquid_probe_without_recovery(
|
|
2306
2402
|
self, well_core: WellCore, loc: Location
|
|
2307
2403
|
) -> LiquidTrackingType:
|
|
2308
2404
|
labware_id = well_core.labware_id
|
|
2309
2405
|
well_name = well_core.get_name()
|
|
2406
|
+
offset = LIQUID_PROBE_START_OFFSET_FROM_WELL_TOP
|
|
2310
2407
|
well_location = WellLocation(
|
|
2311
|
-
origin=WellOrigin.TOP, offset=WellOffset(x=
|
|
2408
|
+
origin=WellOrigin.TOP, offset=WellOffset(x=offset.x, y=offset.y, z=offset.z)
|
|
2312
2409
|
)
|
|
2313
2410
|
pipette_movement_conflict.check_safe_for_pipette_movement(
|
|
2314
2411
|
engine_state=self._engine_client.state,
|
|
@@ -4,10 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from opentrons_shared_data.liquid_classes import LiquidClassDefinitionDoesNotExist
|
|
7
|
-
|
|
8
|
-
from opentrons.protocol_engine import commands as cmd
|
|
9
|
-
from opentrons.protocol_engine.commands import LoadModuleResult
|
|
10
|
-
|
|
11
7
|
from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
|
|
12
8
|
from opentrons_shared_data.labware.labware_definition import (
|
|
13
9
|
labware_definition_type_adapter,
|
|
@@ -35,7 +31,8 @@ from opentrons.hardware_control.types import DoorState
|
|
|
35
31
|
from opentrons.protocols.api_support.util import AxisMaxSpeeds
|
|
36
32
|
from opentrons.protocols.api_support.types import APIVersion
|
|
37
33
|
|
|
38
|
-
|
|
34
|
+
from opentrons.protocol_engine import commands as cmd
|
|
35
|
+
from opentrons.protocol_engine.commands import LoadModuleResult
|
|
39
36
|
from opentrons.protocol_engine import (
|
|
40
37
|
DeckSlotLocation,
|
|
41
38
|
AddressableAreaLocation,
|
|
@@ -112,14 +109,14 @@ class ProtocolCore(
|
|
|
112
109
|
self._engine_client = engine_client
|
|
113
110
|
self._api_version = api_version
|
|
114
111
|
self._sync_hardware = sync_hardware
|
|
115
|
-
self._last_location: Optional[Location] = None
|
|
112
|
+
self._last_location: Optional[Union[Location, TrashBin, WasteChute]] = None
|
|
116
113
|
self._last_mount: Optional[Mount] = None
|
|
117
114
|
self._labware_cores_by_id: Dict[str, LabwareCore] = {}
|
|
118
115
|
self._module_cores_by_id: Dict[
|
|
119
116
|
str, Union[ModuleCore, NonConnectedModuleCore]
|
|
120
117
|
] = {}
|
|
121
118
|
self._disposal_locations: List[Union[Labware, TrashBin, WasteChute]] = []
|
|
122
|
-
self.
|
|
119
|
+
self._liquid_class_def_cache: Dict[Tuple[str, int], LiquidClassSchemaV1] = {}
|
|
123
120
|
self._load_fixed_trash()
|
|
124
121
|
|
|
125
122
|
@property
|
|
@@ -918,7 +915,7 @@ class ProtocolCore(
|
|
|
918
915
|
def get_last_location(
|
|
919
916
|
self,
|
|
920
917
|
mount: Optional[Mount] = None,
|
|
921
|
-
) -> Optional[Location]:
|
|
918
|
+
) -> Optional[Union[Location, TrashBin, WasteChute]]:
|
|
922
919
|
"""Get the last accessed location."""
|
|
923
920
|
if mount is None or mount == self._last_mount:
|
|
924
921
|
return self._last_location
|
|
@@ -927,7 +924,7 @@ class ProtocolCore(
|
|
|
927
924
|
|
|
928
925
|
def set_last_location(
|
|
929
926
|
self,
|
|
930
|
-
location: Optional[Location],
|
|
927
|
+
location: Optional[Union[Location, TrashBin, WasteChute]],
|
|
931
928
|
mount: Optional[Mount] = None,
|
|
932
929
|
) -> None:
|
|
933
930
|
"""Set the last accessed location."""
|
|
@@ -1097,20 +1094,22 @@ class ProtocolCore(
|
|
|
1097
1094
|
display_color=(liquid.displayColor.root if liquid.displayColor else None),
|
|
1098
1095
|
)
|
|
1099
1096
|
|
|
1100
|
-
def define_liquid_class(self, name: str) -> LiquidClass:
|
|
1097
|
+
def define_liquid_class(self, name: str, version: int) -> LiquidClass:
|
|
1101
1098
|
"""Define a liquid class for use in transfer functions."""
|
|
1102
1099
|
try:
|
|
1103
1100
|
# Check if we have already loaded this liquid class' definition
|
|
1104
|
-
liquid_class_def = self.
|
|
1101
|
+
liquid_class_def = self._liquid_class_def_cache[(name, version)]
|
|
1105
1102
|
except KeyError:
|
|
1106
1103
|
try:
|
|
1107
1104
|
# Fetching the liquid class data from file and parsing it
|
|
1108
1105
|
# is an expensive operation and should be avoided.
|
|
1109
1106
|
# Calling this often will degrade protocol execution performance.
|
|
1110
|
-
liquid_class_def = liquid_classes.load_definition(name)
|
|
1111
|
-
self.
|
|
1107
|
+
liquid_class_def = liquid_classes.load_definition(name, version=version)
|
|
1108
|
+
self._liquid_class_def_cache[(name, version)] = liquid_class_def
|
|
1112
1109
|
except LiquidClassDefinitionDoesNotExist:
|
|
1113
|
-
raise ValueError(
|
|
1110
|
+
raise ValueError(
|
|
1111
|
+
f"Liquid class definition not found for '{name}' version {version}."
|
|
1112
|
+
)
|
|
1114
1113
|
|
|
1115
1114
|
return LiquidClass.create(liquid_class_def)
|
|
1116
1115
|
|
|
@@ -131,9 +131,9 @@ class RobotCore(AbstractRobot):
|
|
|
131
131
|
)
|
|
132
132
|
|
|
133
133
|
def release_grip(self) -> None:
|
|
134
|
-
self._engine_client.execute_command(cmd.robot.
|
|
134
|
+
self._engine_client.execute_command(cmd.robot.OpenGripperJawParams())
|
|
135
135
|
|
|
136
136
|
def close_gripper(self, force: Optional[float] = None) -> None:
|
|
137
137
|
self._engine_client.execute_command(
|
|
138
|
-
cmd.robot.
|
|
138
|
+
cmd.robot.CloseGripperJawParams(force=force)
|
|
139
139
|
)
|