opentrons 8.3.1a1__py2.py3-none-any.whl → 8.4.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.
- opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
- opentrons/calibration_storage/ot2/tip_length.py +6 -6
- opentrons/config/advanced_settings.py +9 -11
- opentrons/config/feature_flags.py +0 -4
- opentrons/config/reset.py +7 -2
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/async_serial.py +4 -0
- opentrons/drivers/asyncio/communication/errors.py +41 -8
- opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
- opentrons/drivers/flex_stacker/__init__.py +9 -3
- opentrons/drivers/flex_stacker/abstract.py +140 -15
- opentrons/drivers/flex_stacker/driver.py +593 -47
- opentrons/drivers/flex_stacker/errors.py +64 -0
- opentrons/drivers/flex_stacker/simulator.py +222 -24
- opentrons/drivers/flex_stacker/types.py +211 -15
- opentrons/drivers/flex_stacker/utils.py +19 -0
- opentrons/execute.py +4 -2
- opentrons/hardware_control/api.py +5 -0
- opentrons/hardware_control/backends/flex_protocol.py +4 -0
- opentrons/hardware_control/backends/ot3controller.py +12 -1
- opentrons/hardware_control/backends/ot3simulator.py +3 -0
- opentrons/hardware_control/backends/subsystem_manager.py +8 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
- opentrons/hardware_control/modules/__init__.py +12 -1
- opentrons/hardware_control/modules/absorbance_reader.py +11 -9
- opentrons/hardware_control/modules/flex_stacker.py +498 -0
- opentrons/hardware_control/modules/heater_shaker.py +12 -10
- opentrons/hardware_control/modules/magdeck.py +5 -1
- opentrons/hardware_control/modules/tempdeck.py +5 -1
- opentrons/hardware_control/modules/thermocycler.py +15 -14
- opentrons/hardware_control/modules/types.py +191 -1
- opentrons/hardware_control/modules/utils.py +3 -0
- opentrons/hardware_control/motion_utilities.py +20 -0
- opentrons/hardware_control/ot3api.py +145 -15
- opentrons/hardware_control/protocols/liquid_handler.py +47 -1
- opentrons/hardware_control/types.py +6 -0
- opentrons/legacy_commands/commands.py +19 -3
- opentrons/legacy_commands/helpers.py +15 -0
- opentrons/legacy_commands/types.py +3 -2
- opentrons/protocol_api/__init__.py +2 -0
- opentrons/protocol_api/_liquid.py +39 -8
- opentrons/protocol_api/_liquid_properties.py +20 -19
- opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
- opentrons/protocol_api/core/common.py +3 -1
- opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
- opentrons/protocol_api/core/engine/instrument.py +1233 -65
- opentrons/protocol_api/core/engine/labware.py +8 -4
- opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
- opentrons/protocol_api/core/engine/module_core.py +118 -2
- opentrons/protocol_api/core/engine/protocol.py +253 -11
- opentrons/protocol_api/core/engine/stringify.py +19 -8
- opentrons/protocol_api/core/engine/transfer_components_executor.py +853 -0
- opentrons/protocol_api/core/engine/well.py +60 -5
- opentrons/protocol_api/core/instrument.py +65 -19
- opentrons/protocol_api/core/labware.py +6 -2
- opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +69 -21
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
- opentrons/protocol_api/core/legacy/legacy_well_core.py +25 -1
- opentrons/protocol_api/core/legacy/load_info.py +4 -12
- opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
- opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +67 -21
- opentrons/protocol_api/core/module.py +43 -0
- opentrons/protocol_api/core/protocol.py +33 -0
- opentrons/protocol_api/core/well.py +21 -1
- opentrons/protocol_api/instrument_context.py +246 -123
- opentrons/protocol_api/labware.py +75 -11
- opentrons/protocol_api/module_contexts.py +140 -0
- opentrons/protocol_api/protocol_context.py +156 -16
- opentrons/protocol_api/validation.py +51 -41
- opentrons/protocol_engine/__init__.py +21 -2
- opentrons/protocol_engine/actions/actions.py +5 -5
- opentrons/protocol_engine/clients/sync_client.py +6 -0
- opentrons/protocol_engine/commands/__init__.py +30 -0
- opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
- opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
- opentrons/protocol_engine/commands/aspirate.py +6 -2
- opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
- opentrons/protocol_engine/commands/aspirate_while_tracking.py +237 -0
- opentrons/protocol_engine/commands/blow_out.py +2 -0
- opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
- opentrons/protocol_engine/commands/command_unions.py +69 -0
- opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
- opentrons/protocol_engine/commands/dispense.py +3 -1
- opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
- opentrons/protocol_engine/commands/dispense_while_tracking.py +240 -0
- opentrons/protocol_engine/commands/drop_tip.py +23 -1
- opentrons/protocol_engine/commands/evotip_dispense.py +6 -7
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +24 -29
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +1 -7
- opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
- opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
- opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
- opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
- opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
- opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
- opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
- opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
- opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
- opentrons/protocol_engine/commands/flex_stacker/store.py +288 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
- opentrons/protocol_engine/commands/labware_handling_common.py +24 -0
- opentrons/protocol_engine/commands/liquid_probe.py +21 -12
- opentrons/protocol_engine/commands/load_labware.py +42 -39
- opentrons/protocol_engine/commands/load_lid.py +21 -13
- opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
- opentrons/protocol_engine/commands/load_module.py +18 -17
- opentrons/protocol_engine/commands/load_pipette.py +3 -0
- opentrons/protocol_engine/commands/move_labware.py +139 -20
- opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
- opentrons/protocol_engine/commands/pipetting_common.py +154 -7
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +17 -2
- opentrons/protocol_engine/commands/reload_labware.py +6 -19
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
- opentrons/protocol_engine/errors/__init__.py +8 -0
- opentrons/protocol_engine/errors/exceptions.py +50 -0
- opentrons/protocol_engine/execution/equipment.py +123 -106
- opentrons/protocol_engine/execution/labware_movement.py +8 -6
- opentrons/protocol_engine/execution/pipetting.py +233 -26
- opentrons/protocol_engine/execution/tip_handler.py +14 -5
- opentrons/protocol_engine/labware_offset_standardization.py +173 -0
- opentrons/protocol_engine/protocol_engine.py +22 -13
- opentrons/protocol_engine/resources/deck_configuration_provider.py +94 -2
- opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
- opentrons/protocol_engine/resources/labware_validation.py +7 -5
- opentrons/protocol_engine/slot_standardization.py +11 -23
- opentrons/protocol_engine/state/addressable_areas.py +84 -46
- opentrons/protocol_engine/state/frustum_helpers.py +26 -10
- opentrons/protocol_engine/state/geometry.py +683 -100
- opentrons/protocol_engine/state/labware.py +252 -55
- opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
- opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
- opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
- opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
- opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
- opentrons/protocol_engine/state/modules.py +178 -52
- opentrons/protocol_engine/state/pipettes.py +54 -0
- opentrons/protocol_engine/state/state.py +1 -1
- opentrons/protocol_engine/state/tips.py +14 -0
- opentrons/protocol_engine/state/update_types.py +180 -25
- opentrons/protocol_engine/state/wells.py +54 -8
- opentrons/protocol_engine/types/__init__.py +292 -0
- opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
- opentrons/protocol_engine/types/command_annotations.py +53 -0
- opentrons/protocol_engine/types/deck_configuration.py +72 -0
- opentrons/protocol_engine/types/execution.py +96 -0
- opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
- opentrons/protocol_engine/types/instrument.py +47 -0
- opentrons/protocol_engine/types/instrument_sensors.py +47 -0
- opentrons/protocol_engine/types/labware.py +110 -0
- opentrons/protocol_engine/types/labware_movement.py +22 -0
- opentrons/protocol_engine/types/labware_offset_location.py +108 -0
- opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
- opentrons/protocol_engine/types/liquid.py +40 -0
- opentrons/protocol_engine/types/liquid_class.py +59 -0
- opentrons/protocol_engine/types/liquid_handling.py +13 -0
- opentrons/protocol_engine/types/liquid_level_detection.py +137 -0
- opentrons/protocol_engine/types/location.py +193 -0
- opentrons/protocol_engine/types/module.py +269 -0
- opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
- opentrons/protocol_engine/types/run_time_parameters.py +133 -0
- opentrons/protocol_engine/types/tip.py +18 -0
- opentrons/protocol_engine/types/util.py +21 -0
- opentrons/protocol_engine/types/well_position.py +107 -0
- opentrons/protocol_reader/extract_labware_definitions.py +7 -3
- opentrons/protocol_reader/file_format_validator.py +5 -3
- opentrons/protocol_runner/json_translator.py +4 -2
- opentrons/protocol_runner/legacy_command_mapper.py +6 -2
- opentrons/protocol_runner/run_orchestrator.py +4 -1
- opentrons/protocols/advanced_control/transfers/common.py +48 -1
- opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +16 -3
- opentrons/protocols/labware.py +5 -6
- opentrons/protocols/models/__init__.py +0 -21
- opentrons/simulate.py +4 -2
- opentrons/types.py +15 -6
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/METADATA +4 -4
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/RECORD +188 -148
- opentrons/calibration_storage/ot2/models/defaults.py +0 -0
- opentrons/calibration_storage/ot3/models/defaults.py +0 -0
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_engine/types.py +0 -1311
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/LICENSE +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/WHEEL +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/entry_points.txt +0 -0
- {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Models and implementation for the ``moveLabware`` command."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type, Any, List
|
|
5
|
+
from typing_extensions import TypedDict # note: need this instead of typing for py<3.12
|
|
5
6
|
|
|
6
7
|
from pydantic.json_schema import SkipJsonSchema
|
|
7
8
|
from pydantic import BaseModel, Field
|
|
@@ -18,7 +19,7 @@ from opentrons.types import Point
|
|
|
18
19
|
from ..types import (
|
|
19
20
|
ModuleModel,
|
|
20
21
|
CurrentWell,
|
|
21
|
-
|
|
22
|
+
LoadableLabwareLocation,
|
|
22
23
|
DeckSlotLocation,
|
|
23
24
|
ModuleLocation,
|
|
24
25
|
OnLabwareLocation,
|
|
@@ -26,6 +27,9 @@ from ..types import (
|
|
|
26
27
|
LabwareMovementStrategy,
|
|
27
28
|
LabwareOffsetVector,
|
|
28
29
|
LabwareMovementOffsetData,
|
|
30
|
+
LabwareLocationSequence,
|
|
31
|
+
NotOnDeckLocationSequenceComponent,
|
|
32
|
+
OFF_DECK_LOCATION,
|
|
29
33
|
)
|
|
30
34
|
from ..errors import (
|
|
31
35
|
LabwareMovementNotAllowedError,
|
|
@@ -64,7 +68,9 @@ class MoveLabwareParams(BaseModel):
|
|
|
64
68
|
"""Input parameters for a ``moveLabware`` command."""
|
|
65
69
|
|
|
66
70
|
labwareId: str = Field(..., description="The ID of the labware to move.")
|
|
67
|
-
newLocation:
|
|
71
|
+
newLocation: LoadableLabwareLocation = Field(
|
|
72
|
+
..., description="Where to move the labware."
|
|
73
|
+
)
|
|
68
74
|
strategy: LabwareMovementStrategy = Field(
|
|
69
75
|
...,
|
|
70
76
|
description="Whether to use the gripper to perform the labware movement"
|
|
@@ -100,6 +106,31 @@ class MoveLabwareResult(BaseModel):
|
|
|
100
106
|
" so the default of (0, 0, 0) will be used."
|
|
101
107
|
),
|
|
102
108
|
)
|
|
109
|
+
eventualDestinationLocationSequence: LabwareLocationSequence | None = Field(
|
|
110
|
+
None,
|
|
111
|
+
description=(
|
|
112
|
+
"The full location in which this labware will eventually reside. This will typically be the same as its "
|
|
113
|
+
"immediate destination, but if this labware is going to the trash then this field will be off deck."
|
|
114
|
+
),
|
|
115
|
+
)
|
|
116
|
+
immediateDestinationLocationSequence: LabwareLocationSequence | None = Field(
|
|
117
|
+
None,
|
|
118
|
+
description=(
|
|
119
|
+
"The full location to which this labware is being moved, right now."
|
|
120
|
+
),
|
|
121
|
+
)
|
|
122
|
+
originLocationSequence: LabwareLocationSequence | None = Field(
|
|
123
|
+
None,
|
|
124
|
+
description="The full location down to the deck of the labware before this command.",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class ErrorDetails(TypedDict):
|
|
129
|
+
"""Location details for a failed gripper move."""
|
|
130
|
+
|
|
131
|
+
originLocationSequence: LabwareLocationSequence
|
|
132
|
+
immediateDestinationLocationSequence: LabwareLocationSequence
|
|
133
|
+
eventualDestinationLocationSequence: LabwareLocationSequence
|
|
103
134
|
|
|
104
135
|
|
|
105
136
|
class GripperMovementError(ErrorOccurrence):
|
|
@@ -112,6 +143,8 @@ class GripperMovementError(ErrorOccurrence):
|
|
|
112
143
|
|
|
113
144
|
errorType: Literal["gripperMovement"] = "gripperMovement"
|
|
114
145
|
|
|
146
|
+
errorInfo: ErrorDetails
|
|
147
|
+
|
|
115
148
|
|
|
116
149
|
_ExecuteReturn = SuccessData[MoveLabwareResult] | DefinedErrorData[GripperMovementError]
|
|
117
150
|
|
|
@@ -152,6 +185,11 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
152
185
|
f"Cannot move fixed trash labware '{current_labware_definition.parameters.loadName}'."
|
|
153
186
|
)
|
|
154
187
|
|
|
188
|
+
origin_location_sequence = self._state_view.geometry.get_location_sequence(
|
|
189
|
+
params.labwareId
|
|
190
|
+
)
|
|
191
|
+
eventual_destination_location_sequence: LabwareLocationSequence | None = None
|
|
192
|
+
|
|
155
193
|
if isinstance(params.newLocation, AddressableAreaLocation):
|
|
156
194
|
area_name = params.newLocation.addressableAreaName
|
|
157
195
|
if (
|
|
@@ -181,9 +219,19 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
181
219
|
y=0,
|
|
182
220
|
z=0,
|
|
183
221
|
)
|
|
222
|
+
eventual_destination_location_sequence = [
|
|
223
|
+
NotOnDeckLocationSequenceComponent(
|
|
224
|
+
logicalLocationName=OFF_DECK_LOCATION
|
|
225
|
+
)
|
|
226
|
+
]
|
|
184
227
|
elif fixture_validation.is_trash(area_name):
|
|
185
228
|
# When dropping labware in the trash bins we want to ensure they are lids
|
|
186
229
|
# and enforce a y-axis drop offset to ensure they fall within the trash bin
|
|
230
|
+
eventual_destination_location_sequence = [
|
|
231
|
+
NotOnDeckLocationSequenceComponent(
|
|
232
|
+
logicalLocationName=OFF_DECK_LOCATION
|
|
233
|
+
)
|
|
234
|
+
]
|
|
187
235
|
if labware_validation.validate_definition_is_lid(
|
|
188
236
|
self._state_view.labware.get_definition(params.labwareId)
|
|
189
237
|
):
|
|
@@ -220,7 +268,7 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
220
268
|
)
|
|
221
269
|
|
|
222
270
|
# Check that labware and destination do not have labware on top
|
|
223
|
-
self._state_view.labware.
|
|
271
|
+
self._state_view.labware.raise_if_labware_has_non_lid_labware_on_top(
|
|
224
272
|
labware_id=params.labwareId
|
|
225
273
|
)
|
|
226
274
|
|
|
@@ -282,7 +330,6 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
282
330
|
raise LabwareMovementNotAllowedError(
|
|
283
331
|
f"Cannot move adapter '{current_labware_definition.parameters.loadName}' with gripper."
|
|
284
332
|
)
|
|
285
|
-
|
|
286
333
|
validated_current_loc = (
|
|
287
334
|
self._state_view.geometry.ensure_valid_gripper_location(
|
|
288
335
|
current_labware.location
|
|
@@ -299,6 +346,16 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
299
346
|
if trash_lid_drop_offset:
|
|
300
347
|
user_offset_data.dropOffset += trash_lid_drop_offset
|
|
301
348
|
|
|
349
|
+
immediate_destination_location_sequence = (
|
|
350
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
351
|
+
validated_new_loc
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
if eventual_destination_location_sequence is None:
|
|
355
|
+
eventual_destination_location_sequence = (
|
|
356
|
+
immediate_destination_location_sequence
|
|
357
|
+
)
|
|
358
|
+
|
|
302
359
|
try:
|
|
303
360
|
# Skips gripper moves when using virtual gripper
|
|
304
361
|
await self._labware_movement.move_labware_with_gripper(
|
|
@@ -315,20 +372,23 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
315
372
|
# todo(mm, 2024-09-26): Catch LabwareNotPickedUpError when that exists and
|
|
316
373
|
# move_labware_with_gripper() raises it.
|
|
317
374
|
) as exception:
|
|
318
|
-
gripper_movement_error: GripperMovementError | None = (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
375
|
+
gripper_movement_error: GripperMovementError | None = GripperMovementError(
|
|
376
|
+
id=self._model_utils.generate_id(),
|
|
377
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
378
|
+
errorCode=exception.code.value.code,
|
|
379
|
+
detail=exception.code.value.detail,
|
|
380
|
+
errorInfo={
|
|
381
|
+
"originLocationSequence": origin_location_sequence,
|
|
382
|
+
"immediateDestinationLocationSequence": immediate_destination_location_sequence,
|
|
383
|
+
"eventualDestinationLocationSequence": eventual_destination_location_sequence,
|
|
384
|
+
},
|
|
385
|
+
wrappedErrors=[
|
|
386
|
+
ErrorOccurrence.from_failed(
|
|
387
|
+
id=self._model_utils.generate_id(),
|
|
388
|
+
createdAt=self._model_utils.get_timestamp(),
|
|
389
|
+
error=exception,
|
|
390
|
+
)
|
|
391
|
+
],
|
|
332
392
|
)
|
|
333
393
|
else:
|
|
334
394
|
gripper_movement_error = None
|
|
@@ -344,7 +404,27 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
344
404
|
|
|
345
405
|
elif params.strategy == LabwareMovementStrategy.MANUAL_MOVE_WITH_PAUSE:
|
|
346
406
|
# Pause to allow for manual labware movement
|
|
407
|
+
immediate_destination_location_sequence = (
|
|
408
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
409
|
+
params.newLocation
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
if eventual_destination_location_sequence is None:
|
|
413
|
+
eventual_destination_location_sequence = (
|
|
414
|
+
immediate_destination_location_sequence
|
|
415
|
+
)
|
|
416
|
+
|
|
347
417
|
await self._run_control.wait_for_resume()
|
|
418
|
+
else:
|
|
419
|
+
immediate_destination_location_sequence = (
|
|
420
|
+
self._state_view.geometry.get_predicted_location_sequence(
|
|
421
|
+
params.newLocation
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
if eventual_destination_location_sequence is None:
|
|
425
|
+
eventual_destination_location_sequence = (
|
|
426
|
+
immediate_destination_location_sequence
|
|
427
|
+
)
|
|
348
428
|
|
|
349
429
|
# We may have just moved the labware that contains the current well out from
|
|
350
430
|
# under the pipette. Clear the current location to reflect the fact that the
|
|
@@ -364,8 +444,47 @@ class MoveLabwareImplementation(AbstractCommandImpl[MoveLabwareParams, _ExecuteR
|
|
|
364
444
|
new_offset_id=new_offset_id,
|
|
365
445
|
)
|
|
366
446
|
|
|
447
|
+
if labware_validation.validate_definition_is_lid(
|
|
448
|
+
definition=self._state_view.labware.get_definition(params.labwareId)
|
|
449
|
+
):
|
|
450
|
+
parent_updates: List[str] = []
|
|
451
|
+
lid_updates: List[str | None] = []
|
|
452
|
+
# when moving a lid between locations we need to:
|
|
453
|
+
if (
|
|
454
|
+
isinstance(current_labware.location, OnLabwareLocation)
|
|
455
|
+
and self._state_view.labware.get_lid_by_labware_id(
|
|
456
|
+
current_labware.location.labwareId
|
|
457
|
+
)
|
|
458
|
+
is not None
|
|
459
|
+
):
|
|
460
|
+
# if the source location was a parent labware and not a lid stack or lid, update the parent labware lid ID to None (no more lid)
|
|
461
|
+
parent_updates.append(current_labware.location.labwareId)
|
|
462
|
+
lid_updates.append(None)
|
|
463
|
+
|
|
464
|
+
# If we're moving to a non lid object, add to the setlids list of things to do
|
|
465
|
+
if isinstance(
|
|
466
|
+
available_new_location, OnLabwareLocation
|
|
467
|
+
) and not labware_validation.validate_definition_is_lid(
|
|
468
|
+
self._state_view.labware.get_definition(
|
|
469
|
+
available_new_location.labwareId
|
|
470
|
+
)
|
|
471
|
+
):
|
|
472
|
+
parent_updates.append(available_new_location.labwareId)
|
|
473
|
+
lid_updates.append(params.labwareId)
|
|
474
|
+
# Add to setlids
|
|
475
|
+
if len(parent_updates) > 0:
|
|
476
|
+
state_update.set_lids(
|
|
477
|
+
parent_labware_ids=parent_updates,
|
|
478
|
+
lid_ids=lid_updates,
|
|
479
|
+
)
|
|
480
|
+
|
|
367
481
|
return SuccessData(
|
|
368
|
-
public=MoveLabwareResult(
|
|
482
|
+
public=MoveLabwareResult(
|
|
483
|
+
offsetId=new_offset_id,
|
|
484
|
+
originLocationSequence=origin_location_sequence,
|
|
485
|
+
immediateDestinationLocationSequence=immediate_destination_location_sequence,
|
|
486
|
+
eventualDestinationLocationSequence=eventual_destination_location_sequence,
|
|
487
|
+
),
|
|
369
488
|
state_update=state_update,
|
|
370
489
|
)
|
|
371
490
|
|
|
@@ -150,7 +150,7 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
150
150
|
pipette_id=pipette_id,
|
|
151
151
|
tip_geometry=e.tip_geometry,
|
|
152
152
|
)
|
|
153
|
-
.set_fluid_empty(pipette_id=pipette_id)
|
|
153
|
+
.set_fluid_empty(pipette_id=pipette_id, clean_tip=True)
|
|
154
154
|
.mark_tips_as_used(
|
|
155
155
|
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
156
156
|
)
|
|
@@ -188,7 +188,10 @@ class PickUpTipImplementation(AbstractCommandImpl[PickUpTipParams, _ExecuteRetur
|
|
|
188
188
|
.mark_tips_as_used(
|
|
189
189
|
pipette_id=pipette_id, labware_id=labware_id, well_name=well_name
|
|
190
190
|
)
|
|
191
|
-
.set_fluid_empty(pipette_id=pipette_id)
|
|
191
|
+
.set_fluid_empty(pipette_id=pipette_id, clean_tip=True)
|
|
192
|
+
.set_pipette_ready_to_aspirate(
|
|
193
|
+
pipette_id=pipette_id, ready_to_aspirate=True
|
|
194
|
+
)
|
|
192
195
|
)
|
|
193
196
|
return SuccessData(
|
|
194
197
|
public=PickUpTipResult(
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Common pipetting command base models."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
|
-
from typing import Literal, Tuple, TYPE_CHECKING
|
|
5
|
-
|
|
4
|
+
from typing import Literal, Tuple, TYPE_CHECKING, Optional
|
|
5
|
+
import numpy
|
|
6
6
|
from typing_extensions import TypedDict
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
@@ -20,6 +20,10 @@ if TYPE_CHECKING:
|
|
|
20
20
|
from ..notes import CommandNoteAdder
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
DEFAULT_CORRECTION_VOLUME = 0.0
|
|
24
|
+
"""Default correction volume (uL) for any aspirate/ dispense volume."""
|
|
25
|
+
|
|
26
|
+
|
|
23
27
|
class PipetteIdMixin(BaseModel):
|
|
24
28
|
"""Mixin for command requests that take a pipette ID."""
|
|
25
29
|
|
|
@@ -41,6 +45,10 @@ class AspirateVolumeMixin(BaseModel):
|
|
|
41
45
|
" There is some tolerance for floating point rounding errors.",
|
|
42
46
|
ge=0,
|
|
43
47
|
)
|
|
48
|
+
correctionVolume: Optional[float] = Field(
|
|
49
|
+
None,
|
|
50
|
+
description="The correction volume in uL.",
|
|
51
|
+
)
|
|
44
52
|
|
|
45
53
|
|
|
46
54
|
class DispenseVolumeMixin(BaseModel):
|
|
@@ -53,6 +61,10 @@ class DispenseVolumeMixin(BaseModel):
|
|
|
53
61
|
" There is some tolerance for floating point rounding errors.",
|
|
54
62
|
ge=0,
|
|
55
63
|
)
|
|
64
|
+
correctionVolume: Optional[float] = Field(
|
|
65
|
+
None,
|
|
66
|
+
description="The correction volume in uL.",
|
|
67
|
+
)
|
|
56
68
|
|
|
57
69
|
|
|
58
70
|
class FlowRateMixin(BaseModel):
|
|
@@ -176,18 +188,69 @@ async def aspirate_in_place(
|
|
|
176
188
|
pipette_id: str,
|
|
177
189
|
volume: float,
|
|
178
190
|
flow_rate: float,
|
|
191
|
+
correction_volume: float,
|
|
179
192
|
location_if_error: ErrorLocationInfo,
|
|
180
193
|
command_note_adder: CommandNoteAdder,
|
|
181
194
|
pipetting: PipettingHandler,
|
|
182
195
|
model_utils: ModelUtils,
|
|
183
196
|
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
|
|
184
|
-
"""Execute an aspirate in place
|
|
197
|
+
"""Execute an aspirate in place micro-operation."""
|
|
185
198
|
try:
|
|
186
199
|
volume_aspirated = await pipetting.aspirate_in_place(
|
|
187
200
|
pipette_id=pipette_id,
|
|
188
201
|
volume=volume,
|
|
189
202
|
flow_rate=flow_rate,
|
|
190
203
|
command_note_adder=command_note_adder,
|
|
204
|
+
correction_volume=correction_volume,
|
|
205
|
+
)
|
|
206
|
+
except PipetteOverpressureError as e:
|
|
207
|
+
return DefinedErrorData(
|
|
208
|
+
public=OverpressureError(
|
|
209
|
+
id=model_utils.generate_id(),
|
|
210
|
+
createdAt=model_utils.get_timestamp(),
|
|
211
|
+
wrappedErrors=[
|
|
212
|
+
ErrorOccurrence.from_failed(
|
|
213
|
+
id=model_utils.generate_id(),
|
|
214
|
+
createdAt=model_utils.get_timestamp(),
|
|
215
|
+
error=e,
|
|
216
|
+
)
|
|
217
|
+
],
|
|
218
|
+
errorInfo=location_if_error,
|
|
219
|
+
),
|
|
220
|
+
state_update=StateUpdate().set_fluid_unknown(pipette_id=pipette_id),
|
|
221
|
+
)
|
|
222
|
+
else:
|
|
223
|
+
return SuccessData(
|
|
224
|
+
public=BaseLiquidHandlingResult(
|
|
225
|
+
volume=volume_aspirated,
|
|
226
|
+
),
|
|
227
|
+
state_update=StateUpdate().set_fluid_aspirated(
|
|
228
|
+
pipette_id=pipette_id,
|
|
229
|
+
fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=volume_aspirated),
|
|
230
|
+
),
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
async def aspirate_while_tracking(
|
|
235
|
+
pipette_id: str,
|
|
236
|
+
labware_id: str,
|
|
237
|
+
well_name: str,
|
|
238
|
+
volume: float,
|
|
239
|
+
flow_rate: float,
|
|
240
|
+
location_if_error: ErrorLocationInfo,
|
|
241
|
+
command_note_adder: CommandNoteAdder,
|
|
242
|
+
pipetting: PipettingHandler,
|
|
243
|
+
model_utils: ModelUtils,
|
|
244
|
+
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
|
|
245
|
+
"""Execute an aspirate while tracking microoperation."""
|
|
246
|
+
try:
|
|
247
|
+
volume_aspirated = await pipetting.aspirate_while_tracking(
|
|
248
|
+
pipette_id=pipette_id,
|
|
249
|
+
labware_id=labware_id,
|
|
250
|
+
well_name=well_name,
|
|
251
|
+
volume=volume,
|
|
252
|
+
flow_rate=flow_rate,
|
|
253
|
+
command_note_adder=command_note_adder,
|
|
191
254
|
)
|
|
192
255
|
except PipetteOverpressureError as e:
|
|
193
256
|
return DefinedErrorData(
|
|
@@ -217,22 +280,95 @@ async def aspirate_in_place(
|
|
|
217
280
|
)
|
|
218
281
|
|
|
219
282
|
|
|
283
|
+
async def dispense_while_tracking(
|
|
284
|
+
pipette_id: str,
|
|
285
|
+
labware_id: str,
|
|
286
|
+
well_name: str,
|
|
287
|
+
volume: float,
|
|
288
|
+
flow_rate: float,
|
|
289
|
+
push_out: float | None,
|
|
290
|
+
location_if_error: ErrorLocationInfo,
|
|
291
|
+
pipetting: PipettingHandler,
|
|
292
|
+
model_utils: ModelUtils,
|
|
293
|
+
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
|
|
294
|
+
"""Execute an dispense while tracking microoperation."""
|
|
295
|
+
# The current volume won't be none since it passed validation
|
|
296
|
+
current_volume = (
|
|
297
|
+
pipetting.get_state_view().pipettes.get_aspirated_volume(pipette_id) or 0.0
|
|
298
|
+
)
|
|
299
|
+
is_full_dispense = bool(numpy.isclose(current_volume - volume, 0))
|
|
300
|
+
ready = push_out == 0 if push_out is not None else not is_full_dispense
|
|
301
|
+
try:
|
|
302
|
+
volume_dispensed = await pipetting.dispense_while_tracking(
|
|
303
|
+
pipette_id=pipette_id,
|
|
304
|
+
labware_id=labware_id,
|
|
305
|
+
well_name=well_name,
|
|
306
|
+
volume=volume,
|
|
307
|
+
flow_rate=flow_rate,
|
|
308
|
+
push_out=push_out,
|
|
309
|
+
is_full_dispense=is_full_dispense,
|
|
310
|
+
)
|
|
311
|
+
except PipetteOverpressureError as e:
|
|
312
|
+
return DefinedErrorData(
|
|
313
|
+
public=OverpressureError(
|
|
314
|
+
id=model_utils.generate_id(),
|
|
315
|
+
createdAt=model_utils.get_timestamp(),
|
|
316
|
+
wrappedErrors=[
|
|
317
|
+
ErrorOccurrence.from_failed(
|
|
318
|
+
id=model_utils.generate_id(),
|
|
319
|
+
createdAt=model_utils.get_timestamp(),
|
|
320
|
+
error=e,
|
|
321
|
+
)
|
|
322
|
+
],
|
|
323
|
+
errorInfo=location_if_error,
|
|
324
|
+
),
|
|
325
|
+
state_update=StateUpdate()
|
|
326
|
+
.set_fluid_unknown(pipette_id=pipette_id)
|
|
327
|
+
.set_pipette_ready_to_aspirate(
|
|
328
|
+
pipette_id=pipette_id, ready_to_aspirate=False
|
|
329
|
+
),
|
|
330
|
+
)
|
|
331
|
+
else:
|
|
332
|
+
return SuccessData(
|
|
333
|
+
public=BaseLiquidHandlingResult(
|
|
334
|
+
volume=volume_dispensed,
|
|
335
|
+
),
|
|
336
|
+
state_update=StateUpdate()
|
|
337
|
+
.set_fluid_ejected(
|
|
338
|
+
pipette_id=pipette_id,
|
|
339
|
+
volume=volume_dispensed,
|
|
340
|
+
)
|
|
341
|
+
.set_pipette_ready_to_aspirate(
|
|
342
|
+
pipette_id=pipette_id, ready_to_aspirate=ready
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
|
|
220
347
|
async def dispense_in_place(
|
|
221
348
|
pipette_id: str,
|
|
222
349
|
volume: float,
|
|
223
350
|
flow_rate: float,
|
|
224
351
|
push_out: float | None,
|
|
352
|
+
correction_volume: float,
|
|
225
353
|
location_if_error: ErrorLocationInfo,
|
|
226
354
|
pipetting: PipettingHandler,
|
|
227
355
|
model_utils: ModelUtils,
|
|
228
356
|
) -> SuccessData[BaseLiquidHandlingResult] | DefinedErrorData[OverpressureError]:
|
|
229
|
-
"""Dispense-in-place as a
|
|
357
|
+
"""Dispense-in-place as a micro-operation."""
|
|
358
|
+
# The current volume won't be none since it passed validation
|
|
359
|
+
current_volume = (
|
|
360
|
+
pipetting.get_state_view().pipettes.get_aspirated_volume(pipette_id) or 0.0
|
|
361
|
+
)
|
|
362
|
+
is_full_dispense = bool(numpy.isclose(current_volume - volume, 0))
|
|
363
|
+
ready: bool = push_out == 0 if push_out is not None else not is_full_dispense
|
|
230
364
|
try:
|
|
231
365
|
volume = await pipetting.dispense_in_place(
|
|
232
366
|
pipette_id=pipette_id,
|
|
233
367
|
volume=volume,
|
|
234
368
|
flow_rate=flow_rate,
|
|
235
369
|
push_out=push_out,
|
|
370
|
+
is_full_dispense=is_full_dispense,
|
|
371
|
+
correction_volume=correction_volume,
|
|
236
372
|
)
|
|
237
373
|
except PipetteOverpressureError as e:
|
|
238
374
|
return DefinedErrorData(
|
|
@@ -248,13 +384,19 @@ async def dispense_in_place(
|
|
|
248
384
|
],
|
|
249
385
|
errorInfo=location_if_error,
|
|
250
386
|
),
|
|
251
|
-
state_update=StateUpdate()
|
|
387
|
+
state_update=StateUpdate()
|
|
388
|
+
.set_fluid_unknown(pipette_id=pipette_id)
|
|
389
|
+
.set_pipette_ready_to_aspirate(
|
|
390
|
+
pipette_id=pipette_id, ready_to_aspirate=False
|
|
391
|
+
),
|
|
252
392
|
)
|
|
253
393
|
else:
|
|
254
394
|
return SuccessData(
|
|
255
395
|
public=BaseLiquidHandlingResult(volume=volume),
|
|
256
|
-
state_update=StateUpdate()
|
|
257
|
-
|
|
396
|
+
state_update=StateUpdate()
|
|
397
|
+
.set_fluid_ejected(pipette_id=pipette_id, volume=volume)
|
|
398
|
+
.set_pipette_ready_to_aspirate(
|
|
399
|
+
pipette_id=pipette_id, ready_to_aspirate=ready
|
|
258
400
|
),
|
|
259
401
|
)
|
|
260
402
|
|
|
@@ -290,3 +432,8 @@ async def blow_out_in_place(
|
|
|
290
432
|
public=EmptyResult(),
|
|
291
433
|
state_update=StateUpdate().set_fluid_empty(pipette_id=pipette_id),
|
|
292
434
|
)
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
async def increase_evo_disp_count(pipette_id: str, pipetting: PipettingHandler) -> None:
|
|
438
|
+
"""Tell a pipette to increase it's evo-tip-dispense-count in eeprom."""
|
|
439
|
+
await pipetting.increase_evo_disp_count(pipette_id)
|
|
@@ -5,7 +5,11 @@ from pydantic import BaseModel
|
|
|
5
5
|
from typing import TYPE_CHECKING, Optional, Type, Union
|
|
6
6
|
from typing_extensions import Literal
|
|
7
7
|
|
|
8
|
-
from .pipetting_common import
|
|
8
|
+
from .pipetting_common import (
|
|
9
|
+
OverpressureError,
|
|
10
|
+
PipetteIdMixin,
|
|
11
|
+
prepare_for_aspirate,
|
|
12
|
+
)
|
|
9
13
|
from .command import (
|
|
10
14
|
AbstractCommandImpl,
|
|
11
15
|
BaseCommand,
|
|
@@ -66,6 +70,14 @@ class PrepareToAspirateImplementation(
|
|
|
66
70
|
|
|
67
71
|
async def execute(self, params: PrepareToAspirateParams) -> _ExecuteReturn:
|
|
68
72
|
"""Prepare the pipette to aspirate."""
|
|
73
|
+
ready_to_aspirate = self._pipetting_handler.get_is_ready_to_aspirate(
|
|
74
|
+
pipette_id=params.pipetteId
|
|
75
|
+
)
|
|
76
|
+
if ready_to_aspirate:
|
|
77
|
+
return SuccessData(
|
|
78
|
+
public=PrepareToAspirateResult(),
|
|
79
|
+
)
|
|
80
|
+
|
|
69
81
|
current_position = await self._gantry_mover.get_position(params.pipetteId)
|
|
70
82
|
prepare_result = await prepare_for_aspirate(
|
|
71
83
|
pipette_id=params.pipetteId,
|
|
@@ -79,12 +91,15 @@ class PrepareToAspirateImplementation(
|
|
|
79
91
|
)
|
|
80
92
|
},
|
|
81
93
|
)
|
|
94
|
+
|
|
82
95
|
if isinstance(prepare_result, DefinedErrorData):
|
|
83
96
|
return prepare_result
|
|
84
97
|
else:
|
|
85
98
|
return SuccessData(
|
|
86
99
|
public=PrepareToAspirateResult(),
|
|
87
|
-
state_update=prepare_result.state_update
|
|
100
|
+
state_update=prepare_result.state_update.set_pipette_ready_to_aspirate(
|
|
101
|
+
pipette_id=params.pipetteId, ready_to_aspirate=True
|
|
102
|
+
),
|
|
88
103
|
)
|
|
89
104
|
|
|
90
105
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Reload labware command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
from pydantic import BaseModel, Field
|
|
4
5
|
from typing import TYPE_CHECKING, Optional, Type
|
|
5
6
|
from typing_extensions import Literal
|
|
6
7
|
|
|
8
|
+
from .labware_handling_common import LabwarePositionResultMixin
|
|
7
9
|
from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
|
|
8
10
|
from ..errors.error_occurrence import ErrorOccurrence
|
|
9
11
|
from ..state.update_types import StateUpdate
|
|
@@ -24,27 +26,9 @@ class ReloadLabwareParams(BaseModel):
|
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
|
|
27
|
-
class ReloadLabwareResult(
|
|
29
|
+
class ReloadLabwareResult(LabwarePositionResultMixin):
|
|
28
30
|
"""Result data from the execution of a LoadLabware command."""
|
|
29
31
|
|
|
30
|
-
labwareId: str = Field(
|
|
31
|
-
...,
|
|
32
|
-
description="An ID to reference this labware in subsequent commands. Same as the one in the parameters.",
|
|
33
|
-
)
|
|
34
|
-
offsetId: Optional[str] = Field(
|
|
35
|
-
# Default `None` instead of `...` so this field shows up as non-required in
|
|
36
|
-
# OpenAPI. The server is allowed to omit it or make it null.
|
|
37
|
-
None,
|
|
38
|
-
description=(
|
|
39
|
-
"An ID referencing the labware offset that will apply"
|
|
40
|
-
" to the reloaded labware."
|
|
41
|
-
" This offset will be in effect until the labware is moved"
|
|
42
|
-
" with a `moveLabware` command."
|
|
43
|
-
" Null or undefined means no offset applies,"
|
|
44
|
-
" so the default of (0, 0, 0) will be used."
|
|
45
|
-
),
|
|
46
|
-
)
|
|
47
|
-
|
|
48
32
|
|
|
49
33
|
class ReloadLabwareImplementation(
|
|
50
34
|
AbstractCommandImpl[ReloadLabwareParams, SuccessData[ReloadLabwareResult]]
|
|
@@ -77,6 +61,9 @@ class ReloadLabwareImplementation(
|
|
|
77
61
|
public=ReloadLabwareResult(
|
|
78
62
|
labwareId=params.labwareId,
|
|
79
63
|
offsetId=reloaded_labware.offsetId,
|
|
64
|
+
locationSequence=self._state_view.geometry.get_predicted_location_sequence(
|
|
65
|
+
reloaded_labware.location
|
|
66
|
+
),
|
|
80
67
|
),
|
|
81
68
|
state_update=state_update,
|
|
82
69
|
)
|
|
@@ -69,7 +69,9 @@ class UnsafeBlowOutInPlaceImplementation(
|
|
|
69
69
|
)
|
|
70
70
|
state_update = update_types.StateUpdate()
|
|
71
71
|
state_update.set_fluid_empty(pipette_id=params.pipetteId)
|
|
72
|
-
|
|
72
|
+
state_update.set_pipette_ready_to_aspirate(
|
|
73
|
+
pipette_id=params.pipetteId, ready_to_aspirate=False
|
|
74
|
+
)
|
|
73
75
|
return SuccessData(
|
|
74
76
|
public=UnsafeBlowOutInPlaceResult(), state_update=state_update
|
|
75
77
|
)
|
|
@@ -78,7 +78,12 @@ class UnsafeDropTipInPlaceImplementation(
|
|
|
78
78
|
[Axis.of_main_tool_actuator(pipette_location.mount.to_hw_mount())]
|
|
79
79
|
)
|
|
80
80
|
await self._tip_handler.drop_tip(
|
|
81
|
-
pipette_id=params.pipetteId,
|
|
81
|
+
pipette_id=params.pipetteId,
|
|
82
|
+
home_after=params.homeAfter,
|
|
83
|
+
ignore_plunger=(
|
|
84
|
+
self._state_view.tips.get_pipette_active_channels(params.pipetteId)
|
|
85
|
+
== 96
|
|
86
|
+
),
|
|
82
87
|
)
|
|
83
88
|
|
|
84
89
|
state_update = StateUpdate()
|
|
@@ -76,6 +76,7 @@ from .exceptions import (
|
|
|
76
76
|
CommandNotAllowedError,
|
|
77
77
|
InvalidLiquidHeightFound,
|
|
78
78
|
LiquidHeightUnknownError,
|
|
79
|
+
LiquidVolumeUnknownError,
|
|
79
80
|
IncompleteLabwareDefinitionError,
|
|
80
81
|
IncompleteWellDefinitionError,
|
|
81
82
|
OperationLocationNotInWellError,
|
|
@@ -84,6 +85,9 @@ from .exceptions import (
|
|
|
84
85
|
InvalidLiquidError,
|
|
85
86
|
LiquidClassDoesNotExistError,
|
|
86
87
|
LiquidClassRedefinitionError,
|
|
88
|
+
OffsetLocationInvalidError,
|
|
89
|
+
FlexStackerLabwarePoolNotYetDefinedError,
|
|
90
|
+
FlexStackerNotLogicallyEmptyError,
|
|
87
91
|
)
|
|
88
92
|
|
|
89
93
|
from .error_occurrence import ErrorOccurrence, ProtocolCommandFailedError
|
|
@@ -164,11 +168,15 @@ __all__ = [
|
|
|
164
168
|
"LocationIsLidDockSlotError",
|
|
165
169
|
"InvalidAxisForRobotType",
|
|
166
170
|
"NotSupportedOnRobotType",
|
|
171
|
+
"OffsetLocationInvalidError",
|
|
172
|
+
"FlexStackerLabwarePoolNotYetDefinedError",
|
|
173
|
+
"FlexStackerNotLogicallyEmptyError",
|
|
167
174
|
# error occurrence models
|
|
168
175
|
"ErrorOccurrence",
|
|
169
176
|
"CommandNotAllowedError",
|
|
170
177
|
"InvalidLiquidHeightFound",
|
|
171
178
|
"LiquidHeightUnknownError",
|
|
179
|
+
"LiquidVolumeUnknownError",
|
|
172
180
|
"IncompleteLabwareDefinitionError",
|
|
173
181
|
"IncompleteWellDefinitionError",
|
|
174
182
|
"OperationLocationNotInWellError",
|