opentrons 8.2.0a3__py2.py3-none-any.whl → 8.3.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/calibration_storage/deck_configuration.py +3 -3
- opentrons/calibration_storage/file_operators.py +3 -3
- opentrons/calibration_storage/helpers.py +3 -1
- opentrons/calibration_storage/ot2/models/v1.py +16 -29
- opentrons/calibration_storage/ot2/tip_length.py +7 -4
- opentrons/calibration_storage/ot3/models/v1.py +14 -23
- opentrons/cli/analyze.py +18 -6
- opentrons/config/defaults_ot3.py +1 -0
- opentrons/drivers/asyncio/communication/__init__.py +2 -0
- opentrons/drivers/asyncio/communication/errors.py +16 -3
- opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
- opentrons/drivers/command_builder.py +2 -2
- opentrons/drivers/flex_stacker/__init__.py +9 -0
- opentrons/drivers/flex_stacker/abstract.py +89 -0
- opentrons/drivers/flex_stacker/driver.py +260 -0
- opentrons/drivers/flex_stacker/simulator.py +109 -0
- opentrons/drivers/flex_stacker/types.py +138 -0
- opentrons/drivers/heater_shaker/driver.py +18 -3
- opentrons/drivers/temp_deck/driver.py +13 -3
- opentrons/drivers/thermocycler/driver.py +17 -3
- opentrons/execute.py +3 -1
- opentrons/hardware_control/__init__.py +1 -2
- opentrons/hardware_control/api.py +33 -21
- opentrons/hardware_control/backends/flex_protocol.py +17 -7
- opentrons/hardware_control/backends/ot3controller.py +213 -63
- opentrons/hardware_control/backends/ot3simulator.py +18 -9
- opentrons/hardware_control/backends/ot3utils.py +43 -15
- opentrons/hardware_control/dev_types.py +4 -0
- opentrons/hardware_control/emulation/heater_shaker.py +4 -0
- opentrons/hardware_control/emulation/module_server/client.py +1 -1
- opentrons/hardware_control/emulation/module_server/server.py +5 -3
- opentrons/hardware_control/emulation/settings.py +3 -4
- opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
- opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
- opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
- opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
- opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
- opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
- opentrons/hardware_control/modules/mod_abc.py +2 -2
- opentrons/hardware_control/motion_utilities.py +68 -0
- opentrons/hardware_control/nozzle_manager.py +39 -41
- opentrons/hardware_control/ot3_calibration.py +1 -1
- opentrons/hardware_control/ot3api.py +78 -31
- opentrons/hardware_control/protocols/gripper_controller.py +3 -0
- opentrons/hardware_control/protocols/hardware_manager.py +5 -1
- opentrons/hardware_control/protocols/liquid_handler.py +22 -1
- opentrons/hardware_control/protocols/motion_controller.py +7 -0
- opentrons/hardware_control/robot_calibration.py +1 -1
- opentrons/hardware_control/types.py +61 -0
- opentrons/legacy_commands/commands.py +37 -0
- opentrons/legacy_commands/types.py +39 -0
- opentrons/protocol_api/__init__.py +20 -1
- opentrons/protocol_api/_liquid.py +24 -49
- opentrons/protocol_api/_liquid_properties.py +754 -0
- opentrons/protocol_api/_types.py +24 -0
- opentrons/protocol_api/core/common.py +2 -0
- opentrons/protocol_api/core/engine/instrument.py +191 -10
- opentrons/protocol_api/core/engine/labware.py +29 -7
- opentrons/protocol_api/core/engine/protocol.py +130 -5
- opentrons/protocol_api/core/engine/robot.py +139 -0
- opentrons/protocol_api/core/engine/well.py +4 -1
- opentrons/protocol_api/core/instrument.py +73 -4
- opentrons/protocol_api/core/labware.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
- opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
- opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
- opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
- opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
- opentrons/protocol_api/core/protocol.py +34 -1
- opentrons/protocol_api/core/robot.py +51 -0
- opentrons/protocol_api/instrument_context.py +299 -44
- opentrons/protocol_api/labware.py +248 -9
- opentrons/protocol_api/module_contexts.py +21 -17
- opentrons/protocol_api/protocol_context.py +125 -4
- opentrons/protocol_api/robot_context.py +204 -32
- opentrons/protocol_api/validation.py +262 -3
- opentrons/protocol_engine/__init__.py +4 -0
- opentrons/protocol_engine/actions/actions.py +2 -3
- opentrons/protocol_engine/clients/sync_client.py +18 -0
- opentrons/protocol_engine/commands/__init__.py +121 -0
- opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
- opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
- opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
- opentrons/protocol_engine/commands/absorbance_reader/read.py +40 -10
- opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
- opentrons/protocol_engine/commands/aspirate.py +103 -53
- opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
- opentrons/protocol_engine/commands/blow_out.py +44 -39
- opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
- opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
- opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
- opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
- opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
- opentrons/protocol_engine/commands/command.py +73 -66
- opentrons/protocol_engine/commands/command_unions.py +140 -1
- opentrons/protocol_engine/commands/comment.py +1 -1
- opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
- opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
- opentrons/protocol_engine/commands/custom.py +6 -12
- opentrons/protocol_engine/commands/dispense.py +82 -48
- opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
- opentrons/protocol_engine/commands/drop_tip.py +52 -31
- opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
- opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
- opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
- opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
- opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
- opentrons/protocol_engine/commands/get_next_tip.py +134 -0
- opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/home.py +13 -4
- opentrons/protocol_engine/commands/liquid_probe.py +125 -31
- opentrons/protocol_engine/commands/load_labware.py +33 -6
- opentrons/protocol_engine/commands/load_lid.py +146 -0
- opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
- opentrons/protocol_engine/commands/load_liquid.py +12 -4
- opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
- opentrons/protocol_engine/commands/load_module.py +31 -10
- opentrons/protocol_engine/commands/load_pipette.py +19 -8
- opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
- opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
- opentrons/protocol_engine/commands/move_labware.py +28 -6
- opentrons/protocol_engine/commands/move_relative.py +35 -25
- opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
- opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
- opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
- opentrons/protocol_engine/commands/move_to_well.py +40 -24
- opentrons/protocol_engine/commands/movement_common.py +338 -0
- opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
- opentrons/protocol_engine/commands/pipetting_common.py +169 -87
- opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
- opentrons/protocol_engine/commands/reload_labware.py +1 -1
- opentrons/protocol_engine/commands/retract_axis.py +1 -1
- opentrons/protocol_engine/commands/robot/__init__.py +69 -0
- opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
- opentrons/protocol_engine/commands/robot/common.py +18 -0
- opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
- opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
- opentrons/protocol_engine/commands/robot/move_to.py +94 -0
- opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
- opentrons/protocol_engine/commands/save_position.py +14 -5
- opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
- opentrons/protocol_engine/commands/set_status_bar.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
- opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
- opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
- opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
- opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
- opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
- opentrons/protocol_engine/commands/touch_tip.py +65 -16
- opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
- opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
- opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
- opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
- opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +4 -2
- opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
- opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
- opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
- opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
- opentrons/protocol_engine/errors/__init__.py +12 -0
- opentrons/protocol_engine/errors/error_occurrence.py +19 -20
- opentrons/protocol_engine/errors/exceptions.py +76 -0
- opentrons/protocol_engine/execution/command_executor.py +1 -1
- opentrons/protocol_engine/execution/equipment.py +73 -5
- opentrons/protocol_engine/execution/gantry_mover.py +369 -8
- opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
- opentrons/protocol_engine/execution/movement.py +27 -0
- opentrons/protocol_engine/execution/pipetting.py +5 -1
- opentrons/protocol_engine/execution/tip_handler.py +34 -15
- opentrons/protocol_engine/notes/notes.py +1 -1
- opentrons/protocol_engine/protocol_engine.py +7 -6
- opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
- opentrons/protocol_engine/resources/labware_validation.py +18 -0
- opentrons/protocol_engine/resources/module_data_provider.py +1 -1
- opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
- opentrons/protocol_engine/slot_standardization.py +9 -9
- opentrons/protocol_engine/state/_move_types.py +9 -5
- opentrons/protocol_engine/state/_well_math.py +193 -0
- opentrons/protocol_engine/state/addressable_areas.py +25 -61
- opentrons/protocol_engine/state/command_history.py +12 -0
- opentrons/protocol_engine/state/commands.py +22 -14
- opentrons/protocol_engine/state/files.py +10 -12
- opentrons/protocol_engine/state/fluid_stack.py +138 -0
- opentrons/protocol_engine/state/frustum_helpers.py +63 -69
- opentrons/protocol_engine/state/geometry.py +47 -1
- opentrons/protocol_engine/state/labware.py +92 -26
- opentrons/protocol_engine/state/liquid_classes.py +82 -0
- opentrons/protocol_engine/state/liquids.py +16 -4
- opentrons/protocol_engine/state/modules.py +56 -71
- opentrons/protocol_engine/state/motion.py +6 -1
- opentrons/protocol_engine/state/pipettes.py +149 -58
- opentrons/protocol_engine/state/state.py +21 -2
- opentrons/protocol_engine/state/state_summary.py +4 -2
- opentrons/protocol_engine/state/tips.py +11 -44
- opentrons/protocol_engine/state/update_types.py +343 -48
- opentrons/protocol_engine/state/wells.py +19 -11
- opentrons/protocol_engine/types.py +176 -28
- opentrons/protocol_reader/extract_labware_definitions.py +5 -2
- opentrons/protocol_reader/file_format_validator.py +5 -5
- opentrons/protocol_runner/json_file_reader.py +9 -3
- opentrons/protocol_runner/json_translator.py +51 -25
- opentrons/protocol_runner/legacy_command_mapper.py +66 -64
- opentrons/protocol_runner/protocol_runner.py +35 -4
- opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
- opentrons/protocol_runner/run_orchestrator.py +13 -3
- opentrons/protocols/advanced_control/common.py +38 -0
- opentrons/protocols/advanced_control/mix.py +1 -1
- opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
- opentrons/protocols/advanced_control/transfers/common.py +56 -0
- opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
- opentrons/protocols/api_support/definitions.py +1 -1
- opentrons/protocols/api_support/instrument.py +1 -1
- opentrons/protocols/api_support/util.py +10 -0
- opentrons/protocols/labware.py +70 -8
- opentrons/protocols/models/json_protocol.py +5 -9
- opentrons/simulate.py +3 -1
- opentrons/types.py +162 -2
- opentrons/util/entrypoint_util.py +2 -5
- opentrons/util/logging_config.py +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
- {opentrons-8.2.0a3.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Configure for volume command request, result, and implementation models."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Type, Any
|
|
4
|
+
|
|
3
5
|
from pydantic import BaseModel, Field
|
|
4
|
-
from
|
|
6
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
5
7
|
from typing_extensions import Literal
|
|
6
8
|
|
|
7
9
|
from .pipetting_common import PipetteIdMixin
|
|
@@ -16,6 +18,10 @@ if TYPE_CHECKING:
|
|
|
16
18
|
ConfigureForVolumeCommandType = Literal["configureForVolume"]
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
22
|
+
s.pop("default", None)
|
|
23
|
+
|
|
24
|
+
|
|
19
25
|
class ConfigureForVolumeParams(PipetteIdMixin):
|
|
20
26
|
"""Parameters required to configure volume for a specific pipette."""
|
|
21
27
|
|
|
@@ -25,12 +31,13 @@ class ConfigureForVolumeParams(PipetteIdMixin):
|
|
|
25
31
|
"than a pipette-specific maximum volume.",
|
|
26
32
|
ge=0,
|
|
27
33
|
)
|
|
28
|
-
tipOverlapNotAfterVersion:
|
|
34
|
+
tipOverlapNotAfterVersion: str | SkipJsonSchema[None] = Field(
|
|
29
35
|
None,
|
|
30
36
|
description="A version of tip overlap data to not exceed. The highest-versioned "
|
|
31
37
|
"tip overlap data that does not exceed this version will be used. Versions are "
|
|
32
38
|
"expressed as vN where N is an integer, counting up from v0. If None, the current "
|
|
33
39
|
"highest version will be used.",
|
|
40
|
+
json_schema_extra=_remove_default,
|
|
34
41
|
)
|
|
35
42
|
|
|
36
43
|
|
|
@@ -81,7 +88,7 @@ class ConfigureForVolume(
|
|
|
81
88
|
|
|
82
89
|
commandType: ConfigureForVolumeCommandType = "configureForVolume"
|
|
83
90
|
params: ConfigureForVolumeParams
|
|
84
|
-
result: Optional[ConfigureForVolumeResult]
|
|
91
|
+
result: Optional[ConfigureForVolumeResult] = None
|
|
85
92
|
|
|
86
93
|
_ImplementationCls: Type[
|
|
87
94
|
ConfigureForVolumeImplementation
|
|
@@ -61,9 +61,11 @@ class ConfigureNozzleLayoutImplementation(
|
|
|
61
61
|
self, params: ConfigureNozzleLayoutParams
|
|
62
62
|
) -> SuccessData[ConfigureNozzleLayoutResult]:
|
|
63
63
|
"""Check that requested pipette can support the requested nozzle layout."""
|
|
64
|
-
primary_nozzle = params.configurationParams.
|
|
65
|
-
front_right_nozzle = params.configurationParams.
|
|
66
|
-
|
|
64
|
+
primary_nozzle = params.configurationParams.model_dump().get("primaryNozzle")
|
|
65
|
+
front_right_nozzle = params.configurationParams.model_dump().get(
|
|
66
|
+
"frontRightNozzle"
|
|
67
|
+
)
|
|
68
|
+
back_left_nozzle = params.configurationParams.model_dump().get("backLeftNozzle")
|
|
67
69
|
nozzle_params = await self._tip_handler.available_for_nozzle_layout(
|
|
68
70
|
pipette_id=params.pipetteId,
|
|
69
71
|
style=params.configurationParams.style,
|
|
@@ -97,7 +99,7 @@ class ConfigureNozzleLayout(
|
|
|
97
99
|
|
|
98
100
|
commandType: ConfigureNozzleLayoutCommandType = "configureNozzleLayout"
|
|
99
101
|
params: ConfigureNozzleLayoutParams
|
|
100
|
-
result: Optional[ConfigureNozzleLayoutResult]
|
|
102
|
+
result: Optional[ConfigureNozzleLayoutResult] = None
|
|
101
103
|
|
|
102
104
|
_ImplementationCls: Type[
|
|
103
105
|
ConfigureNozzleLayoutImplementation
|
|
@@ -10,7 +10,7 @@ data still adheres to the shapes that ProtocolEngine expects.
|
|
|
10
10
|
If you are implementing a custom command, you should probably
|
|
11
11
|
put your own disambiguation identifier in the payload.
|
|
12
12
|
"""
|
|
13
|
-
from pydantic import BaseModel,
|
|
13
|
+
from pydantic import ConfigDict, BaseModel, SerializeAsAny
|
|
14
14
|
from typing import Optional, Type
|
|
15
15
|
from typing_extensions import Literal
|
|
16
16
|
|
|
@@ -24,19 +24,13 @@ CustomCommandType = Literal["custom"]
|
|
|
24
24
|
class CustomParams(BaseModel):
|
|
25
25
|
"""Payload used by a custom command."""
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
"""Allow arbitrary fields."""
|
|
29
|
-
|
|
30
|
-
extra = Extra.allow
|
|
27
|
+
model_config = ConfigDict(extra="allow")
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class CustomResult(BaseModel):
|
|
34
31
|
"""Result data from a custom command."""
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
"""Allow arbitrary fields."""
|
|
38
|
-
|
|
39
|
-
extra = Extra.allow
|
|
33
|
+
model_config = ConfigDict(extra="allow")
|
|
40
34
|
|
|
41
35
|
|
|
42
36
|
class CustomImplementation(
|
|
@@ -50,7 +44,7 @@ class CustomImplementation(
|
|
|
50
44
|
async def execute(self, params: CustomParams) -> SuccessData[CustomResult]:
|
|
51
45
|
"""A custom command does nothing when executed directly."""
|
|
52
46
|
return SuccessData(
|
|
53
|
-
public=CustomResult.
|
|
47
|
+
public=CustomResult.model_construct(),
|
|
54
48
|
)
|
|
55
49
|
|
|
56
50
|
|
|
@@ -58,8 +52,8 @@ class Custom(BaseCommand[CustomParams, CustomResult, ErrorOccurrence]):
|
|
|
58
52
|
"""Custom command model."""
|
|
59
53
|
|
|
60
54
|
commandType: CustomCommandType = "custom"
|
|
61
|
-
params: CustomParams
|
|
62
|
-
result: Optional[CustomResult]
|
|
55
|
+
params: SerializeAsAny[CustomParams]
|
|
56
|
+
result: Optional[CustomResult] = None
|
|
63
57
|
|
|
64
58
|
_ImplementationCls: Type[CustomImplementation] = CustomImplementation
|
|
65
59
|
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
"""Dispense command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
|
-
from typing import TYPE_CHECKING, Optional, Type, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type, Union, Any
|
|
4
5
|
from typing_extensions import Literal
|
|
5
6
|
|
|
6
|
-
from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
|
|
7
7
|
|
|
8
8
|
from pydantic import Field
|
|
9
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
9
10
|
|
|
10
|
-
from ..types import DeckPoint
|
|
11
11
|
from ..state.update_types import StateUpdate, CLEAR
|
|
12
12
|
from .pipetting_common import (
|
|
13
13
|
PipetteIdMixin,
|
|
14
14
|
DispenseVolumeMixin,
|
|
15
15
|
FlowRateMixin,
|
|
16
|
-
LiquidHandlingWellLocationMixin,
|
|
17
16
|
BaseLiquidHandlingResult,
|
|
18
|
-
DestinationPositionResult,
|
|
19
17
|
OverpressureError,
|
|
18
|
+
dispense_in_place,
|
|
19
|
+
)
|
|
20
|
+
from .movement_common import (
|
|
21
|
+
LiquidHandlingWellLocationMixin,
|
|
22
|
+
DestinationPositionResult,
|
|
23
|
+
StallOrCollisionError,
|
|
24
|
+
move_to_well,
|
|
20
25
|
)
|
|
21
26
|
from .command import (
|
|
22
27
|
AbstractCommandImpl,
|
|
@@ -25,7 +30,6 @@ from .command import (
|
|
|
25
30
|
DefinedErrorData,
|
|
26
31
|
SuccessData,
|
|
27
32
|
)
|
|
28
|
-
from ..errors.error_occurrence import ErrorOccurrence
|
|
29
33
|
|
|
30
34
|
if TYPE_CHECKING:
|
|
31
35
|
from ..execution import MovementHandler, PipettingHandler
|
|
@@ -36,14 +40,19 @@ if TYPE_CHECKING:
|
|
|
36
40
|
DispenseCommandType = Literal["dispense"]
|
|
37
41
|
|
|
38
42
|
|
|
43
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
44
|
+
s.pop("default", None)
|
|
45
|
+
|
|
46
|
+
|
|
39
47
|
class DispenseParams(
|
|
40
48
|
PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin, LiquidHandlingWellLocationMixin
|
|
41
49
|
):
|
|
42
50
|
"""Payload required to dispense to a specific well."""
|
|
43
51
|
|
|
44
|
-
pushOut:
|
|
52
|
+
pushOut: float | SkipJsonSchema[None] = Field(
|
|
45
53
|
None,
|
|
46
54
|
description="push the plunger a small amount farther than necessary for accurate low-volume dispensing",
|
|
55
|
+
json_schema_extra=_remove_default,
|
|
47
56
|
)
|
|
48
57
|
|
|
49
58
|
|
|
@@ -55,7 +64,7 @@ class DispenseResult(BaseLiquidHandlingResult, DestinationPositionResult):
|
|
|
55
64
|
|
|
56
65
|
_ExecuteReturn = Union[
|
|
57
66
|
SuccessData[DispenseResult],
|
|
58
|
-
DefinedErrorData[OverpressureError],
|
|
67
|
+
DefinedErrorData[OverpressureError] | DefinedErrorData[StallOrCollisionError],
|
|
59
68
|
]
|
|
60
69
|
|
|
61
70
|
|
|
@@ -77,7 +86,6 @@ class DispenseImplementation(AbstractCommandImpl[DispenseParams, _ExecuteReturn]
|
|
|
77
86
|
|
|
78
87
|
async def execute(self, params: DispenseParams) -> _ExecuteReturn:
|
|
79
88
|
"""Move to and dispense to the requested well."""
|
|
80
|
-
state_update = StateUpdate()
|
|
81
89
|
well_location = params.wellLocation
|
|
82
90
|
labware_id = params.labwareId
|
|
83
91
|
well_name = params.wellName
|
|
@@ -85,66 +93,92 @@ class DispenseImplementation(AbstractCommandImpl[DispenseParams, _ExecuteReturn]
|
|
|
85
93
|
|
|
86
94
|
# TODO(pbm, 10-15-24): call self._state_view.geometry.validate_dispense_volume_into_well()
|
|
87
95
|
|
|
88
|
-
|
|
96
|
+
move_result = await move_to_well(
|
|
97
|
+
movement=self._movement,
|
|
98
|
+
model_utils=self._model_utils,
|
|
89
99
|
pipette_id=params.pipetteId,
|
|
90
100
|
labware_id=labware_id,
|
|
91
101
|
well_name=well_name,
|
|
92
102
|
well_location=well_location,
|
|
93
103
|
)
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
if isinstance(move_result, DefinedErrorData):
|
|
105
|
+
return move_result
|
|
106
|
+
dispense_result = await dispense_in_place(
|
|
96
107
|
pipette_id=params.pipetteId,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
volume=volume,
|
|
109
|
+
flow_rate=params.flowRate,
|
|
110
|
+
push_out=params.pushOut,
|
|
111
|
+
location_if_error={
|
|
112
|
+
"retryLocation": (
|
|
113
|
+
move_result.public.position.x,
|
|
114
|
+
move_result.public.position.y,
|
|
115
|
+
move_result.public.position.z,
|
|
116
|
+
)
|
|
117
|
+
},
|
|
118
|
+
pipetting=self._pipetting,
|
|
119
|
+
model_utils=self._model_utils,
|
|
100
120
|
)
|
|
101
121
|
|
|
102
|
-
|
|
103
|
-
volume = await self._pipetting.dispense_in_place(
|
|
104
|
-
pipette_id=params.pipetteId,
|
|
105
|
-
volume=volume,
|
|
106
|
-
flow_rate=params.flowRate,
|
|
107
|
-
push_out=params.pushOut,
|
|
108
|
-
)
|
|
109
|
-
except PipetteOverpressureError as e:
|
|
110
|
-
state_update.set_liquid_operated(
|
|
111
|
-
labware_id=labware_id,
|
|
112
|
-
well_name=well_name,
|
|
113
|
-
volume_added=CLEAR,
|
|
114
|
-
)
|
|
122
|
+
if isinstance(dispense_result, DefinedErrorData):
|
|
115
123
|
return DefinedErrorData(
|
|
116
|
-
public=
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
public=dispense_result.public,
|
|
125
|
+
state_update=(
|
|
126
|
+
StateUpdate.reduce(
|
|
127
|
+
move_result.state_update, dispense_result.state_update
|
|
128
|
+
).set_liquid_operated(
|
|
129
|
+
labware_id=labware_id,
|
|
130
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
131
|
+
labware_id, well_name, params.pipetteId
|
|
132
|
+
),
|
|
133
|
+
volume_added=CLEAR,
|
|
134
|
+
)
|
|
135
|
+
),
|
|
136
|
+
state_update_if_false_positive=StateUpdate.reduce(
|
|
137
|
+
move_result.state_update,
|
|
138
|
+
dispense_result.state_update_if_false_positive,
|
|
127
139
|
),
|
|
128
|
-
state_update=state_update,
|
|
129
140
|
)
|
|
130
141
|
else:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
volume_added = (
|
|
143
|
+
self._state_view.pipettes.get_liquid_dispensed_by_ejecting_volume(
|
|
144
|
+
pipette_id=params.pipetteId, volume=dispense_result.public.volume
|
|
145
|
+
)
|
|
135
146
|
)
|
|
147
|
+
if volume_added is not None:
|
|
148
|
+
volume_added *= self._state_view.geometry.get_nozzles_per_well(
|
|
149
|
+
labware_id, well_name, params.pipetteId
|
|
150
|
+
)
|
|
136
151
|
return SuccessData(
|
|
137
|
-
public=DispenseResult(
|
|
138
|
-
|
|
152
|
+
public=DispenseResult(
|
|
153
|
+
volume=dispense_result.public.volume,
|
|
154
|
+
position=move_result.public.position,
|
|
155
|
+
),
|
|
156
|
+
state_update=(
|
|
157
|
+
StateUpdate.reduce(
|
|
158
|
+
move_result.state_update, dispense_result.state_update
|
|
159
|
+
).set_liquid_operated(
|
|
160
|
+
labware_id=labware_id,
|
|
161
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
162
|
+
labware_id, well_name, params.pipetteId
|
|
163
|
+
),
|
|
164
|
+
volume_added=volume_added
|
|
165
|
+
if volume_added is not None
|
|
166
|
+
else CLEAR,
|
|
167
|
+
)
|
|
168
|
+
),
|
|
139
169
|
)
|
|
140
170
|
|
|
141
171
|
|
|
142
|
-
class Dispense(
|
|
172
|
+
class Dispense(
|
|
173
|
+
BaseCommand[
|
|
174
|
+
DispenseParams, DispenseResult, OverpressureError | StallOrCollisionError
|
|
175
|
+
]
|
|
176
|
+
):
|
|
143
177
|
"""Dispense command model."""
|
|
144
178
|
|
|
145
179
|
commandType: DispenseCommandType = "dispense"
|
|
146
180
|
params: DispenseParams
|
|
147
|
-
result: Optional[DispenseResult]
|
|
181
|
+
result: Optional[DispenseResult] = None
|
|
148
182
|
|
|
149
183
|
_ImplementationCls: Type[DispenseImplementation] = DispenseImplementation
|
|
150
184
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Dispense-in-place command request, result, and implementation models."""
|
|
2
|
+
|
|
2
3
|
from __future__ import annotations
|
|
3
|
-
from typing import TYPE_CHECKING, Optional, Type, Union
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Type, Union, Any
|
|
4
5
|
from typing_extensions import Literal
|
|
5
6
|
from pydantic import Field
|
|
6
|
-
|
|
7
|
-
from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
|
|
7
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
8
8
|
|
|
9
9
|
from .pipetting_common import (
|
|
10
10
|
PipetteIdMixin,
|
|
@@ -12,6 +12,7 @@ from .pipetting_common import (
|
|
|
12
12
|
FlowRateMixin,
|
|
13
13
|
BaseLiquidHandlingResult,
|
|
14
14
|
OverpressureError,
|
|
15
|
+
dispense_in_place,
|
|
15
16
|
)
|
|
16
17
|
from .command import (
|
|
17
18
|
AbstractCommandImpl,
|
|
@@ -20,8 +21,7 @@ from .command import (
|
|
|
20
21
|
SuccessData,
|
|
21
22
|
DefinedErrorData,
|
|
22
23
|
)
|
|
23
|
-
from ..
|
|
24
|
-
from ..state.update_types import StateUpdate, CLEAR
|
|
24
|
+
from ..state.update_types import CLEAR
|
|
25
25
|
from ..types import CurrentWell
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
@@ -33,12 +33,17 @@ if TYPE_CHECKING:
|
|
|
33
33
|
DispenseInPlaceCommandType = Literal["dispenseInPlace"]
|
|
34
34
|
|
|
35
35
|
|
|
36
|
+
def _remove_default(s: dict[str, Any]) -> None:
|
|
37
|
+
s.pop("default", None)
|
|
38
|
+
|
|
39
|
+
|
|
36
40
|
class DispenseInPlaceParams(PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin):
|
|
37
41
|
"""Payload required to dispense in place."""
|
|
38
42
|
|
|
39
|
-
pushOut:
|
|
43
|
+
pushOut: float | SkipJsonSchema[None] = Field(
|
|
40
44
|
None,
|
|
41
45
|
description="push the plunger a small amount farther than necessary for accurate low-volume dispensing",
|
|
46
|
+
json_schema_extra=_remove_default,
|
|
42
47
|
)
|
|
43
48
|
|
|
44
49
|
|
|
@@ -74,63 +79,78 @@ class DispenseInPlaceImplementation(
|
|
|
74
79
|
|
|
75
80
|
async def execute(self, params: DispenseInPlaceParams) -> _ExecuteReturn:
|
|
76
81
|
"""Dispense without moving the pipette."""
|
|
77
|
-
state_update = StateUpdate()
|
|
78
82
|
current_location = self._state_view.pipettes.get_current_location()
|
|
79
83
|
current_position = await self._gantry_mover.get_position(params.pipetteId)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
result = await dispense_in_place(
|
|
85
|
+
pipette_id=params.pipetteId,
|
|
86
|
+
volume=params.volume,
|
|
87
|
+
flow_rate=params.flowRate,
|
|
88
|
+
push_out=params.pushOut,
|
|
89
|
+
location_if_error={
|
|
90
|
+
"retryLocation": (
|
|
91
|
+
current_position.x,
|
|
92
|
+
current_position.y,
|
|
93
|
+
current_position.z,
|
|
94
|
+
)
|
|
95
|
+
},
|
|
96
|
+
pipetting=self._pipetting,
|
|
97
|
+
model_utils=self._model_utils,
|
|
98
|
+
)
|
|
99
|
+
if isinstance(result, DefinedErrorData):
|
|
88
100
|
if (
|
|
89
101
|
isinstance(current_location, CurrentWell)
|
|
90
102
|
and current_location.pipette_id == params.pipetteId
|
|
91
103
|
):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
ErrorOccurrence.from_failed(
|
|
103
|
-
id=self._model_utils.generate_id(),
|
|
104
|
-
createdAt=self._model_utils.get_timestamp(),
|
|
105
|
-
error=e,
|
|
106
|
-
)
|
|
107
|
-
],
|
|
108
|
-
errorInfo=(
|
|
109
|
-
{
|
|
110
|
-
"retryLocation": (
|
|
111
|
-
current_position.x,
|
|
112
|
-
current_position.y,
|
|
113
|
-
current_position.z,
|
|
114
|
-
)
|
|
115
|
-
}
|
|
104
|
+
return DefinedErrorData(
|
|
105
|
+
public=result.public,
|
|
106
|
+
state_update=result.state_update.set_liquid_operated(
|
|
107
|
+
labware_id=current_location.labware_id,
|
|
108
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
109
|
+
current_location.labware_id,
|
|
110
|
+
current_location.well_name,
|
|
111
|
+
params.pipetteId,
|
|
112
|
+
),
|
|
113
|
+
volume_added=CLEAR,
|
|
116
114
|
),
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
state_update_if_false_positive=result.state_update_if_false_positive,
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
return result
|
|
120
119
|
else:
|
|
121
120
|
if (
|
|
122
121
|
isinstance(current_location, CurrentWell)
|
|
123
122
|
and current_location.pipette_id == params.pipetteId
|
|
124
123
|
):
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
volume_added = (
|
|
125
|
+
self._state_view.pipettes.get_liquid_dispensed_by_ejecting_volume(
|
|
126
|
+
pipette_id=params.pipetteId, volume=result.public.volume
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
if volume_added is not None:
|
|
130
|
+
volume_added *= self._state_view.geometry.get_nozzles_per_well(
|
|
131
|
+
current_location.labware_id,
|
|
132
|
+
current_location.well_name,
|
|
133
|
+
params.pipetteId,
|
|
134
|
+
)
|
|
135
|
+
return SuccessData(
|
|
136
|
+
public=DispenseInPlaceResult(volume=result.public.volume),
|
|
137
|
+
state_update=result.state_update.set_liquid_operated(
|
|
138
|
+
labware_id=current_location.labware_id,
|
|
139
|
+
well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
|
|
140
|
+
current_location.labware_id,
|
|
141
|
+
current_location.well_name,
|
|
142
|
+
params.pipetteId,
|
|
143
|
+
),
|
|
144
|
+
volume_added=volume_added
|
|
145
|
+
if volume_added is not None
|
|
146
|
+
else CLEAR,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
return SuccessData(
|
|
151
|
+
public=DispenseInPlaceResult(volume=result.public.volume),
|
|
152
|
+
state_update=result.state_update,
|
|
129
153
|
)
|
|
130
|
-
return SuccessData(
|
|
131
|
-
public=DispenseInPlaceResult(volume=volume),
|
|
132
|
-
state_update=state_update,
|
|
133
|
-
)
|
|
134
154
|
|
|
135
155
|
|
|
136
156
|
class DispenseInPlace(
|
|
@@ -140,7 +160,7 @@ class DispenseInPlace(
|
|
|
140
160
|
|
|
141
161
|
commandType: DispenseInPlaceCommandType = "dispenseInPlace"
|
|
142
162
|
params: DispenseInPlaceParams
|
|
143
|
-
result: Optional[DispenseInPlaceResult]
|
|
163
|
+
result: Optional[DispenseInPlaceResult] = None
|
|
144
164
|
|
|
145
165
|
_ImplementationCls: Type[
|
|
146
166
|
DispenseInPlaceImplementation
|