opentrons 8.4.0a2__py2.py3-none-any.whl → 8.4.0a4__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.
Potentially problematic release.
This version of opentrons might be problematic. Click here for more details.
- opentrons/legacy_commands/commands.py +83 -2
- opentrons/legacy_commands/helpers.py +59 -1
- opentrons/legacy_commands/types.py +30 -0
- opentrons/protocol_api/core/engine/instrument.py +158 -87
- opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
- opentrons/protocol_api/core/engine/transfer_components_executor.py +12 -23
- opentrons/protocol_api/core/instrument.py +7 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +7 -30
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +7 -4
- opentrons/protocol_api/core/well.py +1 -1
- opentrons/protocol_api/instrument_context.py +189 -75
- opentrons/protocol_api/labware.py +7 -6
- opentrons/protocol_api/protocol_context.py +18 -16
- opentrons/protocol_engine/commands/__init__.py +38 -38
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +0 -6
- opentrons/protocol_engine/commands/command_unions.py +33 -33
- opentrons/protocol_engine/commands/dispense_while_tracking.py +1 -6
- opentrons/protocol_engine/commands/flex_stacker/empty.py +6 -6
- opentrons/protocol_engine/commands/flex_stacker/fill.py +6 -6
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +6 -6
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +9 -9
- opentrons/protocol_engine/commands/flex_stacker/store.py +16 -13
- opentrons/protocol_engine/commands/labware_handling_common.py +6 -1
- opentrons/protocol_engine/commands/liquid_probe.py +1 -2
- opentrons/protocol_engine/commands/move_to_well.py +5 -11
- opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +27 -27
- opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +32 -27
- opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +22 -22
- opentrons/protocol_engine/labware_offset_standardization.py +22 -1
- opentrons/protocol_engine/resources/deck_configuration_provider.py +8 -4
- opentrons/protocol_engine/state/frustum_helpers.py +12 -4
- opentrons/protocol_engine/state/geometry.py +122 -73
- opentrons/protocol_engine/state/update_types.py +1 -1
- opentrons/protocol_engine/state/wells.py +1 -1
- opentrons/protocol_engine/types/__init__.py +6 -0
- opentrons/protocol_engine/types/location.py +2 -1
- opentrons/protocol_engine/types/well_position.py +18 -1
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
- opentrons/protocols/labware.py +23 -18
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/RECORD +45 -45
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a2.dist-info → opentrons-8.4.0a4.dist-info}/top_level.txt +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
from contextlib import contextmanager
|
|
5
|
+
from itertools import dropwhile
|
|
5
6
|
from typing import (
|
|
6
7
|
Optional,
|
|
7
8
|
TYPE_CHECKING,
|
|
@@ -48,8 +49,17 @@ from opentrons.protocol_engine.types import (
|
|
|
48
49
|
AddressableOffsetVector,
|
|
49
50
|
LiquidClassRecord,
|
|
50
51
|
NextTipInfo,
|
|
52
|
+
PickUpTipWellLocation,
|
|
53
|
+
LiquidHandlingWellLocation,
|
|
54
|
+
)
|
|
55
|
+
from opentrons.protocol_engine.types import (
|
|
56
|
+
LiquidTrackingType,
|
|
57
|
+
WellLocationFunction,
|
|
58
|
+
)
|
|
59
|
+
from opentrons.protocol_engine.types.automatic_tip_selection import (
|
|
60
|
+
NoTipAvailable,
|
|
61
|
+
NoTipReason,
|
|
51
62
|
)
|
|
52
|
-
from opentrons.protocol_engine.types.liquid_level_detection import LiquidTrackingType
|
|
53
63
|
from opentrons.protocol_engine.errors.exceptions import TipNotAttachedError
|
|
54
64
|
from opentrons.protocol_engine.clients import SyncClient as EngineClient
|
|
55
65
|
from opentrons.protocols.api_support.definitions import MAX_SUPPORTED_VERSION
|
|
@@ -59,6 +69,7 @@ from opentrons_shared_data.pipette.types import (
|
|
|
59
69
|
)
|
|
60
70
|
from opentrons_shared_data.errors.exceptions import (
|
|
61
71
|
UnsupportedHardwareCommand,
|
|
72
|
+
CommandPreconditionViolated,
|
|
62
73
|
)
|
|
63
74
|
from opentrons_shared_data.liquid_classes.liquid_class_definition import BlowoutLocation
|
|
64
75
|
from opentrons.protocol_api._nozzle_layout import NozzleLayout
|
|
@@ -222,10 +233,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
222
233
|
(
|
|
223
234
|
well_location,
|
|
224
235
|
dynamic_liquid_tracking,
|
|
225
|
-
) = self._engine_client.state.geometry.
|
|
236
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
226
237
|
labware_id=labware_id,
|
|
227
238
|
well_name=well_name,
|
|
228
239
|
absolute_point=location.point,
|
|
240
|
+
location_type=WellLocationFunction.LIQUID_HANDLING,
|
|
229
241
|
meniscus_tracking=meniscus_tracking,
|
|
230
242
|
)
|
|
231
243
|
pipette_movement_conflict.check_safe_for_pipette_movement(
|
|
@@ -235,8 +247,8 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
235
247
|
well_name=well_name,
|
|
236
248
|
well_location=well_location,
|
|
237
249
|
)
|
|
250
|
+
assert isinstance(well_location, LiquidHandlingWellLocation)
|
|
238
251
|
if dynamic_liquid_tracking:
|
|
239
|
-
|
|
240
252
|
self._engine_client.execute_command(
|
|
241
253
|
cmd.AspirateWhileTrackingParams(
|
|
242
254
|
pipetteId=self._pipette_id,
|
|
@@ -334,12 +346,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
334
346
|
(
|
|
335
347
|
well_location,
|
|
336
348
|
dynamic_liquid_tracking,
|
|
337
|
-
) = self._engine_client.state.geometry.
|
|
349
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
338
350
|
labware_id=labware_id,
|
|
339
351
|
well_name=well_name,
|
|
340
352
|
absolute_point=location.point,
|
|
353
|
+
location_type=WellLocationFunction.LIQUID_HANDLING,
|
|
341
354
|
meniscus_tracking=meniscus_tracking,
|
|
342
355
|
)
|
|
356
|
+
assert isinstance(well_location, LiquidHandlingWellLocation)
|
|
343
357
|
pipette_movement_conflict.check_safe_for_pipette_movement(
|
|
344
358
|
engine_state=self._engine_client.state,
|
|
345
359
|
pipette_id=self._pipette_id,
|
|
@@ -425,13 +439,16 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
425
439
|
well_name = well_core.get_name()
|
|
426
440
|
labware_id = well_core.labware_id
|
|
427
441
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
442
|
+
(
|
|
443
|
+
well_location,
|
|
444
|
+
_,
|
|
445
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
446
|
+
labware_id=labware_id,
|
|
447
|
+
well_name=well_name,
|
|
448
|
+
absolute_point=location.point,
|
|
449
|
+
location_type=WellLocationFunction.BASE,
|
|
434
450
|
)
|
|
451
|
+
|
|
435
452
|
pipette_movement_conflict.check_safe_for_pipette_movement(
|
|
436
453
|
engine_state=self._engine_client.state,
|
|
437
454
|
pipette_id=self._pipette_id,
|
|
@@ -439,6 +456,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
439
456
|
well_name=well_name,
|
|
440
457
|
well_location=well_location,
|
|
441
458
|
)
|
|
459
|
+
assert isinstance(well_location, WellLocation)
|
|
442
460
|
self._engine_client.execute_command(
|
|
443
461
|
cmd.BlowOutParams(
|
|
444
462
|
pipetteId=self._pipette_id,
|
|
@@ -533,12 +551,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
533
551
|
well_name = well_core.get_name()
|
|
534
552
|
labware_id = well_core.labware_id
|
|
535
553
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
554
|
+
(
|
|
555
|
+
well_location,
|
|
556
|
+
_,
|
|
557
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
558
|
+
labware_id=labware_id,
|
|
559
|
+
well_name=well_name,
|
|
560
|
+
absolute_point=location.point,
|
|
561
|
+
location_type=WellLocationFunction.PICK_UP_TIP,
|
|
542
562
|
)
|
|
543
563
|
pipette_movement_conflict.check_safe_for_tip_pickup_and_return(
|
|
544
564
|
engine_state=self._engine_client.state,
|
|
@@ -552,7 +572,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
552
572
|
well_name=well_name,
|
|
553
573
|
well_location=well_location,
|
|
554
574
|
)
|
|
555
|
-
|
|
575
|
+
assert isinstance(well_location, PickUpTipWellLocation)
|
|
556
576
|
self._engine_client.execute_command(
|
|
557
577
|
cmd.PickUpTipParams(
|
|
558
578
|
pipetteId=self._pipette_id,
|
|
@@ -590,12 +610,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
590
610
|
scrape_tips = False
|
|
591
611
|
|
|
592
612
|
if location is not None:
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
613
|
+
(
|
|
614
|
+
relative_well_location,
|
|
615
|
+
_,
|
|
616
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
617
|
+
labware_id=labware_id,
|
|
618
|
+
well_name=well_name,
|
|
619
|
+
absolute_point=location.point,
|
|
620
|
+
location_type=WellLocationFunction.DROP_TIP,
|
|
599
621
|
)
|
|
600
622
|
|
|
601
623
|
well_location = DropTipWellLocation(
|
|
@@ -731,14 +753,18 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
731
753
|
raise ValueError("Trash Bin and Waste Chute have no Wells.")
|
|
732
754
|
labware_id = well_core.labware_id
|
|
733
755
|
well_name = well_core.get_name()
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
756
|
+
(
|
|
757
|
+
well_location,
|
|
758
|
+
_,
|
|
759
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
760
|
+
labware_id=labware_id,
|
|
761
|
+
well_name=well_name,
|
|
762
|
+
absolute_point=location.point,
|
|
763
|
+
location_type=WellLocationFunction.LIQUID_HANDLING,
|
|
740
764
|
)
|
|
741
|
-
|
|
765
|
+
assert isinstance(well_location, LiquidHandlingWellLocation)
|
|
766
|
+
if well_location.volumeOffset and well_location.volumeOffset != 0:
|
|
767
|
+
raise ValueError("volume offset not supported with move_to")
|
|
742
768
|
self._engine_client.execute_command(
|
|
743
769
|
cmd.MoveToWellParams(
|
|
744
770
|
pipetteId=self._pipette_id,
|
|
@@ -779,16 +805,18 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
779
805
|
) -> None:
|
|
780
806
|
labware_id = well_core.labware_id
|
|
781
807
|
well_name = well_core.get_name()
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
808
|
+
(
|
|
809
|
+
well_location,
|
|
810
|
+
_,
|
|
811
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
812
|
+
labware_id=labware_id,
|
|
813
|
+
well_name=well_name,
|
|
814
|
+
absolute_point=location.point,
|
|
815
|
+
location_type=WellLocationFunction.PICK_UP_TIP,
|
|
788
816
|
)
|
|
789
|
-
|
|
817
|
+
assert isinstance(well_location, PickUpTipWellLocation)
|
|
790
818
|
self._engine_client.execute_command(
|
|
791
|
-
cmd.
|
|
819
|
+
cmd.SealPipetteToTipParams(
|
|
792
820
|
pipetteId=self._pipette_id,
|
|
793
821
|
labwareId=labware_id,
|
|
794
822
|
wellName=well_name,
|
|
@@ -801,12 +829,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
801
829
|
labware_id = well_core.labware_id
|
|
802
830
|
|
|
803
831
|
if location is not None:
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
832
|
+
(
|
|
833
|
+
relative_well_location,
|
|
834
|
+
_,
|
|
835
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
836
|
+
labware_id=labware_id,
|
|
837
|
+
well_name=well_name,
|
|
838
|
+
absolute_point=location.point,
|
|
839
|
+
location_type=WellLocationFunction.BASE,
|
|
810
840
|
)
|
|
811
841
|
|
|
812
842
|
well_location = DropTipWellLocation(
|
|
@@ -824,7 +854,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
824
854
|
well_location=well_location,
|
|
825
855
|
)
|
|
826
856
|
self._engine_client.execute_command(
|
|
827
|
-
cmd.
|
|
857
|
+
cmd.UnsealPipetteFromTipParams(
|
|
828
858
|
pipetteId=self._pipette_id,
|
|
829
859
|
labwareId=labware_id,
|
|
830
860
|
wellName=well_name,
|
|
@@ -860,10 +890,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
860
890
|
(
|
|
861
891
|
well_location,
|
|
862
892
|
dynamic_tracking,
|
|
863
|
-
) = self._engine_client.state.geometry.
|
|
893
|
+
) = self._engine_client.state.geometry.get_relative_well_location(
|
|
864
894
|
labware_id=labware_id,
|
|
865
895
|
well_name=well_name,
|
|
866
896
|
absolute_point=location.point,
|
|
897
|
+
location_type=WellLocationFunction.LIQUID_HANDLING,
|
|
867
898
|
)
|
|
868
899
|
pipette_movement_conflict.check_safe_for_pipette_movement(
|
|
869
900
|
engine_state=self._engine_client.state,
|
|
@@ -872,8 +903,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
872
903
|
well_name=well_name,
|
|
873
904
|
well_location=well_location,
|
|
874
905
|
)
|
|
906
|
+
assert isinstance(well_location, LiquidHandlingWellLocation)
|
|
875
907
|
self._engine_client.execute_command(
|
|
876
|
-
cmd.
|
|
908
|
+
cmd.PressureDispenseParams(
|
|
877
909
|
pipetteId=self._pipette_id,
|
|
878
910
|
labwareId=labware_id,
|
|
879
911
|
wellName=well_name,
|
|
@@ -1115,9 +1147,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1115
1147
|
tiprack=tiprack_uri,
|
|
1116
1148
|
aspirate=transfer_properties.aspirate.as_shared_data_model(),
|
|
1117
1149
|
singleDispense=transfer_properties.dispense.as_shared_data_model(),
|
|
1118
|
-
multiDispense=
|
|
1119
|
-
|
|
1120
|
-
|
|
1150
|
+
multiDispense=(
|
|
1151
|
+
transfer_properties.multi_dispense.as_shared_data_model()
|
|
1152
|
+
if transfer_properties.multi_dispense
|
|
1153
|
+
else None
|
|
1154
|
+
),
|
|
1121
1155
|
)
|
|
1122
1156
|
result = self._engine_client.execute_command_without_recovery(
|
|
1123
1157
|
cmd.LoadLiquidClassParams(
|
|
@@ -1127,21 +1161,41 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1127
1161
|
return result.liquidClassId
|
|
1128
1162
|
|
|
1129
1163
|
def get_next_tip(
|
|
1130
|
-
self, tip_racks: List[LabwareCore], starting_well: Optional[
|
|
1164
|
+
self, tip_racks: List[LabwareCore], starting_well: Optional[WellCore]
|
|
1131
1165
|
) -> Optional[NextTipInfo]:
|
|
1132
1166
|
"""Get the next tip to pick up."""
|
|
1167
|
+
if starting_well is not None:
|
|
1168
|
+
# Drop tip racks until the one with the starting tip is reached (if any)
|
|
1169
|
+
valid_tip_racks = list(
|
|
1170
|
+
dropwhile(
|
|
1171
|
+
lambda tr: starting_well.labware_id != tr.labware_id, tip_racks
|
|
1172
|
+
)
|
|
1173
|
+
)
|
|
1174
|
+
else:
|
|
1175
|
+
valid_tip_racks = tip_racks
|
|
1176
|
+
|
|
1133
1177
|
result = self._engine_client.execute_command_without_recovery(
|
|
1134
1178
|
cmd.GetNextTipParams(
|
|
1135
1179
|
pipetteId=self._pipette_id,
|
|
1136
|
-
labwareIds=[tip_rack.labware_id for tip_rack in
|
|
1137
|
-
startingTipWell=starting_well
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1180
|
+
labwareIds=[tip_rack.labware_id for tip_rack in valid_tip_racks],
|
|
1181
|
+
startingTipWell=starting_well.get_name()
|
|
1182
|
+
if starting_well is not None
|
|
1183
|
+
else None,
|
|
1184
|
+
)
|
|
1185
|
+
)
|
|
1186
|
+
next_tip_info = result.nextTipInfo
|
|
1187
|
+
if isinstance(next_tip_info, NoTipAvailable):
|
|
1188
|
+
if next_tip_info.noTipReason == NoTipReason.STARTING_TIP_WITH_PARTIAL:
|
|
1189
|
+
raise CommandPreconditionViolated(
|
|
1190
|
+
"Automatic tip tracking is not available when using a partial pipette"
|
|
1191
|
+
" nozzle configuration and InstrumentContext.starting_tip."
|
|
1192
|
+
" Switch to a full configuration or set starting_tip to None."
|
|
1193
|
+
)
|
|
1194
|
+
return None
|
|
1195
|
+
else:
|
|
1196
|
+
return next_tip_info
|
|
1143
1197
|
|
|
1144
|
-
def
|
|
1198
|
+
def transfer_with_liquid_class( # noqa: C901
|
|
1145
1199
|
self,
|
|
1146
1200
|
liquid_class: LiquidClass,
|
|
1147
1201
|
volume: float,
|
|
@@ -1149,6 +1203,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1149
1203
|
dest: List[Tuple[Location, WellCore]],
|
|
1150
1204
|
new_tip: TransferTipPolicyV2,
|
|
1151
1205
|
tip_racks: List[Tuple[Location, LabwareCore]],
|
|
1206
|
+
starting_tip: Optional[WellCore],
|
|
1152
1207
|
trash_location: Union[Location, TrashBin, WasteChute],
|
|
1153
1208
|
return_tip: bool,
|
|
1154
1209
|
) -> None:
|
|
@@ -1234,11 +1289,12 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1234
1289
|
def _pick_up_tip() -> WellCore:
|
|
1235
1290
|
next_tip = self.get_next_tip(
|
|
1236
1291
|
tip_racks=[core for loc, core in tip_racks],
|
|
1237
|
-
starting_well=
|
|
1292
|
+
starting_well=starting_tip,
|
|
1238
1293
|
)
|
|
1239
1294
|
if next_tip is None:
|
|
1240
1295
|
raise RuntimeError(
|
|
1241
|
-
f"No tip available among
|
|
1296
|
+
f"No tip available among the tipracks assigned for {self.get_pipette_name()}:"
|
|
1297
|
+
f" {[f'{tip_rack[1].get_display_name()} in {tip_rack[1].get_deck_slot()}' for tip_rack in tip_racks]}"
|
|
1242
1298
|
)
|
|
1243
1299
|
(
|
|
1244
1300
|
tiprack_loc,
|
|
@@ -1325,9 +1381,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1325
1381
|
transfer_properties=transfer_props,
|
|
1326
1382
|
transfer_type=tx_comps_executor.TransferType.ONE_TO_ONE,
|
|
1327
1383
|
tip_contents=post_asp_tip_contents,
|
|
1328
|
-
add_final_air_gap=
|
|
1329
|
-
|
|
1330
|
-
|
|
1384
|
+
add_final_air_gap=(
|
|
1385
|
+
False
|
|
1386
|
+
if is_last_step and new_tip == TransferTipPolicyV2.NEVER
|
|
1387
|
+
else True
|
|
1388
|
+
),
|
|
1331
1389
|
trash_location=trash_location,
|
|
1332
1390
|
)
|
|
1333
1391
|
prev_src = step_source
|
|
@@ -1335,7 +1393,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1335
1393
|
_drop_tip()
|
|
1336
1394
|
|
|
1337
1395
|
# TODO(spp, 2025-02-25): wire up return tip
|
|
1338
|
-
def
|
|
1396
|
+
def distribute_with_liquid_class( # noqa: C901
|
|
1339
1397
|
self,
|
|
1340
1398
|
liquid_class: LiquidClass,
|
|
1341
1399
|
volume: float,
|
|
@@ -1343,6 +1401,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1343
1401
|
dest: List[Tuple[Location, WellCore]],
|
|
1344
1402
|
new_tip: TransferTipPolicyV2,
|
|
1345
1403
|
tip_racks: List[Tuple[Location, LabwareCore]],
|
|
1404
|
+
starting_tip: Optional[WellCore],
|
|
1346
1405
|
trash_location: Union[Location, TrashBin, WasteChute],
|
|
1347
1406
|
return_tip: bool,
|
|
1348
1407
|
) -> None:
|
|
@@ -1417,13 +1476,14 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1417
1476
|
tip_working_volume=working_volume,
|
|
1418
1477
|
)
|
|
1419
1478
|
):
|
|
1420
|
-
self.
|
|
1479
|
+
self.transfer_with_liquid_class(
|
|
1421
1480
|
liquid_class=liquid_class,
|
|
1422
1481
|
volume=volume,
|
|
1423
1482
|
source=[source for _ in range(len(dest))],
|
|
1424
1483
|
dest=dest,
|
|
1425
1484
|
new_tip=new_tip,
|
|
1426
1485
|
tip_racks=tip_racks,
|
|
1486
|
+
starting_tip=starting_tip,
|
|
1427
1487
|
trash_location=trash_location,
|
|
1428
1488
|
return_tip=return_tip,
|
|
1429
1489
|
)
|
|
@@ -1474,11 +1534,12 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1474
1534
|
def _pick_up_tip() -> WellCore:
|
|
1475
1535
|
next_tip = self.get_next_tip(
|
|
1476
1536
|
tip_racks=[core for loc, core in tip_racks],
|
|
1477
|
-
starting_well=
|
|
1537
|
+
starting_well=starting_tip,
|
|
1478
1538
|
)
|
|
1479
1539
|
if next_tip is None:
|
|
1480
1540
|
raise RuntimeError(
|
|
1481
|
-
f"No tip available among
|
|
1541
|
+
f"No tip available among the tipracks assigned for {self.get_pipette_name()}:"
|
|
1542
|
+
f" {[f'{tip_rack[1].get_display_name()} in {tip_rack[1].get_deck_slot()}' for tip_rack in tip_racks]}"
|
|
1482
1543
|
)
|
|
1483
1544
|
(
|
|
1484
1545
|
tiprack_loc,
|
|
@@ -1569,7 +1630,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1569
1630
|
and not transfer_props.multi_dispense.retract.blowout.enabled
|
|
1570
1631
|
):
|
|
1571
1632
|
raise RuntimeError(
|
|
1572
|
-
"Distribute
|
|
1633
|
+
"Distribute uses a disposal volume but location for disposing of"
|
|
1573
1634
|
" the disposal volume cannot be found when blowout is disabled."
|
|
1574
1635
|
" Specify a blowout location and enable blowout when using a disposal volume."
|
|
1575
1636
|
)
|
|
@@ -1610,9 +1671,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1610
1671
|
transfer_properties=transfer_props,
|
|
1611
1672
|
transfer_type=tx_comps_executor.TransferType.ONE_TO_MANY,
|
|
1612
1673
|
tip_contents=tip_contents,
|
|
1613
|
-
add_final_air_gap=
|
|
1614
|
-
|
|
1615
|
-
|
|
1674
|
+
add_final_air_gap=(
|
|
1675
|
+
False
|
|
1676
|
+
if is_last_step and new_tip == TransferTipPolicyV2.NEVER
|
|
1677
|
+
else True
|
|
1678
|
+
),
|
|
1616
1679
|
trash_location=trash_location,
|
|
1617
1680
|
)
|
|
1618
1681
|
else:
|
|
@@ -1623,9 +1686,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1623
1686
|
transfer_properties=transfer_props,
|
|
1624
1687
|
transfer_type=tx_comps_executor.TransferType.ONE_TO_MANY,
|
|
1625
1688
|
tip_contents=tip_contents,
|
|
1626
|
-
add_final_air_gap=
|
|
1627
|
-
|
|
1628
|
-
|
|
1689
|
+
add_final_air_gap=(
|
|
1690
|
+
False
|
|
1691
|
+
if is_last_step and new_tip == TransferTipPolicyV2.NEVER
|
|
1692
|
+
else True
|
|
1693
|
+
),
|
|
1629
1694
|
trash_location=trash_location,
|
|
1630
1695
|
conditioning_volume=conditioning_vol,
|
|
1631
1696
|
disposal_volume=disposal_vol,
|
|
@@ -1657,7 +1722,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1657
1722
|
<= tip_working_volume
|
|
1658
1723
|
)
|
|
1659
1724
|
|
|
1660
|
-
def
|
|
1725
|
+
def consolidate_with_liquid_class( # noqa: C901
|
|
1661
1726
|
self,
|
|
1662
1727
|
liquid_class: LiquidClass,
|
|
1663
1728
|
volume: float,
|
|
@@ -1665,6 +1730,7 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1665
1730
|
dest: Tuple[Location, WellCore],
|
|
1666
1731
|
new_tip: TransferTipPolicyV2,
|
|
1667
1732
|
tip_racks: List[Tuple[Location, LabwareCore]],
|
|
1733
|
+
starting_tip: Optional[WellCore],
|
|
1668
1734
|
trash_location: Union[Location, TrashBin, WasteChute],
|
|
1669
1735
|
return_tip: bool,
|
|
1670
1736
|
) -> None:
|
|
@@ -1747,11 +1813,12 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1747
1813
|
def _pick_up_tip() -> WellCore:
|
|
1748
1814
|
next_tip = self.get_next_tip(
|
|
1749
1815
|
tip_racks=[core for loc, core in tip_racks],
|
|
1750
|
-
starting_well=
|
|
1816
|
+
starting_well=starting_tip,
|
|
1751
1817
|
)
|
|
1752
1818
|
if next_tip is None:
|
|
1753
1819
|
raise RuntimeError(
|
|
1754
|
-
f"No tip available among
|
|
1820
|
+
f"No tip available among the tipracks assigned for {self.get_pipette_name()}:"
|
|
1821
|
+
f" {[ f'{tip_rack[1].get_display_name()} in {tip_rack[1].get_deck_slot()}' for tip_rack in tip_racks]}"
|
|
1755
1822
|
)
|
|
1756
1823
|
(
|
|
1757
1824
|
tiprack_loc,
|
|
@@ -1817,9 +1884,9 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1817
1884
|
transfer_properties=transfer_props,
|
|
1818
1885
|
transfer_type=tx_comps_executor.TransferType.MANY_TO_ONE,
|
|
1819
1886
|
tip_contents=tip_contents,
|
|
1820
|
-
volume_for_pipette_mode_configuration=
|
|
1821
|
-
|
|
1822
|
-
|
|
1887
|
+
volume_for_pipette_mode_configuration=(
|
|
1888
|
+
total_dispense_volume if step_num == 0 else None
|
|
1889
|
+
),
|
|
1823
1890
|
)
|
|
1824
1891
|
tip_contents = self.dispense_liquid_class(
|
|
1825
1892
|
volume=total_dispense_volume,
|
|
@@ -1828,9 +1895,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1828
1895
|
transfer_properties=transfer_props,
|
|
1829
1896
|
transfer_type=tx_comps_executor.TransferType.MANY_TO_ONE,
|
|
1830
1897
|
tip_contents=tip_contents,
|
|
1831
|
-
add_final_air_gap=
|
|
1832
|
-
|
|
1833
|
-
|
|
1898
|
+
add_final_air_gap=(
|
|
1899
|
+
False
|
|
1900
|
+
if is_last_step and new_tip == TransferTipPolicyV2.NEVER
|
|
1901
|
+
else True
|
|
1902
|
+
),
|
|
1834
1903
|
trash_location=trash_location,
|
|
1835
1904
|
)
|
|
1836
1905
|
prev_src = next_source
|
|
@@ -1881,9 +1950,11 @@ class InstrumentCore(AbstractInstrument[WellCore, LabwareCore]):
|
|
|
1881
1950
|
aspirate_props = transfer_properties.aspirate
|
|
1882
1951
|
tx_commons.check_valid_liquid_class_volume_parameters(
|
|
1883
1952
|
aspirate_volume=volume,
|
|
1884
|
-
air_gap=
|
|
1885
|
-
|
|
1886
|
-
|
|
1953
|
+
air_gap=(
|
|
1954
|
+
aspirate_props.retract.air_gap_by_volume.get_for_volume(volume)
|
|
1955
|
+
if conditioning_volume is None
|
|
1956
|
+
else 0
|
|
1957
|
+
),
|
|
1887
1958
|
disposal_volume=0, # Disposal volume is accounted for in aspirate vol
|
|
1888
1959
|
max_volume=self.get_working_volume(),
|
|
1889
1960
|
)
|
|
@@ -19,14 +19,9 @@ from opentrons.protocol_engine import (
|
|
|
19
19
|
StateView,
|
|
20
20
|
DeckSlotLocation,
|
|
21
21
|
OnLabwareLocation,
|
|
22
|
-
WellLocation,
|
|
23
|
-
LiquidHandlingWellLocation,
|
|
24
|
-
PickUpTipWellLocation,
|
|
25
22
|
DropTipWellLocation,
|
|
26
23
|
)
|
|
27
|
-
from opentrons.protocol_engine.types import
|
|
28
|
-
StagingSlotLocation,
|
|
29
|
-
)
|
|
24
|
+
from opentrons.protocol_engine.types import StagingSlotLocation, WellLocationType
|
|
30
25
|
from opentrons.types import DeckSlotName, StagingSlotName, Point
|
|
31
26
|
from . import point_calculations
|
|
32
27
|
|
|
@@ -69,12 +64,7 @@ def check_safe_for_pipette_movement( # noqa: C901
|
|
|
69
64
|
pipette_id: str,
|
|
70
65
|
labware_id: str,
|
|
71
66
|
well_name: str,
|
|
72
|
-
well_location:
|
|
73
|
-
WellLocation,
|
|
74
|
-
LiquidHandlingWellLocation,
|
|
75
|
-
PickUpTipWellLocation,
|
|
76
|
-
DropTipWellLocation,
|
|
77
|
-
],
|
|
67
|
+
well_location: WellLocationType,
|
|
78
68
|
) -> None:
|
|
79
69
|
"""Check if the labware is safe to move to with a pipette in partial tip configuration.
|
|
80
70
|
|
|
@@ -100,12 +90,14 @@ def check_safe_for_pipette_movement( # noqa: C901
|
|
|
100
90
|
partially_configured=True,
|
|
101
91
|
)
|
|
102
92
|
well_location_point = engine_state.geometry.get_well_position(
|
|
103
|
-
labware_id=labware_id,
|
|
93
|
+
labware_id=labware_id,
|
|
94
|
+
well_name=well_name,
|
|
95
|
+
well_location=well_location,
|
|
96
|
+
pipette_id=pipette_id,
|
|
104
97
|
)
|
|
105
98
|
primary_nozzle = engine_state.pipettes.get_primary_nozzle(pipette_id)
|
|
106
99
|
|
|
107
100
|
destination_cp = _get_critical_point_to_use(engine_state, labware_id)
|
|
108
|
-
|
|
109
101
|
pipette_bounds_at_well_location = (
|
|
110
102
|
engine_state.pipettes.get_pipette_bounds_at_specified_move_to_position(
|
|
111
103
|
pipette_id=pipette_id,
|
|
@@ -180,7 +180,6 @@ class TransferComponentsExecutor:
|
|
|
180
180
|
# TODO: do volume configuration + prepare for aspirate only if the mode needs to be changed
|
|
181
181
|
self._instrument.configure_for_volume(volume_for_pipette_mode_configuration) # type: ignore[arg-type]
|
|
182
182
|
self._instrument.prepare_to_aspirate()
|
|
183
|
-
|
|
184
183
|
tx_utils.raise_if_location_inside_liquid(
|
|
185
184
|
location=submerge_start_location,
|
|
186
185
|
well_location=self._target_location,
|
|
@@ -207,8 +206,7 @@ class TransferComponentsExecutor:
|
|
|
207
206
|
minimum_z_height=None,
|
|
208
207
|
speed=submerge_properties.speed,
|
|
209
208
|
)
|
|
210
|
-
if submerge_properties.delay.enabled:
|
|
211
|
-
assert submerge_properties.delay.duration is not None
|
|
209
|
+
if submerge_properties.delay.enabled and submerge_properties.delay.duration:
|
|
212
210
|
self._instrument.delay(submerge_properties.delay.duration)
|
|
213
211
|
|
|
214
212
|
def aspirate_and_wait(self, volume: float) -> None:
|
|
@@ -227,9 +225,7 @@ class TransferComponentsExecutor:
|
|
|
227
225
|
)
|
|
228
226
|
self._tip_state.append_liquid(volume)
|
|
229
227
|
delay_props = aspirate_props.delay
|
|
230
|
-
if delay_props.enabled:
|
|
231
|
-
# Assertion only for mypy purposes
|
|
232
|
-
assert delay_props.duration is not None
|
|
228
|
+
if delay_props.enabled and delay_props.duration:
|
|
233
229
|
self._instrument.delay(delay_props.duration)
|
|
234
230
|
|
|
235
231
|
def dispense_and_wait(
|
|
@@ -257,8 +253,7 @@ class TransferComponentsExecutor:
|
|
|
257
253
|
self._tip_state.ready_to_aspirate = False
|
|
258
254
|
self._tip_state.delete_liquid(volume)
|
|
259
255
|
dispense_delay = dispense_properties.delay
|
|
260
|
-
if dispense_delay.enabled:
|
|
261
|
-
assert dispense_delay.duration is not None
|
|
256
|
+
if dispense_delay.enabled and dispense_delay.duration:
|
|
262
257
|
self._instrument.delay(dispense_delay.duration)
|
|
263
258
|
|
|
264
259
|
def mix(self, mix_properties: MixProperties, last_dispense_push_out: bool) -> None:
|
|
@@ -361,8 +356,7 @@ class TransferComponentsExecutor:
|
|
|
361
356
|
speed=retract_props.speed,
|
|
362
357
|
)
|
|
363
358
|
retract_delay = retract_props.delay
|
|
364
|
-
if retract_delay.enabled:
|
|
365
|
-
assert retract_delay.duration is not None
|
|
359
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
366
360
|
self._instrument.delay(retract_delay.duration)
|
|
367
361
|
touch_tip_props = retract_props.touch_tip
|
|
368
362
|
if touch_tip_props.enabled:
|
|
@@ -459,8 +453,7 @@ class TransferComponentsExecutor:
|
|
|
459
453
|
speed=retract_props.speed,
|
|
460
454
|
)
|
|
461
455
|
retract_delay = retract_props.delay
|
|
462
|
-
if retract_delay.enabled:
|
|
463
|
-
assert retract_delay.duration is not None
|
|
456
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
464
457
|
self._instrument.delay(retract_delay.duration)
|
|
465
458
|
|
|
466
459
|
blowout_props = retract_props.blowout
|
|
@@ -599,8 +592,7 @@ class TransferComponentsExecutor:
|
|
|
599
592
|
speed=retract_props.speed,
|
|
600
593
|
)
|
|
601
594
|
retract_delay = retract_props.delay
|
|
602
|
-
if retract_delay.enabled:
|
|
603
|
-
assert retract_delay.duration is not None
|
|
595
|
+
if retract_delay.enabled and retract_delay.duration:
|
|
604
596
|
self._instrument.delay(retract_delay.duration)
|
|
605
597
|
|
|
606
598
|
blowout_props = retract_props.blowout
|
|
@@ -780,8 +772,8 @@ class TransferComponentsExecutor:
|
|
|
780
772
|
correction_volume = aspirate_props.correction_by_volume.get_for_volume(
|
|
781
773
|
air_gap_volume
|
|
782
774
|
)
|
|
783
|
-
# The
|
|
784
|
-
flow_rate =
|
|
775
|
+
# The minimum flow rate should be air_gap_volume per second
|
|
776
|
+
flow_rate = max(
|
|
785
777
|
aspirate_props.flow_rate_by_volume.get_for_volume(air_gap_volume),
|
|
786
778
|
air_gap_volume,
|
|
787
779
|
)
|
|
@@ -791,9 +783,7 @@ class TransferComponentsExecutor:
|
|
|
791
783
|
correction_volume=correction_volume,
|
|
792
784
|
)
|
|
793
785
|
delay_props = aspirate_props.delay
|
|
794
|
-
if delay_props.enabled:
|
|
795
|
-
# Assertion only for mypy purposes
|
|
796
|
-
assert delay_props.duration is not None
|
|
786
|
+
if delay_props.enabled and delay_props.duration:
|
|
797
787
|
self._instrument.delay(delay_props.duration)
|
|
798
788
|
self._tip_state.append_air_gap(air_gap_volume)
|
|
799
789
|
|
|
@@ -807,8 +797,8 @@ class TransferComponentsExecutor:
|
|
|
807
797
|
correction_volume = dispense_props.correction_by_volume.get_for_volume(
|
|
808
798
|
last_air_gap
|
|
809
799
|
)
|
|
810
|
-
# The
|
|
811
|
-
flow_rate =
|
|
800
|
+
# The minimum flow rate should be air_gap_volume per second
|
|
801
|
+
flow_rate = max(
|
|
812
802
|
dispense_props.flow_rate_by_volume.get_for_volume(last_air_gap),
|
|
813
803
|
last_air_gap,
|
|
814
804
|
)
|
|
@@ -824,8 +814,7 @@ class TransferComponentsExecutor:
|
|
|
824
814
|
)
|
|
825
815
|
self._tip_state.delete_air_gap(last_air_gap)
|
|
826
816
|
dispense_delay = dispense_props.delay
|
|
827
|
-
if dispense_delay.enabled:
|
|
828
|
-
assert dispense_delay.duration is not None
|
|
817
|
+
if dispense_delay.enabled and dispense_delay.duration:
|
|
829
818
|
self._instrument.delay(dispense_delay.duration)
|
|
830
819
|
|
|
831
820
|
|