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,29 +1,30 @@
|
|
|
1
1
|
"""Public protocol engine value types and models."""
|
|
2
2
|
from __future__ import annotations
|
|
3
|
-
import re
|
|
4
3
|
from datetime import datetime
|
|
5
4
|
from enum import Enum
|
|
6
5
|
from dataclasses import dataclass
|
|
7
6
|
from pathlib import Path
|
|
7
|
+
from typing import (
|
|
8
|
+
Any,
|
|
9
|
+
Dict,
|
|
10
|
+
FrozenSet,
|
|
11
|
+
List,
|
|
12
|
+
Mapping,
|
|
13
|
+
NamedTuple,
|
|
14
|
+
Optional,
|
|
15
|
+
Tuple,
|
|
16
|
+
Union,
|
|
17
|
+
)
|
|
18
|
+
|
|
8
19
|
from pydantic import (
|
|
20
|
+
ConfigDict,
|
|
9
21
|
BaseModel,
|
|
10
22
|
Field,
|
|
23
|
+
RootModel,
|
|
11
24
|
StrictBool,
|
|
12
25
|
StrictFloat,
|
|
13
26
|
StrictInt,
|
|
14
27
|
StrictStr,
|
|
15
|
-
validator,
|
|
16
|
-
)
|
|
17
|
-
from typing import (
|
|
18
|
-
Optional,
|
|
19
|
-
Union,
|
|
20
|
-
List,
|
|
21
|
-
Dict,
|
|
22
|
-
Any,
|
|
23
|
-
NamedTuple,
|
|
24
|
-
Tuple,
|
|
25
|
-
FrozenSet,
|
|
26
|
-
Mapping,
|
|
27
28
|
)
|
|
28
29
|
from typing_extensions import Literal, TypeGuard
|
|
29
30
|
|
|
@@ -36,7 +37,9 @@ from opentrons.hardware_control.types import (
|
|
|
36
37
|
from opentrons.hardware_control.modules import (
|
|
37
38
|
ModuleType as ModuleType,
|
|
38
39
|
)
|
|
39
|
-
|
|
40
|
+
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
41
|
+
ByTipTypeSetting,
|
|
42
|
+
)
|
|
40
43
|
from opentrons_shared_data.pipette.types import ( # noqa: F401
|
|
41
44
|
# convenience re-export of LabwareUri type
|
|
42
45
|
LabwareUri as LabwareUri,
|
|
@@ -423,6 +426,21 @@ class TipGeometry:
|
|
|
423
426
|
volume: float
|
|
424
427
|
|
|
425
428
|
|
|
429
|
+
class FluidKind(str, Enum):
|
|
430
|
+
"""A kind of fluid that can be inside a pipette."""
|
|
431
|
+
|
|
432
|
+
LIQUID = "LIQUID"
|
|
433
|
+
AIR = "AIR"
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
@dataclass(frozen=True)
|
|
437
|
+
class AspiratedFluid:
|
|
438
|
+
"""Fluid inside a pipette."""
|
|
439
|
+
|
|
440
|
+
kind: FluidKind
|
|
441
|
+
volume: float
|
|
442
|
+
|
|
443
|
+
|
|
426
444
|
class MovementAxis(str, Enum):
|
|
427
445
|
"""Axis on which to issue a relative movement."""
|
|
428
446
|
|
|
@@ -442,6 +460,7 @@ class MotorAxis(str, Enum):
|
|
|
442
460
|
RIGHT_PLUNGER = "rightPlunger"
|
|
443
461
|
EXTENSION_Z = "extensionZ"
|
|
444
462
|
EXTENSION_JAW = "extensionJaw"
|
|
463
|
+
AXIS_96_CHANNEL_CAM = "axis96ChannelCam"
|
|
445
464
|
|
|
446
465
|
|
|
447
466
|
# TODO(mc, 2022-01-18): use opentrons_shared_data.module.types.ModuleModel
|
|
@@ -535,7 +554,7 @@ class ModuleDimensions(BaseModel):
|
|
|
535
554
|
|
|
536
555
|
bareOverallHeight: float
|
|
537
556
|
overLabwareHeight: float
|
|
538
|
-
lidHeight: Optional[float]
|
|
557
|
+
lidHeight: Optional[float] = None
|
|
539
558
|
|
|
540
559
|
|
|
541
560
|
class Vec3f(BaseModel):
|
|
@@ -690,8 +709,8 @@ class LoadedModule(BaseModel):
|
|
|
690
709
|
|
|
691
710
|
id: str
|
|
692
711
|
model: ModuleModel
|
|
693
|
-
location: Optional[DeckSlotLocation]
|
|
694
|
-
serialNumber: Optional[str]
|
|
712
|
+
location: Optional[DeckSlotLocation] = None
|
|
713
|
+
serialNumber: Optional[str] = None
|
|
695
714
|
|
|
696
715
|
|
|
697
716
|
class LabwareOffsetLocation(BaseModel):
|
|
@@ -785,6 +804,10 @@ class LoadedLabware(BaseModel):
|
|
|
785
804
|
location: LabwareLocation = Field(
|
|
786
805
|
..., description="The labware's current location."
|
|
787
806
|
)
|
|
807
|
+
lid_id: Optional[str] = Field(
|
|
808
|
+
None,
|
|
809
|
+
description=("Labware ID of a Lid currently loaded on top of the labware."),
|
|
810
|
+
)
|
|
788
811
|
offsetId: Optional[str] = Field(
|
|
789
812
|
None,
|
|
790
813
|
description=(
|
|
@@ -800,17 +823,14 @@ class LoadedLabware(BaseModel):
|
|
|
800
823
|
)
|
|
801
824
|
|
|
802
825
|
|
|
803
|
-
class HexColor(
|
|
826
|
+
class HexColor(RootModel[str]):
|
|
804
827
|
"""Hex color representation."""
|
|
805
828
|
|
|
806
|
-
|
|
829
|
+
root: str = Field(pattern=r"^#(?:[0-9a-fA-F]{3,4}){1,2}$")
|
|
830
|
+
|
|
807
831
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
match = re.search(r"^#(?:[0-9a-fA-F]{3,4}){1,2}$", v)
|
|
811
|
-
if not match:
|
|
812
|
-
raise ValueError("Color is not a valid hex color.")
|
|
813
|
-
return v
|
|
832
|
+
EmptyLiquidId = Literal["EMPTY"]
|
|
833
|
+
LiquidId = str | EmptyLiquidId
|
|
814
834
|
|
|
815
835
|
|
|
816
836
|
class Liquid(BaseModel):
|
|
@@ -819,7 +839,59 @@ class Liquid(BaseModel):
|
|
|
819
839
|
id: str
|
|
820
840
|
displayName: str
|
|
821
841
|
description: str
|
|
822
|
-
displayColor: Optional[HexColor]
|
|
842
|
+
displayColor: Optional[HexColor] = None
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
class LiquidClassRecord(ByTipTypeSetting, frozen=True):
|
|
846
|
+
"""LiquidClassRecord is our internal representation of an (immutable) liquid class.
|
|
847
|
+
|
|
848
|
+
Conceptually, a liquid class record is the tuple (name, pipette, tip, transfer properties).
|
|
849
|
+
We consider two liquid classes to be the same if every entry in that tuple is the same; and liquid
|
|
850
|
+
classes are different if any entry in the tuple is different.
|
|
851
|
+
|
|
852
|
+
This class defines the tuple via inheritance so that we can reuse the definitions from shared_data.
|
|
853
|
+
"""
|
|
854
|
+
|
|
855
|
+
liquidClassName: str = Field(
|
|
856
|
+
...,
|
|
857
|
+
description="Identifier for the liquid of this liquid class, e.g. glycerol50.",
|
|
858
|
+
)
|
|
859
|
+
pipetteModel: str = Field(
|
|
860
|
+
...,
|
|
861
|
+
description="Identifier for the pipette of this liquid class.",
|
|
862
|
+
)
|
|
863
|
+
# The other fields like tiprack ID, aspirate properties, etc. are pulled in from ByTipTypeSetting.
|
|
864
|
+
|
|
865
|
+
def __hash__(self) -> int:
|
|
866
|
+
"""Hash function for LiquidClassRecord."""
|
|
867
|
+
# Within the Protocol Engine, LiquidClassRecords are immutable, and we'd like to be able to
|
|
868
|
+
# look up LiquidClassRecords by value, which involves hashing. However, Pydantic does not
|
|
869
|
+
# generate a usable hash function if any of the subfields (like Coordinate) are not frozen.
|
|
870
|
+
# So we have to implement the hash function ourselves.
|
|
871
|
+
# Our strategy is to recursively convert this object into a list of (key, value) tuples.
|
|
872
|
+
def dict_to_tuple(d: dict[str, Any]) -> tuple[tuple[str, Any], ...]:
|
|
873
|
+
return tuple(
|
|
874
|
+
(
|
|
875
|
+
field_name,
|
|
876
|
+
dict_to_tuple(value)
|
|
877
|
+
if isinstance(value, dict)
|
|
878
|
+
else tuple(value)
|
|
879
|
+
if isinstance(value, list)
|
|
880
|
+
else value,
|
|
881
|
+
)
|
|
882
|
+
for field_name, value in d.items()
|
|
883
|
+
)
|
|
884
|
+
|
|
885
|
+
return hash(dict_to_tuple(self.model_dump()))
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
class LiquidClassRecordWithId(LiquidClassRecord, frozen=True):
|
|
889
|
+
"""A LiquidClassRecord with its ID, for use in summary lists."""
|
|
890
|
+
|
|
891
|
+
liquidClassId: str = Field(
|
|
892
|
+
...,
|
|
893
|
+
description="Unique identifier for this liquid class.",
|
|
894
|
+
)
|
|
823
895
|
|
|
824
896
|
|
|
825
897
|
class SpeedRange(NamedTuple):
|
|
@@ -976,12 +1048,12 @@ class QuadrantNozzleLayoutConfiguration(BaseModel):
|
|
|
976
1048
|
)
|
|
977
1049
|
frontRightNozzle: str = Field(
|
|
978
1050
|
...,
|
|
979
|
-
|
|
1051
|
+
pattern=NOZZLE_NAME_REGEX,
|
|
980
1052
|
description="The front right nozzle in your configuration.",
|
|
981
1053
|
)
|
|
982
1054
|
backLeftNozzle: str = Field(
|
|
983
1055
|
...,
|
|
984
|
-
|
|
1056
|
+
pattern=NOZZLE_NAME_REGEX,
|
|
985
1057
|
description="The back left nozzle in your configuration.",
|
|
986
1058
|
)
|
|
987
1059
|
|
|
@@ -1041,6 +1113,82 @@ class TipPresenceStatus(str, Enum):
|
|
|
1041
1113
|
}[state]
|
|
1042
1114
|
|
|
1043
1115
|
|
|
1116
|
+
class NextTipInfo(BaseModel):
|
|
1117
|
+
"""Next available tip labware and well name data."""
|
|
1118
|
+
|
|
1119
|
+
labwareId: str = Field(
|
|
1120
|
+
...,
|
|
1121
|
+
description="The labware ID of the tip rack where the next available tip(s) are located.",
|
|
1122
|
+
)
|
|
1123
|
+
tipStartingWell: str = Field(
|
|
1124
|
+
..., description="The (starting) well name of the next available tip(s)."
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
class NoTipReason(Enum):
|
|
1129
|
+
"""The cause of no tip being available for a pipette and tip rack(s)."""
|
|
1130
|
+
|
|
1131
|
+
NO_AVAILABLE_TIPS = "noAvailableTips"
|
|
1132
|
+
STARTING_TIP_WITH_PARTIAL = "startingTipWithPartial"
|
|
1133
|
+
INCOMPATIBLE_CONFIGURATION = "incompatibleConfiguration"
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
class NoTipAvailable(BaseModel):
|
|
1137
|
+
"""No available next tip data."""
|
|
1138
|
+
|
|
1139
|
+
noTipReason: NoTipReason = Field(
|
|
1140
|
+
..., description="The reason why no next available tip could be provided."
|
|
1141
|
+
)
|
|
1142
|
+
message: Optional[str] = Field(
|
|
1143
|
+
None, description="Optional message explaining why a tip wasn't available."
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
class BaseCommandAnnotation(BaseModel):
|
|
1148
|
+
"""Optional annotations for protocol engine commands."""
|
|
1149
|
+
|
|
1150
|
+
commandKeys: List[str] = Field(
|
|
1151
|
+
..., description="Command keys to which this annotation applies"
|
|
1152
|
+
)
|
|
1153
|
+
annotationType: str = Field(
|
|
1154
|
+
..., description="The type of annotation (for machine parsing)"
|
|
1155
|
+
)
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
class SecondOrderCommandAnnotation(BaseCommandAnnotation):
|
|
1159
|
+
"""Annotates a group of atomic commands which were the direct result of a second order command.
|
|
1160
|
+
|
|
1161
|
+
Examples of second order commands would be transfer, consolidate, mix, etc.
|
|
1162
|
+
"""
|
|
1163
|
+
|
|
1164
|
+
annotationType: Literal["secondOrderCommand"] = "secondOrderCommand"
|
|
1165
|
+
params: Dict[str, Any] = Field(
|
|
1166
|
+
...,
|
|
1167
|
+
description="Key value pairs of the parameters passed to the second order command that this annotates.",
|
|
1168
|
+
)
|
|
1169
|
+
machineReadableName: str = Field(
|
|
1170
|
+
...,
|
|
1171
|
+
description="The name of the second order command in the form that the generating software refers to it",
|
|
1172
|
+
)
|
|
1173
|
+
userSpecifiedName: Optional[str] = Field(
|
|
1174
|
+
None, description="The optional user-specified name of the second order command"
|
|
1175
|
+
)
|
|
1176
|
+
userSpecifiedDescription: Optional[str] = Field(
|
|
1177
|
+
None,
|
|
1178
|
+
description="The optional user-specified description of the second order command",
|
|
1179
|
+
)
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
class CustomCommandAnnotation(BaseCommandAnnotation):
|
|
1183
|
+
"""Annotates a group of atomic commands in some manner that Opentrons software does not anticipate or originate."""
|
|
1184
|
+
|
|
1185
|
+
annotationType: Literal["custom"] = "custom"
|
|
1186
|
+
model_config = ConfigDict(extra="allow")
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
CommandAnnotation = Union[SecondOrderCommandAnnotation, CustomCommandAnnotation]
|
|
1190
|
+
|
|
1191
|
+
|
|
1044
1192
|
# TODO (spp, 2024-04-02): move all RTP types to runner
|
|
1045
1193
|
class RTPBase(BaseModel):
|
|
1046
1194
|
"""Parameters defined in a protocol."""
|
|
@@ -41,7 +41,10 @@ async def extract_labware_definitions(
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
async def _extract_from_labware_file(path: Path) -> LabwareDefinition:
|
|
44
|
-
|
|
44
|
+
def _do_parse() -> LabwareDefinition:
|
|
45
|
+
return LabwareDefinition.model_validate_json(path.read_bytes())
|
|
46
|
+
|
|
47
|
+
return await anyio.to_thread.run_sync(_do_parse)
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
async def _extract_from_json_protocol_file(path: Path) -> List[LabwareDefinition]:
|
|
@@ -52,7 +55,7 @@ async def _extract_from_json_protocol_file(path: Path) -> List[LabwareDefinition
|
|
|
52
55
|
# which require this labwareDefinitions key.
|
|
53
56
|
unvalidated_definitions = json_contents["labwareDefinitions"].values()
|
|
54
57
|
validated_definitions = [
|
|
55
|
-
LabwareDefinition.
|
|
58
|
+
LabwareDefinition.model_validate(u) for u in unvalidated_definitions
|
|
56
59
|
]
|
|
57
60
|
return validated_definitions
|
|
58
61
|
|
|
@@ -60,7 +60,7 @@ class FileFormatValidator:
|
|
|
60
60
|
async def _validate_labware_definition(info: IdentifiedLabwareDefinition) -> None:
|
|
61
61
|
def validate_sync() -> None:
|
|
62
62
|
try:
|
|
63
|
-
LabwareDefinition.
|
|
63
|
+
LabwareDefinition.model_validate(info.unvalidated_json)
|
|
64
64
|
except PydanticValidationError as e:
|
|
65
65
|
raise FileFormatValidationError(
|
|
66
66
|
message=f"{info.original_file.name} could not be read as a labware definition.",
|
|
@@ -133,17 +133,17 @@ async def _validate_json_protocol(info: IdentifiedJsonMain) -> None:
|
|
|
133
133
|
def validate_sync() -> None:
|
|
134
134
|
if info.schema_version == 8:
|
|
135
135
|
try:
|
|
136
|
-
JsonProtocolV8.
|
|
136
|
+
JsonProtocolV8.model_validate(info.unvalidated_json)
|
|
137
137
|
except PydanticValidationError as pve:
|
|
138
138
|
_handle_v8_json_protocol_validation_error(info, pve)
|
|
139
139
|
else:
|
|
140
140
|
try:
|
|
141
141
|
if info.schema_version == 7:
|
|
142
|
-
JsonProtocolV7.
|
|
142
|
+
JsonProtocolV7.model_validate(info.unvalidated_json)
|
|
143
143
|
elif info.schema_version == 6:
|
|
144
|
-
JsonProtocolV6.
|
|
144
|
+
JsonProtocolV6.model_validate(info.unvalidated_json)
|
|
145
145
|
else:
|
|
146
|
-
JsonProtocolUpToV5.
|
|
146
|
+
JsonProtocolUpToV5.model_validate(info.unvalidated_json)
|
|
147
147
|
except PydanticValidationError as e:
|
|
148
148
|
raise FileFormatValidationError._generic_json_failure(info, e) from e
|
|
149
149
|
|
|
@@ -30,11 +30,17 @@ class JsonFileReader:
|
|
|
30
30
|
},
|
|
31
31
|
)
|
|
32
32
|
if protocol_source.config.schema_version == 6:
|
|
33
|
-
return ProtocolSchemaV6.
|
|
33
|
+
return ProtocolSchemaV6.model_validate_json(
|
|
34
|
+
protocol_source.main_file.read_bytes()
|
|
35
|
+
)
|
|
34
36
|
elif protocol_source.config.schema_version == 7:
|
|
35
|
-
return ProtocolSchemaV7.
|
|
37
|
+
return ProtocolSchemaV7.model_validate_json(
|
|
38
|
+
protocol_source.main_file.read_bytes()
|
|
39
|
+
)
|
|
36
40
|
elif protocol_source.config.schema_version == 8:
|
|
37
|
-
return ProtocolSchemaV8.
|
|
41
|
+
return ProtocolSchemaV8.model_validate_json(
|
|
42
|
+
protocol_source.main_file.read_bytes()
|
|
43
|
+
)
|
|
38
44
|
else:
|
|
39
45
|
raise ProtocolFilesInvalidError(
|
|
40
46
|
message=f"{name} is a JSON protocol v{protocol_source.config.schema_version} which this robot cannot execute",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Translation of JSON protocol commands into ProtocolEngine commands."""
|
|
2
|
-
|
|
3
|
-
from
|
|
2
|
+
|
|
3
|
+
from typing import List, Union, Iterator
|
|
4
|
+
from pydantic import ValidationError as PydanticValidationError, TypeAdapter
|
|
4
5
|
|
|
5
6
|
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
6
7
|
from opentrons_shared_data.protocol.models import (
|
|
@@ -10,6 +11,8 @@ from opentrons_shared_data.protocol.models import (
|
|
|
10
11
|
protocol_schema_v7,
|
|
11
12
|
ProtocolSchemaV8,
|
|
12
13
|
protocol_schema_v8,
|
|
14
|
+
Location,
|
|
15
|
+
# CommandSchemaId,
|
|
13
16
|
)
|
|
14
17
|
from opentrons_shared_data import command as command_schema
|
|
15
18
|
from opentrons_shared_data.errors.exceptions import InvalidProtocolData, PythonException
|
|
@@ -22,7 +25,7 @@ from opentrons.protocol_engine import (
|
|
|
22
25
|
DeckSlotLocation,
|
|
23
26
|
Liquid,
|
|
24
27
|
)
|
|
25
|
-
from opentrons.protocol_engine.types import HexColor
|
|
28
|
+
from opentrons.protocol_engine.types import HexColor, CommandAnnotation
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
class CommandTranslatorError(Exception):
|
|
@@ -31,6 +34,15 @@ class CommandTranslatorError(Exception):
|
|
|
31
34
|
pass
|
|
32
35
|
|
|
33
36
|
|
|
37
|
+
# Each time a TypeAdapter is instantiated, it will construct a new validator and
|
|
38
|
+
# serializer. To improve performance, TypeAdapters are instantiated once.
|
|
39
|
+
# See https://docs.pydantic.dev/latest/concepts/performance/#typeadapter-instantiated-once
|
|
40
|
+
LabwareLocationAdapter: TypeAdapter[LabwareLocation] = TypeAdapter(LabwareLocation)
|
|
41
|
+
CommandAnnotationAdapter: TypeAdapter[CommandAnnotation] = TypeAdapter(
|
|
42
|
+
CommandAnnotation
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
34
46
|
def _translate_labware_command(
|
|
35
47
|
protocol: ProtocolSchemaV6,
|
|
36
48
|
command: protocol_schema_v6.Command,
|
|
@@ -41,6 +53,8 @@ def _translate_labware_command(
|
|
|
41
53
|
assert labware_id is not None
|
|
42
54
|
definition_id = protocol.labware[labware_id].definitionId
|
|
43
55
|
assert definition_id is not None
|
|
56
|
+
|
|
57
|
+
location = command.params.location
|
|
44
58
|
labware_command = pe_commands.LoadLabwareCreate(
|
|
45
59
|
params=pe_commands.LoadLabwareParams(
|
|
46
60
|
labwareId=command.params.labwareId,
|
|
@@ -48,10 +62,8 @@ def _translate_labware_command(
|
|
|
48
62
|
version=protocol.labwareDefinitions[definition_id].version,
|
|
49
63
|
namespace=protocol.labwareDefinitions[definition_id].namespace,
|
|
50
64
|
loadName=protocol.labwareDefinitions[definition_id].parameters.loadName,
|
|
51
|
-
location=
|
|
52
|
-
|
|
53
|
-
LabwareLocation, # type: ignore[arg-type]
|
|
54
|
-
command.params.location,
|
|
65
|
+
location=LabwareLocationAdapter.validate_python(
|
|
66
|
+
location.model_dump() if isinstance(location, Location) else location
|
|
55
67
|
),
|
|
56
68
|
),
|
|
57
69
|
key=command.key,
|
|
@@ -70,6 +82,7 @@ def _translate_v7_labware_command(
|
|
|
70
82
|
assert command.params.namespace is not None
|
|
71
83
|
assert command.params.loadName is not None
|
|
72
84
|
|
|
85
|
+
location = command.params.location
|
|
73
86
|
labware_command = pe_commands.LoadLabwareCreate(
|
|
74
87
|
params=pe_commands.LoadLabwareParams(
|
|
75
88
|
labwareId=command.params.labwareId,
|
|
@@ -77,10 +90,8 @@ def _translate_v7_labware_command(
|
|
|
77
90
|
version=command.params.version,
|
|
78
91
|
namespace=command.params.namespace,
|
|
79
92
|
loadName=command.params.loadName,
|
|
80
|
-
location=
|
|
81
|
-
|
|
82
|
-
LabwareLocation, # type: ignore[arg-type]
|
|
83
|
-
command.params.location,
|
|
93
|
+
location=LabwareLocationAdapter.validate_python(
|
|
94
|
+
location.model_dump() if isinstance(location, Location) else location
|
|
84
95
|
),
|
|
85
96
|
),
|
|
86
97
|
key=command.key,
|
|
@@ -98,10 +109,14 @@ def _translate_module_command(
|
|
|
98
109
|
# load module command must contain module_id. modules cannot be None.
|
|
99
110
|
assert module_id is not None
|
|
100
111
|
assert modules is not None
|
|
112
|
+
|
|
113
|
+
location = command.params.location
|
|
101
114
|
translated_obj = pe_commands.LoadModuleCreate(
|
|
102
115
|
params=pe_commands.LoadModuleParams(
|
|
103
116
|
model=ModuleModel(modules[module_id].model),
|
|
104
|
-
location=DeckSlotLocation.
|
|
117
|
+
location=DeckSlotLocation.model_validate(
|
|
118
|
+
location.model_dump() if isinstance(location, Location) else location
|
|
119
|
+
),
|
|
105
120
|
moduleId=command.params.moduleId,
|
|
106
121
|
),
|
|
107
122
|
key=command.key,
|
|
@@ -117,10 +132,13 @@ def _translate_v7_module_command(
|
|
|
117
132
|
# load module command must contain module_id. modules cannot be None.
|
|
118
133
|
assert module_id is not None
|
|
119
134
|
assert command.params.model is not None
|
|
135
|
+
location = command.params.location
|
|
120
136
|
translated_obj = pe_commands.LoadModuleCreate(
|
|
121
137
|
params=pe_commands.LoadModuleParams(
|
|
122
138
|
model=ModuleModel(command.params.model),
|
|
123
|
-
location=DeckSlotLocation.
|
|
139
|
+
location=DeckSlotLocation.model_validate(
|
|
140
|
+
location.model_dump() if isinstance(location, Location) else location
|
|
141
|
+
),
|
|
124
142
|
moduleId=command.params.moduleId,
|
|
125
143
|
),
|
|
126
144
|
key=command.key,
|
|
@@ -171,9 +189,9 @@ def _translate_simple_command(
|
|
|
171
189
|
protocol_schema_v6.Command,
|
|
172
190
|
protocol_schema_v7.Command,
|
|
173
191
|
protocol_schema_v8.Command,
|
|
174
|
-
]
|
|
192
|
+
],
|
|
175
193
|
) -> pe_commands.CommandCreate:
|
|
176
|
-
dict_command = command.
|
|
194
|
+
dict_command = command.model_dump(exclude_none=True)
|
|
177
195
|
|
|
178
196
|
# map deprecated `delay` commands to `waitForResume` / `waitForDuration`
|
|
179
197
|
if dict_command["commandType"] == "delay":
|
|
@@ -182,15 +200,7 @@ def _translate_simple_command(
|
|
|
182
200
|
else:
|
|
183
201
|
dict_command["commandType"] = "waitForDuration"
|
|
184
202
|
|
|
185
|
-
|
|
186
|
-
pe_commands.CommandCreate,
|
|
187
|
-
parse_obj_as(
|
|
188
|
-
# https://github.com/samuelcolvin/pydantic/issues/1847
|
|
189
|
-
pe_commands.CommandCreate, # type: ignore[arg-type]
|
|
190
|
-
dict_command,
|
|
191
|
-
),
|
|
192
|
-
)
|
|
193
|
-
return translated_obj
|
|
203
|
+
return pe_commands.CommandCreateAdapter.validate_python(dict_command)
|
|
194
204
|
|
|
195
205
|
|
|
196
206
|
class JsonTranslator:
|
|
@@ -207,7 +217,7 @@ class JsonTranslator:
|
|
|
207
217
|
id=liquid_id,
|
|
208
218
|
displayName=liquid.displayName,
|
|
209
219
|
description=liquid.description,
|
|
210
|
-
displayColor=HexColor(
|
|
220
|
+
displayColor=HexColor(liquid.displayColor)
|
|
211
221
|
if liquid.displayColor is not None
|
|
212
222
|
else None,
|
|
213
223
|
)
|
|
@@ -284,3 +294,19 @@ class JsonTranslator:
|
|
|
284
294
|
)
|
|
285
295
|
|
|
286
296
|
return list(translate_all_commands())
|
|
297
|
+
|
|
298
|
+
def translate_command_annotations(
|
|
299
|
+
self,
|
|
300
|
+
protocol: Union[ProtocolSchemaV8, ProtocolSchemaV7, ProtocolSchemaV6],
|
|
301
|
+
) -> List[CommandAnnotation]:
|
|
302
|
+
"""Translate command annotations in json protocol schema v8."""
|
|
303
|
+
if isinstance(protocol, (ProtocolSchemaV6, ProtocolSchemaV7)):
|
|
304
|
+
return []
|
|
305
|
+
else:
|
|
306
|
+
command_annotations: List[CommandAnnotation] = [
|
|
307
|
+
CommandAnnotationAdapter.validate_python(
|
|
308
|
+
command_annotation.model_dump(),
|
|
309
|
+
)
|
|
310
|
+
for command_annotation in protocol.commandAnnotations
|
|
311
|
+
]
|
|
312
|
+
return command_annotations
|