opentrons 8.4.0a13__py2.py3-none-any.whl → 8.5.0a1__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/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/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.0a13.dist-info → opentrons-8.5.0a1.dist-info}/METADATA +4 -4
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/RECORD +57 -56
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/LICENSE +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/WHEEL +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.4.0a13.dist-info → opentrons-8.5.0a1.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import logging
|
|
3
3
|
from contextlib import ExitStack
|
|
4
|
-
from typing import Any, List, Optional, Sequence, Union, cast,
|
|
4
|
+
from typing import Any, List, Optional, Sequence, Union, cast, Tuple
|
|
5
5
|
from opentrons_shared_data.errors.exceptions import (
|
|
6
6
|
CommandPreconditionViolated,
|
|
7
7
|
CommandParameterLimitViolated,
|
|
@@ -12,7 +12,10 @@ from opentrons_shared_data.errors.exceptions import (
|
|
|
12
12
|
from opentrons.legacy_broker import LegacyBroker
|
|
13
13
|
from opentrons.hardware_control.dev_types import PipetteDict
|
|
14
14
|
from opentrons import types
|
|
15
|
-
from opentrons.legacy_commands import
|
|
15
|
+
from opentrons.legacy_commands import (
|
|
16
|
+
commands as cmds,
|
|
17
|
+
protocol_commands as protocol_cmds,
|
|
18
|
+
)
|
|
16
19
|
|
|
17
20
|
from opentrons.legacy_commands import publisher
|
|
18
21
|
from opentrons.protocols.advanced_control.mix import mix_from_kwargs
|
|
@@ -29,7 +32,7 @@ from opentrons.protocols.api_support.util import (
|
|
|
29
32
|
UnsupportedAPIError,
|
|
30
33
|
)
|
|
31
34
|
|
|
32
|
-
from .core.common import InstrumentCore, ProtocolCore
|
|
35
|
+
from .core.common import InstrumentCore, ProtocolCore, WellCore
|
|
33
36
|
from .core.engine import ENGINE_CORE_API_VERSION
|
|
34
37
|
from .core.legacy.legacy_instrument_core import LegacyInstrumentCore
|
|
35
38
|
from .config import Clearances
|
|
@@ -70,6 +73,16 @@ _AIR_GAP_TRACKING_ADDED_IN = APIVersion(2, 22)
|
|
|
70
73
|
AdvancedLiquidHandling = v1_transfer.AdvancedLiquidHandling
|
|
71
74
|
|
|
72
75
|
|
|
76
|
+
class _Unset:
|
|
77
|
+
"""A sentinel value when no value has been supplied for an argument.
|
|
78
|
+
User code should never use this explicitly."""
|
|
79
|
+
|
|
80
|
+
def __repr__(self) -> str:
|
|
81
|
+
# Without this, the generated docs render the argument as
|
|
82
|
+
# "<opentrons.protocol_api.instrument_context._Unset object at 0x1234>"
|
|
83
|
+
return self.__class__.__name__
|
|
84
|
+
|
|
85
|
+
|
|
73
86
|
class InstrumentContext(publisher.CommandPublisher):
|
|
74
87
|
"""
|
|
75
88
|
A context for a specific pipette or instrument.
|
|
@@ -173,11 +186,12 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
173
186
|
return self._core.get_minimum_liquid_sense_height()
|
|
174
187
|
|
|
175
188
|
@requires_version(2, 0)
|
|
176
|
-
def aspirate(
|
|
189
|
+
def aspirate( # noqa: C901
|
|
177
190
|
self,
|
|
178
191
|
volume: Optional[float] = None,
|
|
179
192
|
location: Optional[Union[types.Location, labware.Well]] = None,
|
|
180
193
|
rate: float = 1.0,
|
|
194
|
+
flow_rate: Optional[float] = None,
|
|
181
195
|
) -> InstrumentContext:
|
|
182
196
|
"""
|
|
183
197
|
Draw liquid into a pipette tip.
|
|
@@ -214,6 +228,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
214
228
|
<flow_rate>`. If not specified, defaults to 1.0. See
|
|
215
229
|
:ref:`new-plunger-flow-rates`.
|
|
216
230
|
:type rate: float
|
|
231
|
+
:param flow_rate: The absolute flow rate in µL/s. If ``flow_rate`` is specified,
|
|
232
|
+
``rate`` must not be set.
|
|
233
|
+
:type flow_rate: float
|
|
217
234
|
:returns: This instance.
|
|
218
235
|
|
|
219
236
|
.. note::
|
|
@@ -223,15 +240,30 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
223
240
|
``location``, specify it as a keyword argument:
|
|
224
241
|
``pipette.aspirate(location=plate['A1'])``
|
|
225
242
|
|
|
243
|
+
.. versionchanged:: 2.24
|
|
244
|
+
Added the ``flow_rate`` parameter.
|
|
226
245
|
"""
|
|
246
|
+
if flow_rate is not None:
|
|
247
|
+
if self.api_version < APIVersion(2, 24):
|
|
248
|
+
raise APIVersionError(
|
|
249
|
+
api_element="flow_rate",
|
|
250
|
+
until_version="2.24",
|
|
251
|
+
current_version=f"{self.api_version}",
|
|
252
|
+
)
|
|
253
|
+
if rate != 1.0:
|
|
254
|
+
raise ValueError("rate must not be set if flow_rate is specified")
|
|
255
|
+
rate = flow_rate / self._core.get_aspirate_flow_rate()
|
|
256
|
+
else:
|
|
257
|
+
flow_rate = self._core.get_aspirate_flow_rate(rate)
|
|
258
|
+
|
|
227
259
|
_log.debug(
|
|
228
|
-
"aspirate {} from {} at {}".format(
|
|
229
|
-
volume, location if location else "current position",
|
|
260
|
+
"aspirate {} from {} at {} µL/s".format(
|
|
261
|
+
volume, location if location else "current position", flow_rate
|
|
230
262
|
)
|
|
231
263
|
)
|
|
232
264
|
|
|
233
265
|
move_to_location: types.Location
|
|
234
|
-
well: Optional[labware.Well]
|
|
266
|
+
well: Optional[labware.Well]
|
|
235
267
|
last_location = self._get_last_location_by_api_version()
|
|
236
268
|
try:
|
|
237
269
|
target = validation.validate_location(
|
|
@@ -245,7 +277,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
245
277
|
"knows where it is."
|
|
246
278
|
) from e
|
|
247
279
|
|
|
248
|
-
if isinstance(target,
|
|
280
|
+
if isinstance(target, validation.DisposalTarget):
|
|
249
281
|
raise ValueError(
|
|
250
282
|
"Trash Bin and Waste Chute are not acceptable location parameters for Aspirate commands."
|
|
251
283
|
)
|
|
@@ -263,7 +295,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
263
295
|
c_vol = self._core.get_available_volume() if volume is None else volume
|
|
264
296
|
else:
|
|
265
297
|
c_vol = self._core.get_available_volume() if not volume else volume
|
|
266
|
-
flow_rate = self._core.get_aspirate_flow_rate(rate)
|
|
267
298
|
|
|
268
299
|
if (
|
|
269
300
|
self.api_version >= APIVersion(2, 20)
|
|
@@ -299,7 +330,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
299
330
|
return self
|
|
300
331
|
|
|
301
332
|
@requires_version(2, 0)
|
|
302
|
-
def dispense(
|
|
333
|
+
def dispense( # noqa: C901
|
|
303
334
|
self,
|
|
304
335
|
volume: Optional[float] = None,
|
|
305
336
|
location: Optional[
|
|
@@ -307,6 +338,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
307
338
|
] = None,
|
|
308
339
|
rate: float = 1.0,
|
|
309
340
|
push_out: Optional[float] = None,
|
|
341
|
+
flow_rate: Optional[float] = None,
|
|
310
342
|
) -> InstrumentContext:
|
|
311
343
|
"""
|
|
312
344
|
Dispense liquid from a pipette tip.
|
|
@@ -363,15 +395,19 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
363
395
|
<flow_rate>`. If not specified, defaults to 1.0. See
|
|
364
396
|
:ref:`new-plunger-flow-rates`.
|
|
365
397
|
:type rate: float
|
|
398
|
+
|
|
366
399
|
:param push_out: Continue past the plunger bottom to help ensure all liquid
|
|
367
400
|
leaves the tip. Measured in µL. The default value is ``None``.
|
|
368
401
|
|
|
369
402
|
When not specified or set to ``None``, the plunger moves by a non-zero default amount.
|
|
370
403
|
|
|
371
|
-
|
|
372
404
|
For a table of default values, see :ref:`push-out-dispense`.
|
|
373
405
|
:type push_out: float
|
|
374
406
|
|
|
407
|
+
:param flow_rate: The absolute flow rate in µL/s. If ``flow_rate`` is specified,
|
|
408
|
+
``rate`` must not be set.
|
|
409
|
+
:type flow_rate: float
|
|
410
|
+
|
|
375
411
|
:returns: This instance.
|
|
376
412
|
|
|
377
413
|
.. note::
|
|
@@ -386,6 +422,13 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
386
422
|
|
|
387
423
|
.. versionchanged:: 2.17
|
|
388
424
|
Behavior of the ``volume`` parameter.
|
|
425
|
+
|
|
426
|
+
.. versionchanged:: 2.24
|
|
427
|
+
Added the ``flow_rate`` parameter.
|
|
428
|
+
|
|
429
|
+
.. versionchanged:: 2.24
|
|
430
|
+
``location`` is no longer required if the pipette just moved to, dispensed, or blew out
|
|
431
|
+
into a trash bin or waste chute.
|
|
389
432
|
"""
|
|
390
433
|
if self.api_version < APIVersion(2, 15) and push_out:
|
|
391
434
|
raise APIVersionError(
|
|
@@ -393,13 +436,27 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
393
436
|
until_version="2.15",
|
|
394
437
|
current_version=f"{self.api_version}",
|
|
395
438
|
)
|
|
439
|
+
|
|
440
|
+
if flow_rate is not None:
|
|
441
|
+
if self.api_version < APIVersion(2, 24):
|
|
442
|
+
raise APIVersionError(
|
|
443
|
+
api_element="flow_rate",
|
|
444
|
+
until_version="2.24",
|
|
445
|
+
current_version=f"{self.api_version}",
|
|
446
|
+
)
|
|
447
|
+
if rate != 1.0:
|
|
448
|
+
raise ValueError("rate must not be set if flow_rate is specified")
|
|
449
|
+
rate = flow_rate / self._core.get_dispense_flow_rate()
|
|
450
|
+
else:
|
|
451
|
+
flow_rate = self._core.get_dispense_flow_rate(rate)
|
|
452
|
+
|
|
396
453
|
_log.debug(
|
|
397
|
-
"dispense {} from {} at {}".format(
|
|
398
|
-
volume, location if location else "current position",
|
|
454
|
+
"dispense {} from {} at {} µL/s".format(
|
|
455
|
+
volume, location if location else "current position", flow_rate
|
|
399
456
|
)
|
|
400
457
|
)
|
|
401
|
-
last_location = self._get_last_location_by_api_version()
|
|
402
458
|
|
|
459
|
+
last_location = self._get_last_location_by_api_version()
|
|
403
460
|
try:
|
|
404
461
|
target = validation.validate_location(
|
|
405
462
|
location=location, last_location=last_location
|
|
@@ -417,15 +474,13 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
417
474
|
else:
|
|
418
475
|
c_vol = self._core.get_current_volume() if not volume else volume
|
|
419
476
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if isinstance(target, (TrashBin, WasteChute)):
|
|
477
|
+
if isinstance(target, validation.DisposalTarget):
|
|
423
478
|
with publisher.publish_context(
|
|
424
479
|
broker=self.broker,
|
|
425
480
|
command=cmds.dispense_in_disposal_location(
|
|
426
481
|
instrument=self,
|
|
427
482
|
volume=c_vol,
|
|
428
|
-
location=target,
|
|
483
|
+
location=target.location,
|
|
429
484
|
rate=rate,
|
|
430
485
|
flow_rate=flow_rate,
|
|
431
486
|
),
|
|
@@ -433,10 +488,10 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
433
488
|
self._core.dispense(
|
|
434
489
|
volume=c_vol,
|
|
435
490
|
rate=rate,
|
|
436
|
-
location=target,
|
|
491
|
+
location=target.location,
|
|
437
492
|
well_core=None,
|
|
438
493
|
flow_rate=flow_rate,
|
|
439
|
-
in_place=
|
|
494
|
+
in_place=target.in_place,
|
|
440
495
|
push_out=push_out,
|
|
441
496
|
meniscus_tracking=None,
|
|
442
497
|
)
|
|
@@ -477,12 +532,17 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
477
532
|
return self
|
|
478
533
|
|
|
479
534
|
@requires_version(2, 0)
|
|
480
|
-
def mix(
|
|
535
|
+
def mix( # noqa: C901
|
|
481
536
|
self,
|
|
482
537
|
repetitions: int = 1,
|
|
483
538
|
volume: Optional[float] = None,
|
|
484
539
|
location: Optional[Union[types.Location, labware.Well]] = None,
|
|
485
540
|
rate: float = 1.0,
|
|
541
|
+
aspirate_flow_rate: Optional[float] = None,
|
|
542
|
+
dispense_flow_rate: Optional[float] = None,
|
|
543
|
+
aspirate_delay: Optional[float] = None,
|
|
544
|
+
dispense_delay: Optional[float] = None,
|
|
545
|
+
final_push_out: Optional[float] = None,
|
|
486
546
|
) -> InstrumentContext:
|
|
487
547
|
"""
|
|
488
548
|
Mix a volume of liquid by repeatedly aspirating and dispensing it in a single location.
|
|
@@ -507,6 +567,16 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
507
567
|
dispensing flow rate is calculated as ``rate`` multiplied by
|
|
508
568
|
:py:attr:`flow_rate.dispense <flow_rate>`. See
|
|
509
569
|
:ref:`new-plunger-flow-rates`.
|
|
570
|
+
:param aspirate_flow_rate: The flow rate for each aspirate in the mix, in µL/s.
|
|
571
|
+
If this is specified, ``rate`` must not be set.
|
|
572
|
+
:param dispense_flow_rate: The flow rate for each dispense in the mix, in µL/s.
|
|
573
|
+
If this is specified, ``rate`` must not be set.
|
|
574
|
+
:param aspirate_delay: How long to wait after each aspirate in the mix, in seconds.
|
|
575
|
+
:param dispense_delay: How long to wait after each dispense in the mix, in seconds.
|
|
576
|
+
:param final_push_out: How much to push out after the final mix repetition. The
|
|
577
|
+
pipette will not push out after earlier repetitions. If
|
|
578
|
+
not specified or ``None``, the pipette will push out the
|
|
579
|
+
default non-zero amount. See :ref:`push-out-dispense`.
|
|
510
580
|
:raises: ``UnexpectedTipRemovalError`` -- If no tip is attached to the pipette.
|
|
511
581
|
:returns: This instance.
|
|
512
582
|
|
|
@@ -519,6 +589,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
519
589
|
|
|
520
590
|
.. versionchanged:: 2.21
|
|
521
591
|
Does not repeatedly check for liquid presence.
|
|
592
|
+
.. versionchanged:: 2.24
|
|
593
|
+
Adds the ``aspirate_flow_rate``, ``dispense_flow_rate``, ``aspirate_delay``,
|
|
594
|
+
``dispense_delay``, and ``final_push_out`` parameters.
|
|
522
595
|
"""
|
|
523
596
|
_log.debug(
|
|
524
597
|
"mixing {}uL with {} repetitions in {} at rate={}".format(
|
|
@@ -533,9 +606,69 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
533
606
|
else:
|
|
534
607
|
c_vol = self._core.get_available_volume() if not volume else volume
|
|
535
608
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
609
|
+
if aspirate_flow_rate:
|
|
610
|
+
if self.api_version < APIVersion(2, 24):
|
|
611
|
+
raise APIVersionError(
|
|
612
|
+
api_element="aspirate_flow_rate",
|
|
613
|
+
until_version="2.24",
|
|
614
|
+
current_version=f"{self._api_version}",
|
|
615
|
+
)
|
|
616
|
+
if rate != 1.0:
|
|
617
|
+
raise ValueError(
|
|
618
|
+
"rate must not be set if aspirate_flow_rate is specified"
|
|
619
|
+
)
|
|
620
|
+
if dispense_flow_rate:
|
|
621
|
+
if self.api_version < APIVersion(2, 24):
|
|
622
|
+
raise APIVersionError(
|
|
623
|
+
api_element="dispense_flow_rate",
|
|
624
|
+
until_version="2.24",
|
|
625
|
+
current_version=f"{self._api_version}",
|
|
626
|
+
)
|
|
627
|
+
if rate != 1.0:
|
|
628
|
+
raise ValueError(
|
|
629
|
+
"rate must not be set if dispense_flow_rate is specified"
|
|
630
|
+
)
|
|
631
|
+
if aspirate_delay and self.api_version < APIVersion(2, 24):
|
|
632
|
+
raise APIVersionError(
|
|
633
|
+
api_element="aspirate_delay",
|
|
634
|
+
until_version="2.24",
|
|
635
|
+
current_version=f"{self._api_version}",
|
|
636
|
+
)
|
|
637
|
+
if dispense_delay and self.api_version < APIVersion(2, 24):
|
|
638
|
+
raise APIVersionError(
|
|
639
|
+
api_element="dispense_delay",
|
|
640
|
+
until_version="2.24",
|
|
641
|
+
current_version=f"{self._api_version}",
|
|
642
|
+
)
|
|
643
|
+
if final_push_out and self.api_version < APIVersion(2, 24):
|
|
644
|
+
raise APIVersionError(
|
|
645
|
+
api_element="final_push_out",
|
|
646
|
+
until_version="2.24",
|
|
647
|
+
current_version=f"{self._api_version}",
|
|
648
|
+
)
|
|
649
|
+
|
|
650
|
+
def delay_with_publish(seconds: float) -> None:
|
|
651
|
+
# We don't have access to ProtocolContext.delay() which would automatically
|
|
652
|
+
# publish a message to the broker, so we have to do it manually:
|
|
653
|
+
with publisher.publish_context(
|
|
654
|
+
broker=self.broker,
|
|
655
|
+
command=protocol_cmds.delay(seconds=seconds, minutes=0, msg=None),
|
|
656
|
+
):
|
|
657
|
+
self._protocol_core.delay(seconds=seconds, msg=None)
|
|
658
|
+
|
|
659
|
+
def aspirate_with_delay(
|
|
660
|
+
location: Optional[types.Location | labware.Well],
|
|
661
|
+
) -> None:
|
|
662
|
+
self.aspirate(volume, location, rate, flow_rate=aspirate_flow_rate)
|
|
663
|
+
if aspirate_delay:
|
|
664
|
+
delay_with_publish(aspirate_delay)
|
|
665
|
+
|
|
666
|
+
def dispense_with_delay(push_out: Optional[float]) -> None:
|
|
667
|
+
self.dispense(
|
|
668
|
+
volume, None, rate, flow_rate=dispense_flow_rate, push_out=push_out
|
|
669
|
+
)
|
|
670
|
+
if dispense_delay:
|
|
671
|
+
delay_with_publish(dispense_delay)
|
|
539
672
|
|
|
540
673
|
with publisher.publish_context(
|
|
541
674
|
broker=self.broker,
|
|
@@ -546,13 +679,22 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
546
679
|
location=location,
|
|
547
680
|
),
|
|
548
681
|
):
|
|
549
|
-
|
|
682
|
+
aspirate_with_delay(location=location)
|
|
550
683
|
with AutoProbeDisable(self):
|
|
551
684
|
while repetitions - 1 > 0:
|
|
552
|
-
|
|
553
|
-
|
|
685
|
+
# starting in 2.16, we disable push_out on all but the last
|
|
686
|
+
# dispense() to prevent the tip from jumping out of the liquid
|
|
687
|
+
# during the mix (PR #14004):
|
|
688
|
+
dispense_with_delay(
|
|
689
|
+
push_out=0 if self.api_version >= APIVersion(2, 16) else None
|
|
690
|
+
)
|
|
691
|
+
# aspirate location was set above, do subsequent aspirates in-place:
|
|
692
|
+
aspirate_with_delay(location=None)
|
|
554
693
|
repetitions -= 1
|
|
555
|
-
|
|
694
|
+
if final_push_out is not None:
|
|
695
|
+
dispense_with_delay(push_out=final_push_out)
|
|
696
|
+
else:
|
|
697
|
+
dispense_with_delay(push_out=None)
|
|
556
698
|
return self
|
|
557
699
|
|
|
558
700
|
@requires_version(2, 0)
|
|
@@ -583,6 +725,10 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
583
725
|
without first calling a method that takes a location, like
|
|
584
726
|
:py:meth:`.aspirate` or :py:meth:`dispense`.
|
|
585
727
|
:returns: This instance.
|
|
728
|
+
|
|
729
|
+
.. versionchanged:: 2.24
|
|
730
|
+
``location`` is no longer required if the pipette just moved to, dispensed, or blew out
|
|
731
|
+
into a trash bin or waste chute.
|
|
586
732
|
"""
|
|
587
733
|
well: Optional[labware.Well] = None
|
|
588
734
|
move_to_location: types.Location
|
|
@@ -623,17 +769,17 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
623
769
|
well = target.well
|
|
624
770
|
elif isinstance(target, validation.PointTarget):
|
|
625
771
|
move_to_location = target.location
|
|
626
|
-
elif isinstance(target,
|
|
772
|
+
elif isinstance(target, validation.DisposalTarget):
|
|
627
773
|
with publisher.publish_context(
|
|
628
774
|
broker=self.broker,
|
|
629
775
|
command=cmds.blow_out_in_disposal_location(
|
|
630
|
-
instrument=self, location=target
|
|
776
|
+
instrument=self, location=target.location
|
|
631
777
|
),
|
|
632
778
|
):
|
|
633
779
|
self._core.blow_out(
|
|
634
|
-
location=target,
|
|
780
|
+
location=target.location,
|
|
635
781
|
well_core=None,
|
|
636
|
-
in_place=
|
|
782
|
+
in_place=target.in_place,
|
|
637
783
|
)
|
|
638
784
|
return self
|
|
639
785
|
|
|
@@ -657,12 +803,13 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
657
803
|
|
|
658
804
|
@publisher.publish(command=cmds.touch_tip)
|
|
659
805
|
@requires_version(2, 0)
|
|
660
|
-
def touch_tip(
|
|
806
|
+
def touch_tip( # noqa: C901
|
|
661
807
|
self,
|
|
662
808
|
location: Optional[labware.Well] = None,
|
|
663
809
|
radius: float = 1.0,
|
|
664
810
|
v_offset: float = -1.0,
|
|
665
811
|
speed: float = 60.0,
|
|
812
|
+
mm_from_edge: Union[float, _Unset] = _Unset(),
|
|
666
813
|
) -> InstrumentContext:
|
|
667
814
|
"""
|
|
668
815
|
Touch the pipette tip to the sides of a well, with the intent of removing leftover droplets.
|
|
@@ -688,12 +835,28 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
688
835
|
- Maximum: 80.0 mm/s
|
|
689
836
|
- Minimum: 1.0 mm/s
|
|
690
837
|
:type speed: float
|
|
838
|
+
:param mm_from_edge: How far to move inside the well, as a distance from the
|
|
839
|
+
well's edge.
|
|
840
|
+
When ``mm_from_edge=0``, the pipette tip will move all the
|
|
841
|
+
way to the edge of the target well. When ``mm_from_edge=1``,
|
|
842
|
+
the pipette tip will move to 1 mm from the well's edge.
|
|
843
|
+
Lower values will press the tip harder into the well's
|
|
844
|
+
walls; higher values will touch the well more lightly, or
|
|
845
|
+
not at all.
|
|
846
|
+
``mm_from_edge`` and ``radius`` are mutually exclusive: to
|
|
847
|
+
use ``mm_from_edge``, ``radius`` must be unspecified (left
|
|
848
|
+
to its default value of 1.0).
|
|
849
|
+
:type mm_from_edge: float
|
|
691
850
|
:raises: ``UnexpectedTipRemovalError`` -- If no tip is attached to the pipette.
|
|
692
851
|
:raises RuntimeError: If no location is specified and the location cache is
|
|
693
852
|
``None``. This should happen if ``touch_tip`` is called
|
|
694
853
|
without first calling a method that takes a location, like
|
|
695
854
|
:py:meth:`.aspirate` or :py:meth:`dispense`.
|
|
855
|
+
:raises: ValueError: If both ``mm_from_edge`` and ``radius`` are specified.
|
|
696
856
|
:returns: This instance.
|
|
857
|
+
|
|
858
|
+
.. versionchanged:: 2.24
|
|
859
|
+
Added the ``mm_from_edge`` parameter.
|
|
697
860
|
"""
|
|
698
861
|
if not self._core.has_tip():
|
|
699
862
|
raise UnexpectedTipRemovalError("touch_tip", self.name, self.mount)
|
|
@@ -703,8 +866,12 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
703
866
|
# If location is a valid well, move to the well first
|
|
704
867
|
if location is None:
|
|
705
868
|
last_location = self._protocol_core.get_last_location()
|
|
706
|
-
if
|
|
707
|
-
|
|
869
|
+
if last_location is None or isinstance(
|
|
870
|
+
last_location, (TrashBin, WasteChute)
|
|
871
|
+
):
|
|
872
|
+
raise RuntimeError(
|
|
873
|
+
f"Cached location of {last_location} is not valid for touch tip."
|
|
874
|
+
)
|
|
708
875
|
parent_labware, well = last_location.labware.get_parent_labware_and_well()
|
|
709
876
|
if not well or not parent_labware:
|
|
710
877
|
raise RuntimeError(
|
|
@@ -716,6 +883,18 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
716
883
|
else:
|
|
717
884
|
raise TypeError(f"location should be a Well, but it is {location}")
|
|
718
885
|
|
|
886
|
+
if not isinstance(mm_from_edge, _Unset):
|
|
887
|
+
if self.api_version < APIVersion(2, 24):
|
|
888
|
+
raise APIVersionError(
|
|
889
|
+
api_element="mm_from_edge",
|
|
890
|
+
until_version="2.24",
|
|
891
|
+
current_version=f"{self.api_version}",
|
|
892
|
+
)
|
|
893
|
+
if radius != 1.0:
|
|
894
|
+
raise ValueError(
|
|
895
|
+
"radius must be set to 1.0 if mm_from_edge is specified"
|
|
896
|
+
)
|
|
897
|
+
|
|
719
898
|
if "touchTipDisabled" in parent_labware.quirks:
|
|
720
899
|
_log.info(f"Ignoring touch tip on labware {well}")
|
|
721
900
|
return self
|
|
@@ -735,13 +914,19 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
735
914
|
radius=radius,
|
|
736
915
|
z_offset=v_offset,
|
|
737
916
|
speed=checked_speed,
|
|
917
|
+
mm_from_edge=mm_from_edge if not isinstance(mm_from_edge, _Unset) else None,
|
|
738
918
|
)
|
|
739
919
|
return self
|
|
740
920
|
|
|
741
921
|
@publisher.publish(command=cmds.air_gap)
|
|
742
922
|
@requires_version(2, 0)
|
|
743
|
-
def air_gap(
|
|
744
|
-
self,
|
|
923
|
+
def air_gap( # noqa: C901
|
|
924
|
+
self,
|
|
925
|
+
volume: Optional[float] = None,
|
|
926
|
+
height: Optional[float] = None,
|
|
927
|
+
in_place: Optional[bool] = None,
|
|
928
|
+
rate: Optional[float] = None,
|
|
929
|
+
flow_rate: Optional[float] = None,
|
|
745
930
|
) -> InstrumentContext:
|
|
746
931
|
"""
|
|
747
932
|
Draw air into the pipette's tip at the current well.
|
|
@@ -756,12 +941,27 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
756
941
|
the air gap. The default is 5 mm above the current well.
|
|
757
942
|
:type height: float
|
|
758
943
|
|
|
944
|
+
:param in_place: Air gap at the pipette's current position, without moving to
|
|
945
|
+
some height above the well. If ``in_place`` is specified,
|
|
946
|
+
``height`` must be unset.
|
|
947
|
+
:type in_place: bool
|
|
948
|
+
|
|
949
|
+
:param rate: A multiplier for the default flow rate of the pipette. Calculated
|
|
950
|
+
as ``rate`` multiplied by :py:attr:`flow_rate.aspirate
|
|
951
|
+
<flow_rate>`. If neither rate nor flow_rate is specified, the pipette
|
|
952
|
+
will aspirate at a rate of 1.0 * InstrumentContext.flow_rate.aspirate. See
|
|
953
|
+
:ref:`new-plunger-flow-rates`.
|
|
954
|
+
:type rate: float
|
|
955
|
+
|
|
956
|
+
:param flow_rate: The rate, in µL/s, at which the pipette will draw in air.
|
|
957
|
+
:type flow_rate: float
|
|
958
|
+
|
|
759
959
|
:raises: ``UnexpectedTipRemovalError`` -- If no tip is attached to the pipette.
|
|
760
960
|
|
|
761
|
-
:raises RuntimeError: If location cache is ``None
|
|
762
|
-
``air_gap()`` is called
|
|
763
|
-
that takes a location (e.g.,
|
|
764
|
-
:py:meth:`dispense`)
|
|
961
|
+
:raises RuntimeError: If location cache is ``None`` and the air gap is not
|
|
962
|
+
``in_place``. This would happen if ``air_gap()`` is called
|
|
963
|
+
without first calling a method that takes a location (e.g.,
|
|
964
|
+
:py:meth:`.aspirate`, :py:meth:`dispense`)
|
|
765
965
|
|
|
766
966
|
:returns: This instance.
|
|
767
967
|
|
|
@@ -779,22 +979,75 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
779
979
|
|
|
780
980
|
.. versionchanged:: 2.22
|
|
781
981
|
No longer implemented as an aspirate.
|
|
982
|
+
.. versionchanged:: 2.24
|
|
983
|
+
Added the ``in_place`` option.
|
|
984
|
+
.. versionchanged:: 2.24
|
|
985
|
+
Adds the ``rate`` and ``flow_rate`` parameter. You can only define one or the other. If
|
|
986
|
+
both are unspecified then ``rate`` is by default set to 1.0.
|
|
987
|
+
Can air gap over a trash bin or waste chute.
|
|
782
988
|
"""
|
|
783
989
|
if not self._core.has_tip():
|
|
784
990
|
raise UnexpectedTipRemovalError("air_gap", self.name, self.mount)
|
|
785
991
|
|
|
786
|
-
if
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
992
|
+
if rate is not None and self.api_version < APIVersion(2, 24):
|
|
993
|
+
raise APIVersionError(
|
|
994
|
+
api_element="rate",
|
|
995
|
+
until_version="2.24",
|
|
996
|
+
current_version=f"{self._api_version}",
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
if flow_rate is not None and self.api_version < APIVersion(2, 24):
|
|
1000
|
+
raise APIVersionError(
|
|
1001
|
+
api_element="flow_rate",
|
|
1002
|
+
until_version="2.24",
|
|
1003
|
+
current_version=f"{self._api_version}",
|
|
1004
|
+
)
|
|
1005
|
+
|
|
1006
|
+
if flow_rate is not None and rate is not None:
|
|
1007
|
+
raise ValueError("Cannot define both flow_rate and rate.")
|
|
1008
|
+
|
|
1009
|
+
if in_place:
|
|
1010
|
+
if self.api_version < APIVersion(2, 24):
|
|
1011
|
+
raise APIVersionError(
|
|
1012
|
+
api_element="in_place",
|
|
1013
|
+
until_version="2.24",
|
|
1014
|
+
current_version=f"{self._api_version}",
|
|
1015
|
+
)
|
|
1016
|
+
if height is not None:
|
|
1017
|
+
raise ValueError("height must be unset if air gapping in_place")
|
|
1018
|
+
else:
|
|
1019
|
+
if height is None:
|
|
1020
|
+
height = 5
|
|
1021
|
+
last_location = self._protocol_core.get_last_location()
|
|
1022
|
+
if self.api_version < APIVersion(2, 24) and isinstance(
|
|
1023
|
+
last_location, (TrashBin, WasteChute)
|
|
1024
|
+
):
|
|
1025
|
+
last_location = None
|
|
1026
|
+
if last_location is None or (
|
|
1027
|
+
isinstance(last_location, types.Location)
|
|
1028
|
+
and not last_location.labware.is_well
|
|
1029
|
+
):
|
|
1030
|
+
raise RuntimeError(
|
|
1031
|
+
f"Cached location of {last_location} is not valid for air gap."
|
|
1032
|
+
)
|
|
1033
|
+
target: Union[types.Location, TrashBin, WasteChute]
|
|
1034
|
+
if isinstance(last_location, types.Location):
|
|
1035
|
+
target = last_location.labware.as_well().top(height)
|
|
1036
|
+
else:
|
|
1037
|
+
target = last_location.top(height)
|
|
1038
|
+
self.move_to(target, publish=False)
|
|
1039
|
+
|
|
793
1040
|
if self.api_version >= _AIR_GAP_TRACKING_ADDED_IN:
|
|
794
1041
|
self._core.prepare_to_aspirate()
|
|
795
1042
|
c_vol = self._core.get_available_volume() if volume is None else volume
|
|
796
|
-
flow_rate
|
|
797
|
-
|
|
1043
|
+
if flow_rate is not None:
|
|
1044
|
+
calculated_rate = flow_rate
|
|
1045
|
+
elif rate is not None:
|
|
1046
|
+
calculated_rate = rate * self._core.get_aspirate_flow_rate()
|
|
1047
|
+
else:
|
|
1048
|
+
calculated_rate = self._core.get_aspirate_flow_rate()
|
|
1049
|
+
|
|
1050
|
+
self._core.air_gap_in_place(c_vol, calculated_rate)
|
|
798
1051
|
else:
|
|
799
1052
|
self.aspirate(volume)
|
|
800
1053
|
return self
|
|
@@ -1530,7 +1783,11 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1530
1783
|
labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
|
|
1531
1784
|
],
|
|
1532
1785
|
dest: Union[
|
|
1533
|
-
labware.Well,
|
|
1786
|
+
labware.Well,
|
|
1787
|
+
Sequence[labware.Well],
|
|
1788
|
+
Sequence[Sequence[labware.Well]],
|
|
1789
|
+
TrashBin,
|
|
1790
|
+
WasteChute,
|
|
1534
1791
|
],
|
|
1535
1792
|
new_tip: TransferTipPolicyV2Type = "once",
|
|
1536
1793
|
trash_location: Optional[
|
|
@@ -1552,7 +1809,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1552
1809
|
:param volume: The amount, in µL, to aspirate from each source and dispense to
|
|
1553
1810
|
each destination.
|
|
1554
1811
|
:param source: A single well or a list of wells to aspirate liquid from.
|
|
1555
|
-
:param dest: A single well
|
|
1812
|
+
:param dest: A single well, list of wells, trash bin, or waste chute to dispense liquid into.
|
|
1556
1813
|
:param new_tip: When to pick up and drop tips during the command.
|
|
1557
1814
|
Defaults to ``"once"``.
|
|
1558
1815
|
|
|
@@ -1560,6 +1817,8 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1560
1817
|
- ``"always"``: Use a new tip for each set of aspirate and dispense steps.
|
|
1561
1818
|
- ``"per source"``: Use one tip for each source well, even if
|
|
1562
1819
|
:ref:`tip refilling <complex-tip-refilling>` is required.
|
|
1820
|
+
- ``"per destination"``: Use one tip for each destination well, even if
|
|
1821
|
+
:ref:`tip refilling <complex-tip-refilling>` is required.
|
|
1563
1822
|
- ``"never"``: Do not pick up or drop tips at all.
|
|
1564
1823
|
|
|
1565
1824
|
See :ref:`param-tip-handling` for details.
|
|
@@ -1591,12 +1850,23 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1591
1850
|
trash_location if trash_location is not None else self.trash_container
|
|
1592
1851
|
),
|
|
1593
1852
|
)
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1853
|
+
|
|
1854
|
+
verified_dest: Union[
|
|
1855
|
+
List[Tuple[types.Location, WellCore]], TrashBin, WasteChute
|
|
1856
|
+
]
|
|
1857
|
+
if isinstance(transfer_args.dest, (TrashBin, WasteChute)):
|
|
1858
|
+
verified_dest = transfer_args.dest
|
|
1859
|
+
else:
|
|
1860
|
+
if len(transfer_args.source) != len(transfer_args.dest):
|
|
1861
|
+
raise ValueError(
|
|
1862
|
+
"Sources and destinations should be of the same length in order to perform a transfer."
|
|
1863
|
+
" To transfer liquid from one source to many destinations, use 'distribute_liquid',"
|
|
1864
|
+
" to transfer liquid to one destination from many sources, use 'consolidate_liquid'."
|
|
1865
|
+
)
|
|
1866
|
+
verified_dest = [
|
|
1867
|
+
(types.Location(types.Point(), labware=well), well._core)
|
|
1868
|
+
for well in transfer_args.dest
|
|
1869
|
+
]
|
|
1600
1870
|
|
|
1601
1871
|
with publisher.publish_context(
|
|
1602
1872
|
broker=self.broker,
|
|
@@ -1613,12 +1883,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1613
1883
|
volume=volume,
|
|
1614
1884
|
source=[
|
|
1615
1885
|
(types.Location(types.Point(), labware=well), well._core)
|
|
1616
|
-
for well in transfer_args.
|
|
1617
|
-
],
|
|
1618
|
-
dest=[
|
|
1619
|
-
(types.Location(types.Point(), labware=well), well._core)
|
|
1620
|
-
for well in transfer_args.destinations_list
|
|
1886
|
+
for well in transfer_args.source
|
|
1621
1887
|
],
|
|
1888
|
+
dest=verified_dest,
|
|
1622
1889
|
new_tip=transfer_args.tip_policy,
|
|
1623
1890
|
tip_racks=[
|
|
1624
1891
|
(types.Location(types.Point(), labware=rack), rack._core)
|
|
@@ -1661,7 +1928,8 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1661
1928
|
|
|
1662
1929
|
:param volume: The amount, in µL, to aspirate from the source and dispense to
|
|
1663
1930
|
each destination.
|
|
1664
|
-
:param source: A single well to
|
|
1931
|
+
:param source: A single well for the pipette to target, or a group of wells to
|
|
1932
|
+
target in a single aspirate for a multi-channel pipette.
|
|
1665
1933
|
:param dest: A list of wells to dispense liquid into.
|
|
1666
1934
|
:param new_tip: When to pick up and drop tips during the command.
|
|
1667
1935
|
Defaults to ``"once"``.
|
|
@@ -1698,10 +1966,15 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1698
1966
|
trash_location if trash_location is not None else self.trash_container
|
|
1699
1967
|
),
|
|
1700
1968
|
)
|
|
1701
|
-
if
|
|
1969
|
+
if isinstance(transfer_args.dest, (TrashBin, WasteChute)):
|
|
1970
|
+
raise ValueError(
|
|
1971
|
+
"distribute_with_liquid_class() does not support trash bin or waste chute"
|
|
1972
|
+
" as a destination."
|
|
1973
|
+
)
|
|
1974
|
+
if len(transfer_args.source) != 1:
|
|
1702
1975
|
raise ValueError(
|
|
1703
1976
|
f"Source should be a single well (or resolve to a single transfer for multi-channel) "
|
|
1704
|
-
f"but received {transfer_args.
|
|
1977
|
+
f"but received {transfer_args.source}."
|
|
1705
1978
|
)
|
|
1706
1979
|
if transfer_args.tip_policy not in [
|
|
1707
1980
|
TransferTipPolicyV2.ONCE,
|
|
@@ -1713,7 +1986,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1713
1986
|
f" 'once' and 'never'."
|
|
1714
1987
|
)
|
|
1715
1988
|
|
|
1716
|
-
verified_source = transfer_args.
|
|
1989
|
+
verified_source = transfer_args.source[0]
|
|
1717
1990
|
with publisher.publish_context(
|
|
1718
1991
|
broker=self.broker,
|
|
1719
1992
|
command=cmds.distribute_with_liquid_class(
|
|
@@ -1733,7 +2006,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1733
2006
|
),
|
|
1734
2007
|
dest=[
|
|
1735
2008
|
(types.Location(types.Point(), labware=well), well._core)
|
|
1736
|
-
for well in transfer_args.
|
|
2009
|
+
for well in transfer_args.dest
|
|
1737
2010
|
],
|
|
1738
2011
|
new_tip=transfer_args.tip_policy, # type: ignore[arg-type]
|
|
1739
2012
|
tip_racks=[
|
|
@@ -1756,7 +2029,7 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1756
2029
|
source: Union[
|
|
1757
2030
|
labware.Well, Sequence[labware.Well], Sequence[Sequence[labware.Well]]
|
|
1758
2031
|
],
|
|
1759
|
-
dest: Union[labware.Well, Sequence[labware.Well]],
|
|
2032
|
+
dest: Union[labware.Well, Sequence[labware.Well], TrashBin, WasteChute],
|
|
1760
2033
|
new_tip: TransferTipPolicyV2Type = "once",
|
|
1761
2034
|
trash_location: Optional[
|
|
1762
2035
|
Union[types.Location, labware.Well, TrashBin, WasteChute]
|
|
@@ -1778,7 +2051,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1778
2051
|
:param volume: The amount, in µL, to aspirate from the source and dispense to
|
|
1779
2052
|
each destination.
|
|
1780
2053
|
:param source: A list of wells to aspirate liquid from.
|
|
1781
|
-
:param dest: A single well to dispense liquid into.
|
|
2054
|
+
:param dest: A single well, list of wells, trash bin, or waste chute to dispense liquid into.
|
|
2055
|
+
Multiple wells can only be given for multi-channel pipette configurations, and
|
|
2056
|
+
must be able to be dispensed to in a single dispense.
|
|
1782
2057
|
:param new_tip: When to pick up and drop tips during the command.
|
|
1783
2058
|
Defaults to ``"once"``.
|
|
1784
2059
|
|
|
@@ -1814,10 +2089,18 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1814
2089
|
trash_location if trash_location is not None else self.trash_container
|
|
1815
2090
|
),
|
|
1816
2091
|
)
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2092
|
+
verified_dest: Union[Tuple[types.Location, WellCore], TrashBin, WasteChute]
|
|
2093
|
+
if isinstance(transfer_args.dest, (TrashBin, WasteChute)):
|
|
2094
|
+
verified_dest = transfer_args.dest
|
|
2095
|
+
else:
|
|
2096
|
+
if len(transfer_args.dest) != 1:
|
|
2097
|
+
raise ValueError(
|
|
2098
|
+
f"Destination should be a single well (or resolve to a single transfer for multi-channel) "
|
|
2099
|
+
f"but received {transfer_args.dest}."
|
|
2100
|
+
)
|
|
2101
|
+
verified_dest = (
|
|
2102
|
+
types.Location(types.Point(), labware=transfer_args.dest[0]),
|
|
2103
|
+
transfer_args.dest[0]._core,
|
|
1821
2104
|
)
|
|
1822
2105
|
if transfer_args.tip_policy not in [
|
|
1823
2106
|
TransferTipPolicyV2.ONCE,
|
|
@@ -1829,7 +2112,6 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1829
2112
|
f" 'once' and 'never'."
|
|
1830
2113
|
)
|
|
1831
2114
|
|
|
1832
|
-
verified_dest = transfer_args.destinations_list[0]
|
|
1833
2115
|
with publisher.publish_context(
|
|
1834
2116
|
broker=self.broker,
|
|
1835
2117
|
command=cmds.consolidate_with_liquid_class(
|
|
@@ -1845,12 +2127,9 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
1845
2127
|
volume=volume,
|
|
1846
2128
|
source=[
|
|
1847
2129
|
(types.Location(types.Point(), labware=well), well._core)
|
|
1848
|
-
for well in transfer_args.
|
|
2130
|
+
for well in transfer_args.source
|
|
1849
2131
|
],
|
|
1850
|
-
dest=
|
|
1851
|
-
types.Location(types.Point(), labware=verified_dest),
|
|
1852
|
-
verified_dest._core,
|
|
1853
|
-
),
|
|
2132
|
+
dest=verified_dest,
|
|
1854
2133
|
new_tip=transfer_args.tip_policy, # type: ignore[arg-type]
|
|
1855
2134
|
tip_racks=[
|
|
1856
2135
|
(types.Location(types.Point(), labware=rack), rack._core)
|
|
@@ -2413,14 +2692,22 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2413
2692
|
"""
|
|
2414
2693
|
return self._well_bottom_clearances
|
|
2415
2694
|
|
|
2416
|
-
def _get_last_location_by_api_version(
|
|
2695
|
+
def _get_last_location_by_api_version(
|
|
2696
|
+
self,
|
|
2697
|
+
) -> Optional[Union[types.Location, TrashBin, WasteChute]]:
|
|
2417
2698
|
"""Get the last location accessed by this pipette, if any.
|
|
2418
2699
|
|
|
2419
2700
|
In pre-engine Protocol API versions, this call omits the pipette mount.
|
|
2701
|
+
Between 2.14 (first engine PAPI version) and 2.23 this only returns None or a Location object.
|
|
2420
2702
|
This is to preserve pre-existing, potentially buggy behavior.
|
|
2421
2703
|
"""
|
|
2422
|
-
if self._api_version >=
|
|
2704
|
+
if self._api_version >= APIVersion(2, 24):
|
|
2423
2705
|
return self._protocol_core.get_last_location(mount=self._core.get_mount())
|
|
2706
|
+
elif self._api_version >= ENGINE_CORE_API_VERSION:
|
|
2707
|
+
last_location = self._protocol_core.get_last_location(
|
|
2708
|
+
mount=self._core.get_mount()
|
|
2709
|
+
)
|
|
2710
|
+
return last_location if isinstance(last_location, types.Location) else None
|
|
2424
2711
|
else:
|
|
2425
2712
|
return self._protocol_core.get_last_location()
|
|
2426
2713
|
|
|
@@ -2476,7 +2763,11 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2476
2763
|
actual_value=str(volume),
|
|
2477
2764
|
)
|
|
2478
2765
|
last_location = self._get_last_location_by_api_version()
|
|
2479
|
-
if
|
|
2766
|
+
if (
|
|
2767
|
+
last_location
|
|
2768
|
+
and isinstance(last_location, types.Location)
|
|
2769
|
+
and isinstance(last_location.labware, labware.Well)
|
|
2770
|
+
):
|
|
2480
2771
|
self.move_to(last_location.labware.top())
|
|
2481
2772
|
self._core.configure_for_volume(volume)
|
|
2482
2773
|
|
|
@@ -2498,19 +2789,19 @@ class InstrumentContext(publisher.CommandPublisher):
|
|
|
2498
2789
|
If the pipette is in a well, it will move out of the well, move the plunger,
|
|
2499
2790
|
and then move back.
|
|
2500
2791
|
|
|
2501
|
-
Use ``prepare_to_aspirate`` when you need to control exactly when the plunger
|
|
2792
|
+
Use ``prepare_to_aspirate()`` when you need to control exactly when the plunger
|
|
2502
2793
|
motion will happen. A common use case is a pre-wetting routine, which requires
|
|
2503
2794
|
preparing for aspiration, moving into a well, and then aspirating *without
|
|
2504
2795
|
leaving the well*::
|
|
2505
2796
|
|
|
2506
2797
|
pipette.move_to(well.bottom(z=2))
|
|
2507
|
-
|
|
2798
|
+
protocol.delay(5)
|
|
2508
2799
|
pipette.mix(10, 10)
|
|
2509
2800
|
pipette.move_to(well.top(z=5))
|
|
2510
2801
|
pipette.blow_out()
|
|
2511
2802
|
pipette.prepare_to_aspirate()
|
|
2512
2803
|
pipette.move_to(well.bottom(z=2))
|
|
2513
|
-
|
|
2804
|
+
protocol.delay(5)
|
|
2514
2805
|
pipette.aspirate(10, well.bottom(z=2))
|
|
2515
2806
|
|
|
2516
2807
|
The call to ``prepare_to_aspirate()`` means that the plunger will be in the
|