opentrons 8.4.1a2__py2.py3-none-any.whl → 8.5.0__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/hardware_control/backends/flex_protocol.py +25 -0
- opentrons/hardware_control/backends/ot3controller.py +76 -1
- opentrons/hardware_control/backends/ot3simulator.py +27 -0
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +1 -0
- opentrons/hardware_control/ot3api.py +32 -0
- 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 +149 -90
- opentrons/protocol_api/_transfer_liquid_validation.py +43 -14
- opentrons/protocol_api/core/engine/instrument.py +367 -221
- opentrons/protocol_api/core/engine/protocol.py +14 -15
- opentrons/protocol_api/core/engine/robot.py +2 -2
- opentrons/protocol_api/core/engine/transfer_components_executor.py +275 -163
- opentrons/protocol_api/core/engine/well.py +16 -0
- opentrons/protocol_api/core/instrument.py +11 -5
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +11 -5
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +2 -2
- opentrons/protocol_api/core/legacy/legacy_well_core.py +8 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +11 -5
- opentrons/protocol_api/core/protocol.py +3 -3
- opentrons/protocol_api/core/well.py +8 -0
- opentrons/protocol_api/instrument_context.py +478 -111
- opentrons/protocol_api/labware.py +10 -0
- opentrons/protocol_api/module_contexts.py +5 -2
- opentrons/protocol_api/protocol_context.py +76 -11
- opentrons/protocol_api/robot_context.py +48 -6
- opentrons/protocol_api/validation.py +15 -8
- 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/load_labware.py +0 -19
- 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/resources/labware_validation.py +7 -1
- 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/labware.py +12 -0
- 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_engine/types/liquid_level_detection.py +68 -8
- opentrons/protocol_runner/legacy_command_mapper.py +12 -6
- 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 +55 -28
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/types.py +6 -6
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/METADATA +4 -4
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/RECORD +67 -66
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/LICENSE +0 -0
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/WHEEL +0 -0
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.1a2.dist-info → opentrons-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from numpy import interp
|
|
3
|
-
from typing import Optional, Dict, Sequence, Tuple, List
|
|
3
|
+
from typing import Optional, Dict, Sequence, Tuple, List, Union
|
|
4
4
|
|
|
5
5
|
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
6
|
+
TransferProperties as SharedDataTransferProperties,
|
|
6
7
|
AspirateProperties as SharedDataAspirateProperties,
|
|
7
8
|
SingleDispenseProperties as SharedDataSingleDispenseProperties,
|
|
8
9
|
MultiDispenseProperties as SharedDataMultiDispenseProperties,
|
|
10
|
+
TipPosition as SharedDataTipPosition,
|
|
9
11
|
DelayProperties as SharedDataDelayProperties,
|
|
10
12
|
DelayParams as SharedDataDelayParams,
|
|
11
13
|
TouchTipProperties as SharedDataTouchTipProperties,
|
|
@@ -22,12 +24,12 @@ from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
|
22
24
|
PositionReference,
|
|
23
25
|
Coordinate,
|
|
24
26
|
)
|
|
25
|
-
|
|
26
27
|
from . import validation
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
class LiquidHandlingPropertyByVolume:
|
|
30
31
|
def __init__(self, by_volume_property: Sequence[Tuple[float, float]]) -> None:
|
|
32
|
+
self._initial_properties_by_volume = by_volume_property
|
|
31
33
|
self._properties_by_volume: Dict[float, float] = {
|
|
32
34
|
float(volume): value for volume, value in by_volume_property
|
|
33
35
|
}
|
|
@@ -60,6 +62,11 @@ class LiquidHandlingPropertyByVolume:
|
|
|
60
62
|
interp(validated_volume, self._sorted_volumes, self._sorted_values)
|
|
61
63
|
)
|
|
62
64
|
|
|
65
|
+
def set_for_all_volumes(self, value: float) -> None:
|
|
66
|
+
"""Override all existing volume-dependent values with the given value."""
|
|
67
|
+
self.clear_values()
|
|
68
|
+
self.set_for_volume(0, value)
|
|
69
|
+
|
|
63
70
|
def set_for_volume(self, volume: float, value: float) -> None:
|
|
64
71
|
"""Add a new volume and value for the property for the interpolation curve."""
|
|
65
72
|
validated_volume = validation.ensure_positive_float(volume)
|
|
@@ -74,6 +81,17 @@ class LiquidHandlingPropertyByVolume:
|
|
|
74
81
|
raise KeyError(f"No value set for volume {volume} uL")
|
|
75
82
|
self._sort_volume_and_values()
|
|
76
83
|
|
|
84
|
+
def clear_values(self) -> None:
|
|
85
|
+
"""Removes all existing volume and value pairs from the curve."""
|
|
86
|
+
self._properties_by_volume = {}
|
|
87
|
+
|
|
88
|
+
def reset_values(self) -> None:
|
|
89
|
+
"""Resets volumes and values to the default."""
|
|
90
|
+
self._properties_by_volume = {
|
|
91
|
+
float(volume): value for volume, value in self._initial_properties_by_volume
|
|
92
|
+
}
|
|
93
|
+
self._sort_volume_and_values()
|
|
94
|
+
|
|
77
95
|
def _sort_volume_and_values(self) -> None:
|
|
78
96
|
"""Sort volume in increasing order along with corresponding values in matching order."""
|
|
79
97
|
self._sorted_volumes, self._sorted_values = (
|
|
@@ -86,6 +104,48 @@ class LiquidHandlingPropertyByVolume:
|
|
|
86
104
|
# We use slots for this dataclass (and the rest of liquid properties) to prevent dynamic creation of attributes
|
|
87
105
|
# not defined in the class, not for any performance reasons. This is so that mistyping properties when overriding
|
|
88
106
|
# values will cause the protocol to fail analysis, rather than silently passing.
|
|
107
|
+
@dataclass(slots=True)
|
|
108
|
+
class TipPosition:
|
|
109
|
+
|
|
110
|
+
_position_reference: PositionReference
|
|
111
|
+
_offset: Coordinate
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def position_reference(self) -> PositionReference:
|
|
115
|
+
return self._position_reference
|
|
116
|
+
|
|
117
|
+
@position_reference.setter
|
|
118
|
+
def position_reference(self, new_position: Union[str, PositionReference]) -> None:
|
|
119
|
+
self._position_reference = (
|
|
120
|
+
new_position
|
|
121
|
+
if isinstance(new_position, PositionReference)
|
|
122
|
+
else PositionReference(new_position)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def offset(self) -> Coordinate:
|
|
127
|
+
return self._offset
|
|
128
|
+
|
|
129
|
+
@offset.setter
|
|
130
|
+
def offset(self, new_offset: Union[Sequence[float], Coordinate]) -> None:
|
|
131
|
+
if isinstance(new_offset, Coordinate):
|
|
132
|
+
new_coordinate: Sequence[Union[int, float]] = [
|
|
133
|
+
new_offset.x,
|
|
134
|
+
new_offset.y,
|
|
135
|
+
new_offset.z,
|
|
136
|
+
]
|
|
137
|
+
else:
|
|
138
|
+
new_coordinate = new_offset
|
|
139
|
+
x, y, z = validation.validate_coordinates(new_coordinate)
|
|
140
|
+
self._offset = Coordinate(x=x, y=y, z=z)
|
|
141
|
+
|
|
142
|
+
def as_shared_data_model(self) -> SharedDataTipPosition:
|
|
143
|
+
return SharedDataTipPosition(
|
|
144
|
+
positionReference=self._position_reference,
|
|
145
|
+
offset=self.offset,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
89
149
|
@dataclass(slots=True)
|
|
90
150
|
class DelayProperties:
|
|
91
151
|
|
|
@@ -126,7 +186,7 @@ class TouchTipProperties:
|
|
|
126
186
|
|
|
127
187
|
_enabled: bool
|
|
128
188
|
_z_offset: Optional[float]
|
|
129
|
-
|
|
189
|
+
_mm_from_edge: Optional[float]
|
|
130
190
|
_speed: Optional[float]
|
|
131
191
|
|
|
132
192
|
@property
|
|
@@ -137,10 +197,10 @@ class TouchTipProperties:
|
|
|
137
197
|
def enabled(self, enable: bool) -> None:
|
|
138
198
|
validated_enable = validation.ensure_boolean(enable)
|
|
139
199
|
if validated_enable and (
|
|
140
|
-
self._z_offset is None or self.
|
|
200
|
+
self._z_offset is None or self._mm_from_edge is None or self._speed is None
|
|
141
201
|
):
|
|
142
202
|
raise ValueError(
|
|
143
|
-
"z_offset,
|
|
203
|
+
"z_offset, mm_from_edge and speed must be set before enabling touch tip."
|
|
144
204
|
)
|
|
145
205
|
self._enabled = validated_enable
|
|
146
206
|
|
|
@@ -154,13 +214,13 @@ class TouchTipProperties:
|
|
|
154
214
|
self._z_offset = validated_offset
|
|
155
215
|
|
|
156
216
|
@property
|
|
157
|
-
def
|
|
158
|
-
return self.
|
|
217
|
+
def mm_from_edge(self) -> Optional[float]:
|
|
218
|
+
return self._mm_from_edge
|
|
159
219
|
|
|
160
|
-
@
|
|
161
|
-
def
|
|
220
|
+
@mm_from_edge.setter
|
|
221
|
+
def mm_from_edge(self, new_mm: float) -> None:
|
|
162
222
|
validated_mm = validation.ensure_float(new_mm)
|
|
163
|
-
self.
|
|
223
|
+
self._mm_from_edge = validated_mm
|
|
164
224
|
|
|
165
225
|
@property
|
|
166
226
|
def speed(self) -> Optional[float]:
|
|
@@ -175,12 +235,12 @@ class TouchTipProperties:
|
|
|
175
235
|
"""Get the touch tip params in schema v1 shape."""
|
|
176
236
|
if (
|
|
177
237
|
self._z_offset is not None
|
|
178
|
-
and self.
|
|
238
|
+
and self._mm_from_edge is not None
|
|
179
239
|
and self._speed is not None
|
|
180
240
|
):
|
|
181
241
|
return SharedDataTouchTipParams(
|
|
182
242
|
zOffset=self._z_offset,
|
|
183
|
-
|
|
243
|
+
mmFromEdge=self._mm_from_edge,
|
|
184
244
|
speed=self._speed,
|
|
185
245
|
)
|
|
186
246
|
else:
|
|
@@ -301,30 +361,11 @@ class BlowoutProperties:
|
|
|
301
361
|
|
|
302
362
|
|
|
303
363
|
@dataclass(slots=True)
|
|
304
|
-
class
|
|
364
|
+
class _SubmergeRetractCommon:
|
|
305
365
|
|
|
306
|
-
_position_reference: PositionReference
|
|
307
|
-
_offset: Coordinate
|
|
308
366
|
_speed: float
|
|
309
367
|
_delay: DelayProperties
|
|
310
368
|
|
|
311
|
-
@property
|
|
312
|
-
def position_reference(self) -> PositionReference:
|
|
313
|
-
return self._position_reference
|
|
314
|
-
|
|
315
|
-
@position_reference.setter
|
|
316
|
-
def position_reference(self, new_position: str) -> None:
|
|
317
|
-
self._position_reference = PositionReference(new_position)
|
|
318
|
-
|
|
319
|
-
@property
|
|
320
|
-
def offset(self) -> Coordinate:
|
|
321
|
-
return self._offset
|
|
322
|
-
|
|
323
|
-
@offset.setter
|
|
324
|
-
def offset(self, new_offset: Sequence[float]) -> None:
|
|
325
|
-
x, y, z = validation.validate_coordinates(new_offset)
|
|
326
|
-
self._offset = Coordinate(x=x, y=y, z=z)
|
|
327
|
-
|
|
328
369
|
@property
|
|
329
370
|
def speed(self) -> float:
|
|
330
371
|
return self._speed
|
|
@@ -340,22 +381,33 @@ class SubmergeRetractCommon:
|
|
|
340
381
|
|
|
341
382
|
|
|
342
383
|
@dataclass(slots=True)
|
|
343
|
-
class Submerge(
|
|
384
|
+
class Submerge(_SubmergeRetractCommon):
|
|
385
|
+
|
|
386
|
+
_start_position: TipPosition
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def start_position(self) -> TipPosition:
|
|
390
|
+
return self._start_position
|
|
391
|
+
|
|
344
392
|
def as_shared_data_model(self) -> SharedDataSubmerge:
|
|
345
393
|
return SharedDataSubmerge(
|
|
346
|
-
|
|
347
|
-
offset=self._offset,
|
|
394
|
+
startPosition=self._start_position.as_shared_data_model(),
|
|
348
395
|
speed=self._speed,
|
|
349
396
|
delay=self._delay.as_shared_data_model(),
|
|
350
397
|
)
|
|
351
398
|
|
|
352
399
|
|
|
353
400
|
@dataclass(slots=True)
|
|
354
|
-
class RetractAspirate(
|
|
401
|
+
class RetractAspirate(_SubmergeRetractCommon):
|
|
355
402
|
|
|
403
|
+
_end_position: TipPosition
|
|
356
404
|
_air_gap_by_volume: LiquidHandlingPropertyByVolume
|
|
357
405
|
_touch_tip: TouchTipProperties
|
|
358
406
|
|
|
407
|
+
@property
|
|
408
|
+
def end_position(self) -> TipPosition:
|
|
409
|
+
return self._end_position
|
|
410
|
+
|
|
359
411
|
@property
|
|
360
412
|
def air_gap_by_volume(self) -> LiquidHandlingPropertyByVolume:
|
|
361
413
|
return self._air_gap_by_volume
|
|
@@ -366,8 +418,7 @@ class RetractAspirate(SubmergeRetractCommon):
|
|
|
366
418
|
|
|
367
419
|
def as_shared_data_model(self) -> SharedDataRetractAspirate:
|
|
368
420
|
return SharedDataRetractAspirate(
|
|
369
|
-
|
|
370
|
-
offset=self._offset,
|
|
421
|
+
endPosition=self._end_position.as_shared_data_model(),
|
|
371
422
|
speed=self._speed,
|
|
372
423
|
airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
|
|
373
424
|
touchTip=self._touch_tip.as_shared_data_model(),
|
|
@@ -376,12 +427,16 @@ class RetractAspirate(SubmergeRetractCommon):
|
|
|
376
427
|
|
|
377
428
|
|
|
378
429
|
@dataclass(slots=True)
|
|
379
|
-
class RetractDispense(
|
|
380
|
-
|
|
430
|
+
class RetractDispense(_SubmergeRetractCommon):
|
|
431
|
+
_end_position: TipPosition
|
|
381
432
|
_air_gap_by_volume: LiquidHandlingPropertyByVolume
|
|
382
433
|
_touch_tip: TouchTipProperties
|
|
383
434
|
_blowout: BlowoutProperties
|
|
384
435
|
|
|
436
|
+
@property
|
|
437
|
+
def end_position(self) -> TipPosition:
|
|
438
|
+
return self._end_position
|
|
439
|
+
|
|
385
440
|
@property
|
|
386
441
|
def air_gap_by_volume(self) -> LiquidHandlingPropertyByVolume:
|
|
387
442
|
return self._air_gap_by_volume
|
|
@@ -396,8 +451,7 @@ class RetractDispense(SubmergeRetractCommon):
|
|
|
396
451
|
|
|
397
452
|
def as_shared_data_model(self) -> SharedDataRetractDispense:
|
|
398
453
|
return SharedDataRetractDispense(
|
|
399
|
-
|
|
400
|
-
offset=self._offset,
|
|
454
|
+
endPosition=self._end_position.as_shared_data_model(),
|
|
401
455
|
speed=self._speed,
|
|
402
456
|
airGapByVolume=self._air_gap_by_volume.as_list_of_tuples(),
|
|
403
457
|
blowout=self._blowout.as_shared_data_model(),
|
|
@@ -407,11 +461,9 @@ class RetractDispense(SubmergeRetractCommon):
|
|
|
407
461
|
|
|
408
462
|
|
|
409
463
|
@dataclass(slots=True)
|
|
410
|
-
class
|
|
464
|
+
class _BaseLiquidHandlingProperties:
|
|
411
465
|
|
|
412
466
|
_submerge: Submerge
|
|
413
|
-
_position_reference: PositionReference
|
|
414
|
-
_offset: Coordinate
|
|
415
467
|
_flow_rate_by_volume: LiquidHandlingPropertyByVolume
|
|
416
468
|
_correction_by_volume: LiquidHandlingPropertyByVolume
|
|
417
469
|
_delay: DelayProperties
|
|
@@ -420,23 +472,6 @@ class BaseLiquidHandlingProperties:
|
|
|
420
472
|
def submerge(self) -> Submerge:
|
|
421
473
|
return self._submerge
|
|
422
474
|
|
|
423
|
-
@property
|
|
424
|
-
def position_reference(self) -> PositionReference:
|
|
425
|
-
return self._position_reference
|
|
426
|
-
|
|
427
|
-
@position_reference.setter
|
|
428
|
-
def position_reference(self, new_position: str) -> None:
|
|
429
|
-
self._position_reference = PositionReference(new_position)
|
|
430
|
-
|
|
431
|
-
@property
|
|
432
|
-
def offset(self) -> Coordinate:
|
|
433
|
-
return self._offset
|
|
434
|
-
|
|
435
|
-
@offset.setter
|
|
436
|
-
def offset(self, new_offset: Sequence[float]) -> None:
|
|
437
|
-
x, y, z = validation.validate_coordinates(new_offset)
|
|
438
|
-
self._offset = Coordinate(x=x, y=y, z=z)
|
|
439
|
-
|
|
440
475
|
@property
|
|
441
476
|
def flow_rate_by_volume(self) -> LiquidHandlingPropertyByVolume:
|
|
442
477
|
return self._flow_rate_by_volume
|
|
@@ -451,12 +486,17 @@ class BaseLiquidHandlingProperties:
|
|
|
451
486
|
|
|
452
487
|
|
|
453
488
|
@dataclass(slots=True)
|
|
454
|
-
class AspirateProperties(
|
|
489
|
+
class AspirateProperties(_BaseLiquidHandlingProperties):
|
|
455
490
|
|
|
491
|
+
_aspirate_position: TipPosition
|
|
456
492
|
_retract: RetractAspirate
|
|
457
493
|
_pre_wet: bool
|
|
458
494
|
_mix: MixProperties
|
|
459
495
|
|
|
496
|
+
@property
|
|
497
|
+
def aspirate_position(self) -> TipPosition:
|
|
498
|
+
return self._aspirate_position
|
|
499
|
+
|
|
460
500
|
@property
|
|
461
501
|
def pre_wet(self) -> bool:
|
|
462
502
|
return self._pre_wet
|
|
@@ -478,8 +518,7 @@ class AspirateProperties(BaseLiquidHandlingProperties):
|
|
|
478
518
|
return SharedDataAspirateProperties(
|
|
479
519
|
submerge=self._submerge.as_shared_data_model(),
|
|
480
520
|
retract=self._retract.as_shared_data_model(),
|
|
481
|
-
|
|
482
|
-
offset=self._offset,
|
|
521
|
+
aspiratePosition=self._aspirate_position.as_shared_data_model(),
|
|
483
522
|
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
|
|
484
523
|
preWet=self._pre_wet,
|
|
485
524
|
mix=self._mix.as_shared_data_model(),
|
|
@@ -489,12 +528,17 @@ class AspirateProperties(BaseLiquidHandlingProperties):
|
|
|
489
528
|
|
|
490
529
|
|
|
491
530
|
@dataclass(slots=True)
|
|
492
|
-
class SingleDispenseProperties(
|
|
531
|
+
class SingleDispenseProperties(_BaseLiquidHandlingProperties):
|
|
493
532
|
|
|
533
|
+
_dispense_position: TipPosition
|
|
494
534
|
_retract: RetractDispense
|
|
495
535
|
_push_out_by_volume: LiquidHandlingPropertyByVolume
|
|
496
536
|
_mix: MixProperties
|
|
497
537
|
|
|
538
|
+
@property
|
|
539
|
+
def dispense_position(self) -> TipPosition:
|
|
540
|
+
return self._dispense_position
|
|
541
|
+
|
|
498
542
|
@property
|
|
499
543
|
def push_out_by_volume(self) -> LiquidHandlingPropertyByVolume:
|
|
500
544
|
return self._push_out_by_volume
|
|
@@ -511,8 +555,7 @@ class SingleDispenseProperties(BaseLiquidHandlingProperties):
|
|
|
511
555
|
return SharedDataSingleDispenseProperties(
|
|
512
556
|
submerge=self._submerge.as_shared_data_model(),
|
|
513
557
|
retract=self._retract.as_shared_data_model(),
|
|
514
|
-
|
|
515
|
-
offset=self._offset,
|
|
558
|
+
dispensePosition=self._dispense_position.as_shared_data_model(),
|
|
516
559
|
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
|
|
517
560
|
mix=self._mix.as_shared_data_model(),
|
|
518
561
|
pushOutByVolume=self._push_out_by_volume.as_list_of_tuples(),
|
|
@@ -522,12 +565,17 @@ class SingleDispenseProperties(BaseLiquidHandlingProperties):
|
|
|
522
565
|
|
|
523
566
|
|
|
524
567
|
@dataclass(slots=True)
|
|
525
|
-
class MultiDispenseProperties(
|
|
568
|
+
class MultiDispenseProperties(_BaseLiquidHandlingProperties):
|
|
526
569
|
|
|
570
|
+
_dispense_position: TipPosition
|
|
527
571
|
_retract: RetractDispense
|
|
528
572
|
_conditioning_by_volume: LiquidHandlingPropertyByVolume
|
|
529
573
|
_disposal_by_volume: LiquidHandlingPropertyByVolume
|
|
530
574
|
|
|
575
|
+
@property
|
|
576
|
+
def dispense_position(self) -> TipPosition:
|
|
577
|
+
return self._dispense_position
|
|
578
|
+
|
|
531
579
|
@property
|
|
532
580
|
def retract(self) -> RetractDispense:
|
|
533
581
|
return self._retract
|
|
@@ -544,8 +592,7 @@ class MultiDispenseProperties(BaseLiquidHandlingProperties):
|
|
|
544
592
|
return SharedDataMultiDispenseProperties(
|
|
545
593
|
submerge=self._submerge.as_shared_data_model(),
|
|
546
594
|
retract=self._retract.as_shared_data_model(),
|
|
547
|
-
|
|
548
|
-
offset=self._offset,
|
|
595
|
+
dispensePosition=self._dispense_position.as_shared_data_model(),
|
|
549
596
|
flowRateByVolume=self._flow_rate_by_volume.as_list_of_tuples(),
|
|
550
597
|
conditioningByVolume=self._conditioning_by_volume.as_list_of_tuples(),
|
|
551
598
|
disposalByVolume=self._disposal_by_volume.as_list_of_tuples(),
|
|
@@ -576,6 +623,12 @@ class TransferProperties:
|
|
|
576
623
|
return self._multi_dispense
|
|
577
624
|
|
|
578
625
|
|
|
626
|
+
def _build_tip_position(tip_position: SharedDataTipPosition) -> TipPosition:
|
|
627
|
+
return TipPosition(
|
|
628
|
+
_position_reference=tip_position.positionReference, _offset=tip_position.offset
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
|
|
579
632
|
def _build_delay_properties(
|
|
580
633
|
delay_properties: SharedDataDelayProperties,
|
|
581
634
|
) -> DelayProperties:
|
|
@@ -591,16 +644,16 @@ def _build_touch_tip_properties(
|
|
|
591
644
|
) -> TouchTipProperties:
|
|
592
645
|
if touch_tip_properties.params is not None:
|
|
593
646
|
z_offset = touch_tip_properties.params.zOffset
|
|
594
|
-
|
|
647
|
+
mm_from_edge = touch_tip_properties.params.mmFromEdge
|
|
595
648
|
speed = touch_tip_properties.params.speed
|
|
596
649
|
else:
|
|
597
650
|
z_offset = None
|
|
598
|
-
|
|
651
|
+
mm_from_edge = None
|
|
599
652
|
speed = None
|
|
600
653
|
return TouchTipProperties(
|
|
601
654
|
_enabled=touch_tip_properties.enable,
|
|
602
655
|
_z_offset=z_offset,
|
|
603
|
-
|
|
656
|
+
_mm_from_edge=mm_from_edge,
|
|
604
657
|
_speed=speed,
|
|
605
658
|
)
|
|
606
659
|
|
|
@@ -637,8 +690,7 @@ def _build_submerge(
|
|
|
637
690
|
submerge_properties: SharedDataSubmerge,
|
|
638
691
|
) -> Submerge:
|
|
639
692
|
return Submerge(
|
|
640
|
-
|
|
641
|
-
_offset=submerge_properties.offset,
|
|
693
|
+
_start_position=_build_tip_position(submerge_properties.startPosition),
|
|
642
694
|
_speed=submerge_properties.speed,
|
|
643
695
|
_delay=_build_delay_properties(submerge_properties.delay),
|
|
644
696
|
)
|
|
@@ -648,8 +700,7 @@ def _build_retract_aspirate(
|
|
|
648
700
|
retract_aspirate: SharedDataRetractAspirate,
|
|
649
701
|
) -> RetractAspirate:
|
|
650
702
|
return RetractAspirate(
|
|
651
|
-
|
|
652
|
-
_offset=retract_aspirate.offset,
|
|
703
|
+
_end_position=_build_tip_position(retract_aspirate.endPosition),
|
|
653
704
|
_speed=retract_aspirate.speed,
|
|
654
705
|
_air_gap_by_volume=LiquidHandlingPropertyByVolume(
|
|
655
706
|
retract_aspirate.airGapByVolume
|
|
@@ -663,8 +714,7 @@ def _build_retract_dispense(
|
|
|
663
714
|
retract_dispense: SharedDataRetractDispense,
|
|
664
715
|
) -> RetractDispense:
|
|
665
716
|
return RetractDispense(
|
|
666
|
-
|
|
667
|
-
_offset=retract_dispense.offset,
|
|
717
|
+
_end_position=_build_tip_position(retract_dispense.endPosition),
|
|
668
718
|
_speed=retract_dispense.speed,
|
|
669
719
|
_air_gap_by_volume=LiquidHandlingPropertyByVolume(
|
|
670
720
|
retract_dispense.airGapByVolume
|
|
@@ -681,8 +731,7 @@ def build_aspirate_properties(
|
|
|
681
731
|
return AspirateProperties(
|
|
682
732
|
_submerge=_build_submerge(aspirate_properties.submerge),
|
|
683
733
|
_retract=_build_retract_aspirate(aspirate_properties.retract),
|
|
684
|
-
|
|
685
|
-
_offset=aspirate_properties.offset,
|
|
734
|
+
_aspirate_position=_build_tip_position(aspirate_properties.aspiratePosition),
|
|
686
735
|
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
|
|
687
736
|
aspirate_properties.flowRateByVolume
|
|
688
737
|
),
|
|
@@ -701,8 +750,9 @@ def build_single_dispense_properties(
|
|
|
701
750
|
return SingleDispenseProperties(
|
|
702
751
|
_submerge=_build_submerge(single_dispense_properties.submerge),
|
|
703
752
|
_retract=_build_retract_dispense(single_dispense_properties.retract),
|
|
704
|
-
|
|
705
|
-
|
|
753
|
+
_dispense_position=_build_tip_position(
|
|
754
|
+
single_dispense_properties.dispensePosition
|
|
755
|
+
),
|
|
706
756
|
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
|
|
707
757
|
single_dispense_properties.flowRateByVolume
|
|
708
758
|
),
|
|
@@ -725,8 +775,9 @@ def build_multi_dispense_properties(
|
|
|
725
775
|
return MultiDispenseProperties(
|
|
726
776
|
_submerge=_build_submerge(multi_dispense_properties.submerge),
|
|
727
777
|
_retract=_build_retract_dispense(multi_dispense_properties.retract),
|
|
728
|
-
|
|
729
|
-
|
|
778
|
+
_dispense_position=_build_tip_position(
|
|
779
|
+
multi_dispense_properties.dispensePosition
|
|
780
|
+
),
|
|
730
781
|
_flow_rate_by_volume=LiquidHandlingPropertyByVolume(
|
|
731
782
|
multi_dispense_properties.flowRateByVolume
|
|
732
783
|
),
|
|
@@ -744,12 +795,20 @@ def build_multi_dispense_properties(
|
|
|
744
795
|
|
|
745
796
|
|
|
746
797
|
def build_transfer_properties(
|
|
747
|
-
|
|
798
|
+
transfer_properties: Union[SharedDataTransferProperties, SharedByTipTypeSetting],
|
|
748
799
|
) -> TransferProperties:
|
|
800
|
+
if isinstance(transfer_properties, SharedByTipTypeSetting):
|
|
801
|
+
_transfer_properties = SharedDataTransferProperties(
|
|
802
|
+
aspirate=transfer_properties.aspirate,
|
|
803
|
+
singleDispense=transfer_properties.singleDispense,
|
|
804
|
+
multiDispense=transfer_properties.multiDispense,
|
|
805
|
+
)
|
|
806
|
+
else:
|
|
807
|
+
_transfer_properties = transfer_properties
|
|
749
808
|
return TransferProperties(
|
|
750
|
-
_aspirate=build_aspirate_properties(
|
|
751
|
-
_dispense=build_single_dispense_properties(
|
|
809
|
+
_aspirate=build_aspirate_properties(_transfer_properties.aspirate),
|
|
810
|
+
_dispense=build_single_dispense_properties(_transfer_properties.singleDispense),
|
|
752
811
|
_multi_dispense=build_multi_dispense_properties(
|
|
753
|
-
|
|
812
|
+
_transfer_properties.multiDispense
|
|
754
813
|
),
|
|
755
814
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import List, Union, Sequence, Optional
|
|
2
|
+
from typing import List, Union, Sequence, Optional, Tuple
|
|
3
3
|
|
|
4
4
|
from opentrons.types import Location, NozzleMapInterface
|
|
5
5
|
from opentrons.protocols.api_support import instrument
|
|
@@ -13,38 +13,44 @@ from opentrons.protocols.advanced_control.transfers.common import (
|
|
|
13
13
|
|
|
14
14
|
from .disposal_locations import TrashBin, WasteChute
|
|
15
15
|
from .labware import Labware, Well
|
|
16
|
+
from .core.common import WellCore
|
|
16
17
|
from . import validation
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
@dataclass
|
|
20
21
|
class TransferInfo:
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
source: List[Well]
|
|
24
|
+
dest: Union[List[Well], TrashBin, WasteChute]
|
|
24
25
|
tip_policy: TransferTipPolicyV2
|
|
25
26
|
tip_racks: List[Labware]
|
|
26
27
|
trash_location: Union[Location, TrashBin, WasteChute]
|
|
28
|
+
last_tip_location: Optional[Tuple[Location, WellCore]]
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def verify_and_normalize_transfer_args(
|
|
30
32
|
source: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
|
|
31
|
-
dest: Union[Well, Sequence[Well], Sequence[Sequence[Well]]],
|
|
33
|
+
dest: Union[Well, Sequence[Well], Sequence[Sequence[Well]], TrashBin, WasteChute],
|
|
32
34
|
tip_policy: TransferTipPolicyV2Type,
|
|
33
|
-
|
|
35
|
+
last_tip_well: Optional[Well],
|
|
34
36
|
tip_racks: List[Labware],
|
|
35
37
|
nozzle_map: NozzleMapInterface,
|
|
36
|
-
|
|
38
|
+
group_wells_for_multi_channel: bool,
|
|
37
39
|
current_volume: float,
|
|
38
40
|
trash_location: Union[Location, Well, Labware, TrashBin, WasteChute],
|
|
39
41
|
) -> TransferInfo:
|
|
40
42
|
flat_sources_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(source)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
if not isinstance(dest, (TrashBin, WasteChute)):
|
|
44
|
+
flat_dests_list = validation.ensure_valid_flat_wells_list_for_transfer_v2(dest)
|
|
45
|
+
else:
|
|
46
|
+
# If trash bin or waste chute, set this to empty to have less isinstance checks after this
|
|
47
|
+
flat_dests_list = []
|
|
48
|
+
if group_wells_for_multi_channel and nozzle_map.tip_count > 1:
|
|
43
49
|
flat_sources_list = tx_liquid_utils.group_wells_for_multi_channel_transfer(
|
|
44
|
-
flat_sources_list, nozzle_map
|
|
50
|
+
flat_sources_list, nozzle_map, "source"
|
|
45
51
|
)
|
|
46
52
|
flat_dests_list = tx_liquid_utils.group_wells_for_multi_channel_transfer(
|
|
47
|
-
flat_dests_list, nozzle_map
|
|
53
|
+
flat_dests_list, nozzle_map, "destination"
|
|
48
54
|
)
|
|
49
55
|
for well in flat_sources_list + flat_dests_list:
|
|
50
56
|
instrument.validate_takes_liquid(
|
|
@@ -55,14 +61,14 @@ def verify_and_normalize_transfer_args(
|
|
|
55
61
|
|
|
56
62
|
valid_new_tip = validation.ensure_new_tip_policy(tip_policy)
|
|
57
63
|
if valid_new_tip == TransferTipPolicyV2.NEVER:
|
|
58
|
-
if
|
|
64
|
+
if last_tip_well is None:
|
|
59
65
|
raise RuntimeError(
|
|
60
66
|
"Pipette has no tip attached to perform transfer."
|
|
61
67
|
" Either do a pick_up_tip beforehand or specify a new_tip parameter"
|
|
62
68
|
" of 'once' or 'always'."
|
|
63
69
|
)
|
|
64
70
|
else:
|
|
65
|
-
valid_tip_racks = [
|
|
71
|
+
valid_tip_racks = [last_tip_well.parent]
|
|
66
72
|
else:
|
|
67
73
|
valid_tip_racks = tip_racks
|
|
68
74
|
if current_volume != 0:
|
|
@@ -82,10 +88,33 @@ def verify_and_normalize_transfer_args(
|
|
|
82
88
|
trash_location=_trash_location
|
|
83
89
|
)
|
|
84
90
|
|
|
91
|
+
if last_tip_well is not None:
|
|
92
|
+
parent_tip_rack = last_tip_well.parent
|
|
93
|
+
last_tip_location = (
|
|
94
|
+
Location(last_tip_well.top().point, parent_tip_rack),
|
|
95
|
+
last_tip_well._core,
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
last_tip_location = None
|
|
99
|
+
|
|
85
100
|
return TransferInfo(
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
source=flat_sources_list,
|
|
102
|
+
dest=flat_dests_list if not isinstance(dest, (TrashBin, WasteChute)) else dest,
|
|
88
103
|
tip_policy=valid_new_tip,
|
|
89
104
|
tip_racks=valid_tip_racks,
|
|
90
105
|
trash_location=valid_trash_location,
|
|
106
|
+
last_tip_location=last_tip_location,
|
|
91
107
|
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def resolve_keep_last_tip(
|
|
111
|
+
keep_last_tip: Optional[bool], tip_strategy: TransferTipPolicyV2
|
|
112
|
+
) -> bool:
|
|
113
|
+
"""Resolve the liquid class transfer argument `keep_last_tip`
|
|
114
|
+
|
|
115
|
+
If set to a boolean value, maintains that setting. Otherwise, default to
|
|
116
|
+
`True` if tip policy is `NEVER`, otherwise default to `False`
|
|
117
|
+
"""
|
|
118
|
+
if keep_last_tip is not None:
|
|
119
|
+
return keep_last_tip
|
|
120
|
+
return tip_strategy == TransferTipPolicyV2.NEVER
|