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,1311 +0,0 @@
|
|
|
1
|
-
"""Public protocol engine value types and models."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
from enum import Enum
|
|
5
|
-
from dataclasses import dataclass
|
|
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
|
-
|
|
19
|
-
from pydantic import (
|
|
20
|
-
ConfigDict,
|
|
21
|
-
BaseModel,
|
|
22
|
-
Field,
|
|
23
|
-
RootModel,
|
|
24
|
-
StrictBool,
|
|
25
|
-
StrictFloat,
|
|
26
|
-
StrictInt,
|
|
27
|
-
StrictStr,
|
|
28
|
-
)
|
|
29
|
-
from typing_extensions import Literal, TypeGuard
|
|
30
|
-
|
|
31
|
-
from opentrons_shared_data.pipette.types import PipetteNameType
|
|
32
|
-
from opentrons.types import MountType, DeckSlotName, StagingSlotName
|
|
33
|
-
from opentrons.hardware_control.types import (
|
|
34
|
-
TipStateType as HwTipStateType,
|
|
35
|
-
InstrumentProbeType,
|
|
36
|
-
)
|
|
37
|
-
from opentrons.hardware_control.modules import (
|
|
38
|
-
ModuleType as ModuleType,
|
|
39
|
-
)
|
|
40
|
-
from opentrons_shared_data.liquid_classes.liquid_class_definition import (
|
|
41
|
-
ByTipTypeSetting,
|
|
42
|
-
)
|
|
43
|
-
from opentrons_shared_data.pipette.types import ( # noqa: F401
|
|
44
|
-
# convenience re-export of LabwareUri type
|
|
45
|
-
LabwareUri as LabwareUri,
|
|
46
|
-
)
|
|
47
|
-
from opentrons_shared_data.module.types import ModuleType as SharedDataModuleType
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# todo(mm, 2024-06-24): This monolithic status field is getting to be a bit much.
|
|
51
|
-
# We should consider splitting this up into multiple fields.
|
|
52
|
-
class EngineStatus(str, Enum):
|
|
53
|
-
"""Current execution status of a ProtocolEngine.
|
|
54
|
-
|
|
55
|
-
This is a high-level summary of what the robot is doing and what interactions are
|
|
56
|
-
appropriate.
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
# Statuses for an ongoing run:
|
|
60
|
-
|
|
61
|
-
IDLE = "idle"
|
|
62
|
-
"""The protocol has not been started yet.
|
|
63
|
-
|
|
64
|
-
The robot may truly be idle, or it may be executing commands with `intent: "setup"`.
|
|
65
|
-
"""
|
|
66
|
-
|
|
67
|
-
RUNNING = "running"
|
|
68
|
-
"""The engine is actively running the protocol."""
|
|
69
|
-
|
|
70
|
-
PAUSED = "paused"
|
|
71
|
-
"""A pause has been requested. Activity is paused, or will pause soon.
|
|
72
|
-
|
|
73
|
-
(There is currently no way to tell which.)
|
|
74
|
-
"""
|
|
75
|
-
|
|
76
|
-
BLOCKED_BY_OPEN_DOOR = "blocked-by-open-door"
|
|
77
|
-
"""The robot's door is open. Activity is paused, or will pause soon."""
|
|
78
|
-
|
|
79
|
-
STOP_REQUESTED = "stop-requested"
|
|
80
|
-
"""A stop has been requested. Activity will stop soon."""
|
|
81
|
-
|
|
82
|
-
FINISHING = "finishing"
|
|
83
|
-
"""The robot is doing post-run cleanup, like homing and dropping tips."""
|
|
84
|
-
|
|
85
|
-
# Statuses for error recovery mode:
|
|
86
|
-
|
|
87
|
-
AWAITING_RECOVERY = "awaiting-recovery"
|
|
88
|
-
"""The engine is waiting for external input to recover from a nonfatal error.
|
|
89
|
-
|
|
90
|
-
New commands with `intent: "fixit"` may be enqueued, which will run immediately.
|
|
91
|
-
The run can't be paused in this state, but it can be canceled, or resumed from the
|
|
92
|
-
next protocol command if recovery is complete.
|
|
93
|
-
"""
|
|
94
|
-
|
|
95
|
-
AWAITING_RECOVERY_PAUSED = "awaiting-recovery-paused"
|
|
96
|
-
"""The engine is paused while in error recovery mode. Activity is paused, or will pause soon.
|
|
97
|
-
|
|
98
|
-
This state is not possible to enter manually. It happens when an open door
|
|
99
|
-
gets closed during error recovery.
|
|
100
|
-
"""
|
|
101
|
-
|
|
102
|
-
AWAITING_RECOVERY_BLOCKED_BY_OPEN_DOOR = "awaiting-recovery-blocked-by-open-door"
|
|
103
|
-
"""The robot's door is open while in recovery mode. Activity is paused, or will pause soon."""
|
|
104
|
-
|
|
105
|
-
# Terminal statuses:
|
|
106
|
-
|
|
107
|
-
STOPPED = "stopped"
|
|
108
|
-
"""All activity is over; it was stopped by an explicit external request."""
|
|
109
|
-
|
|
110
|
-
FAILED = "failed"
|
|
111
|
-
"""All activity is over; there was a fatal error."""
|
|
112
|
-
|
|
113
|
-
SUCCEEDED = "succeeded"
|
|
114
|
-
"""All activity is over; things completed without any fatal error."""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
class DeckSlotLocation(BaseModel):
|
|
118
|
-
"""The location of something placed in a single deck slot."""
|
|
119
|
-
|
|
120
|
-
slotName: DeckSlotName = Field(
|
|
121
|
-
...,
|
|
122
|
-
description=(
|
|
123
|
-
# This description should be kept in sync with LabwareOffsetLocation.slotName.
|
|
124
|
-
"A slot on the robot's deck."
|
|
125
|
-
"\n\n"
|
|
126
|
-
'The plain numbers like `"5"` are for the OT-2,'
|
|
127
|
-
' and the coordinates like `"C2"` are for the Flex.'
|
|
128
|
-
"\n\n"
|
|
129
|
-
"When you provide one of these values, you can use either style."
|
|
130
|
-
" It will automatically be converted to match the robot."
|
|
131
|
-
"\n\n"
|
|
132
|
-
"When one of these values is returned, it will always match the robot."
|
|
133
|
-
),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
class StagingSlotLocation(BaseModel):
|
|
138
|
-
"""The location of something placed in a single staging slot."""
|
|
139
|
-
|
|
140
|
-
slotName: StagingSlotName = Field(
|
|
141
|
-
...,
|
|
142
|
-
description=(
|
|
143
|
-
# This description should be kept in sync with LabwareOffsetLocation.slotName.
|
|
144
|
-
"A slot on the robot's staging area."
|
|
145
|
-
"\n\n"
|
|
146
|
-
"These apply only to the Flex. The OT-2 has no staging slots."
|
|
147
|
-
),
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
class AddressableAreaLocation(BaseModel):
|
|
152
|
-
"""The location of something place in an addressable area. This is a superset of deck slots."""
|
|
153
|
-
|
|
154
|
-
addressableAreaName: str = Field(
|
|
155
|
-
...,
|
|
156
|
-
description=(
|
|
157
|
-
"The name of the addressable area that you want to use."
|
|
158
|
-
" Valid values are the `id`s of `addressableArea`s in the"
|
|
159
|
-
" [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck)."
|
|
160
|
-
),
|
|
161
|
-
)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
class ModuleLocation(BaseModel):
|
|
165
|
-
"""The location of something placed atop a hardware module."""
|
|
166
|
-
|
|
167
|
-
moduleId: str = Field(
|
|
168
|
-
...,
|
|
169
|
-
description="The ID of a loaded module from a prior `loadModule` command.",
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
class OnLabwareLocation(BaseModel):
|
|
174
|
-
"""The location of something placed atop another labware."""
|
|
175
|
-
|
|
176
|
-
labwareId: str = Field(
|
|
177
|
-
...,
|
|
178
|
-
description="The ID of a loaded Labware from a prior `loadLabware` command.",
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
_OffDeckLocationType = Literal["offDeck"]
|
|
183
|
-
OFF_DECK_LOCATION: _OffDeckLocationType = "offDeck"
|
|
184
|
-
|
|
185
|
-
LabwareLocation = Union[
|
|
186
|
-
DeckSlotLocation,
|
|
187
|
-
ModuleLocation,
|
|
188
|
-
OnLabwareLocation,
|
|
189
|
-
_OffDeckLocationType,
|
|
190
|
-
AddressableAreaLocation,
|
|
191
|
-
]
|
|
192
|
-
"""Union of all locations where it's legal to keep a labware."""
|
|
193
|
-
|
|
194
|
-
OnDeckLabwareLocation = Union[
|
|
195
|
-
DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
|
|
196
|
-
]
|
|
197
|
-
|
|
198
|
-
NonStackedLocation = Union[
|
|
199
|
-
DeckSlotLocation, AddressableAreaLocation, ModuleLocation, _OffDeckLocationType
|
|
200
|
-
]
|
|
201
|
-
"""Union of all locations where it's legal to keep a labware that can't be stacked on another labware"""
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
class WellOrigin(str, Enum):
|
|
205
|
-
"""Origin of WellLocation offset.
|
|
206
|
-
|
|
207
|
-
Props:
|
|
208
|
-
TOP: the top-center of the well
|
|
209
|
-
BOTTOM: the bottom-center of the well
|
|
210
|
-
CENTER: the middle-center of the well
|
|
211
|
-
MENISCUS: the meniscus-center of the well
|
|
212
|
-
"""
|
|
213
|
-
|
|
214
|
-
TOP = "top"
|
|
215
|
-
BOTTOM = "bottom"
|
|
216
|
-
CENTER = "center"
|
|
217
|
-
MENISCUS = "meniscus"
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
class PickUpTipWellOrigin(str, Enum):
|
|
221
|
-
"""The origin of a PickUpTipWellLocation offset.
|
|
222
|
-
|
|
223
|
-
Props:
|
|
224
|
-
TOP: the top-center of the well
|
|
225
|
-
BOTTOM: the bottom-center of the well
|
|
226
|
-
CENTER: the middle-center of the well
|
|
227
|
-
"""
|
|
228
|
-
|
|
229
|
-
TOP = "top"
|
|
230
|
-
BOTTOM = "bottom"
|
|
231
|
-
CENTER = "center"
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
class DropTipWellOrigin(str, Enum):
|
|
235
|
-
"""The origin of a DropTipWellLocation offset.
|
|
236
|
-
|
|
237
|
-
Props:
|
|
238
|
-
TOP: the top-center of the well
|
|
239
|
-
BOTTOM: the bottom-center of the well
|
|
240
|
-
CENTER: the middle-center of the well
|
|
241
|
-
DEFAULT: the default drop-tip location of the well,
|
|
242
|
-
based on pipette configuration and length of the tip.
|
|
243
|
-
"""
|
|
244
|
-
|
|
245
|
-
TOP = "top"
|
|
246
|
-
BOTTOM = "bottom"
|
|
247
|
-
CENTER = "center"
|
|
248
|
-
DEFAULT = "default"
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
# This is deliberately a separate type from Vec3f to let components default to 0.
|
|
252
|
-
class WellOffset(BaseModel):
|
|
253
|
-
"""An offset vector in (x, y, z)."""
|
|
254
|
-
|
|
255
|
-
x: float = 0
|
|
256
|
-
y: float = 0
|
|
257
|
-
z: float = 0
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
class WellLocation(BaseModel):
|
|
261
|
-
"""A relative location in reference to a well's location."""
|
|
262
|
-
|
|
263
|
-
origin: WellOrigin = WellOrigin.TOP
|
|
264
|
-
offset: WellOffset = Field(default_factory=WellOffset)
|
|
265
|
-
volumeOffset: float = Field(
|
|
266
|
-
default=0.0,
|
|
267
|
-
description="""A volume of liquid, in µL, to offset the z-axis offset.""",
|
|
268
|
-
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
class LiquidHandlingWellLocation(BaseModel):
|
|
272
|
-
"""A relative location in reference to a well's location.
|
|
273
|
-
|
|
274
|
-
To be used with commands that handle liquids.
|
|
275
|
-
"""
|
|
276
|
-
|
|
277
|
-
origin: WellOrigin = WellOrigin.TOP
|
|
278
|
-
offset: WellOffset = Field(default_factory=WellOffset)
|
|
279
|
-
volumeOffset: Union[float, Literal["operationVolume"]] = Field(
|
|
280
|
-
default=0.0,
|
|
281
|
-
description="""A volume of liquid, in µL, to offset the z-axis offset. When "operationVolume" is specified, this volume is pulled from the command volume parameter.""",
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
class PickUpTipWellLocation(BaseModel):
|
|
286
|
-
"""A relative location in reference to a well's location.
|
|
287
|
-
|
|
288
|
-
To be used for picking up tips.
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
origin: PickUpTipWellOrigin = PickUpTipWellOrigin.TOP
|
|
292
|
-
offset: WellOffset = Field(default_factory=WellOffset)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
class DropTipWellLocation(BaseModel):
|
|
296
|
-
"""Like WellLocation, but for dropping tips.
|
|
297
|
-
|
|
298
|
-
Unlike a typical WellLocation, the location for a drop tip
|
|
299
|
-
defaults to location based on the tip length rather than the well's top.
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
origin: DropTipWellOrigin = DropTipWellOrigin.DEFAULT
|
|
303
|
-
offset: WellOffset = Field(default_factory=WellOffset)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
@dataclass(frozen=True)
|
|
307
|
-
class Dimensions:
|
|
308
|
-
"""Dimensions of an object in deck-space."""
|
|
309
|
-
|
|
310
|
-
x: float
|
|
311
|
-
y: float
|
|
312
|
-
z: float
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
# TODO(mm, 2022-11-07): Deduplicate with Vec3f.
|
|
316
|
-
class DeckPoint(BaseModel):
|
|
317
|
-
"""Coordinates of a point in deck space."""
|
|
318
|
-
|
|
319
|
-
x: float
|
|
320
|
-
y: float
|
|
321
|
-
z: float
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
# TODO(mm, 2023-05-10): Deduplicate with constants in
|
|
325
|
-
# opentrons.protocols.api_support.deck_type
|
|
326
|
-
# and consider moving to shared-data.
|
|
327
|
-
class DeckType(str, Enum):
|
|
328
|
-
"""Types of deck available."""
|
|
329
|
-
|
|
330
|
-
OT2_STANDARD = "ot2_standard"
|
|
331
|
-
OT2_SHORT_TRASH = "ot2_short_trash"
|
|
332
|
-
OT3_STANDARD = "ot3_standard"
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
class LoadedPipette(BaseModel):
|
|
336
|
-
"""A pipette that has been loaded."""
|
|
337
|
-
|
|
338
|
-
id: str
|
|
339
|
-
pipetteName: PipetteNameType
|
|
340
|
-
mount: MountType
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
@dataclass
|
|
344
|
-
class FlowRates:
|
|
345
|
-
"""Default and current flow rates for a pipette."""
|
|
346
|
-
|
|
347
|
-
default_blow_out: Dict[str, float]
|
|
348
|
-
default_aspirate: Dict[str, float]
|
|
349
|
-
default_dispense: Dict[str, float]
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
@dataclass(frozen=True)
|
|
353
|
-
class CurrentWell:
|
|
354
|
-
"""The latest well that the robot has accessed."""
|
|
355
|
-
|
|
356
|
-
pipette_id: str
|
|
357
|
-
labware_id: str
|
|
358
|
-
well_name: str
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
class LoadedVolumeInfo(BaseModel):
|
|
362
|
-
"""A well's liquid volume, initialized by a LoadLiquid, updated by Aspirate and Dispense."""
|
|
363
|
-
|
|
364
|
-
volume: Optional[float] = None
|
|
365
|
-
last_loaded: datetime
|
|
366
|
-
operations_since_load: int
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
class ProbedHeightInfo(BaseModel):
|
|
370
|
-
"""A well's liquid height, initialized by a LiquidProbe, cleared by Aspirate and Dispense."""
|
|
371
|
-
|
|
372
|
-
height: Optional[float] = None
|
|
373
|
-
last_probed: datetime
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
class ProbedVolumeInfo(BaseModel):
|
|
377
|
-
"""A well's liquid volume, initialized by a LiquidProbe, updated by Aspirate and Dispense."""
|
|
378
|
-
|
|
379
|
-
volume: Optional[float] = None
|
|
380
|
-
last_probed: datetime
|
|
381
|
-
operations_since_probe: int
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
class WellInfoSummary(BaseModel):
|
|
385
|
-
"""Payload for a well's liquid info in StateSummary."""
|
|
386
|
-
|
|
387
|
-
labware_id: str
|
|
388
|
-
well_name: str
|
|
389
|
-
loaded_volume: Optional[float] = None
|
|
390
|
-
probed_height: Optional[float] = None
|
|
391
|
-
probed_volume: Optional[float] = None
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
@dataclass
|
|
395
|
-
class WellLiquidInfo:
|
|
396
|
-
"""Tracked and sensed information about liquid in a well."""
|
|
397
|
-
|
|
398
|
-
probed_height: Optional[ProbedHeightInfo]
|
|
399
|
-
loaded_volume: Optional[LoadedVolumeInfo]
|
|
400
|
-
probed_volume: Optional[ProbedVolumeInfo]
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
@dataclass(frozen=True)
|
|
404
|
-
class CurrentAddressableArea:
|
|
405
|
-
"""The latest addressable area the robot has accessed."""
|
|
406
|
-
|
|
407
|
-
pipette_id: str
|
|
408
|
-
addressable_area_name: str
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
CurrentPipetteLocation = Union[CurrentWell, CurrentAddressableArea]
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
@dataclass(frozen=True)
|
|
415
|
-
class TipGeometry:
|
|
416
|
-
"""Tip geometry data.
|
|
417
|
-
|
|
418
|
-
Props:
|
|
419
|
-
length: The effective length (total length minus overlap) of a tip in mm.
|
|
420
|
-
diameter: Tip diameter in mm.
|
|
421
|
-
volume: Maximum volume in µL.
|
|
422
|
-
"""
|
|
423
|
-
|
|
424
|
-
length: float
|
|
425
|
-
diameter: float
|
|
426
|
-
volume: float
|
|
427
|
-
|
|
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
|
-
|
|
444
|
-
class MovementAxis(str, Enum):
|
|
445
|
-
"""Axis on which to issue a relative movement."""
|
|
446
|
-
|
|
447
|
-
X = "x"
|
|
448
|
-
Y = "y"
|
|
449
|
-
Z = "z"
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
class MotorAxis(str, Enum):
|
|
453
|
-
"""Motor axis on which to issue a home command."""
|
|
454
|
-
|
|
455
|
-
X = "x"
|
|
456
|
-
Y = "y"
|
|
457
|
-
LEFT_Z = "leftZ"
|
|
458
|
-
RIGHT_Z = "rightZ"
|
|
459
|
-
LEFT_PLUNGER = "leftPlunger"
|
|
460
|
-
RIGHT_PLUNGER = "rightPlunger"
|
|
461
|
-
EXTENSION_Z = "extensionZ"
|
|
462
|
-
EXTENSION_JAW = "extensionJaw"
|
|
463
|
-
AXIS_96_CHANNEL_CAM = "axis96ChannelCam"
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
# TODO(mc, 2022-01-18): use opentrons_shared_data.module.types.ModuleModel
|
|
467
|
-
class ModuleModel(str, Enum):
|
|
468
|
-
"""All available modules' models."""
|
|
469
|
-
|
|
470
|
-
TEMPERATURE_MODULE_V1 = "temperatureModuleV1"
|
|
471
|
-
TEMPERATURE_MODULE_V2 = "temperatureModuleV2"
|
|
472
|
-
MAGNETIC_MODULE_V1 = "magneticModuleV1"
|
|
473
|
-
MAGNETIC_MODULE_V2 = "magneticModuleV2"
|
|
474
|
-
THERMOCYCLER_MODULE_V1 = "thermocyclerModuleV1"
|
|
475
|
-
THERMOCYCLER_MODULE_V2 = "thermocyclerModuleV2"
|
|
476
|
-
HEATER_SHAKER_MODULE_V1 = "heaterShakerModuleV1"
|
|
477
|
-
MAGNETIC_BLOCK_V1 = "magneticBlockV1"
|
|
478
|
-
ABSORBANCE_READER_V1 = "absorbanceReaderV1"
|
|
479
|
-
|
|
480
|
-
def as_type(self) -> ModuleType:
|
|
481
|
-
"""Get the ModuleType of this model."""
|
|
482
|
-
if ModuleModel.is_temperature_module_model(self):
|
|
483
|
-
return ModuleType.TEMPERATURE
|
|
484
|
-
elif ModuleModel.is_magnetic_module_model(self):
|
|
485
|
-
return ModuleType.MAGNETIC
|
|
486
|
-
elif ModuleModel.is_thermocycler_module_model(self):
|
|
487
|
-
return ModuleType.THERMOCYCLER
|
|
488
|
-
elif ModuleModel.is_heater_shaker_module_model(self):
|
|
489
|
-
return ModuleType.HEATER_SHAKER
|
|
490
|
-
elif ModuleModel.is_magnetic_block(self):
|
|
491
|
-
return ModuleType.MAGNETIC_BLOCK
|
|
492
|
-
elif ModuleModel.is_absorbance_reader(self):
|
|
493
|
-
return ModuleType.ABSORBANCE_READER
|
|
494
|
-
|
|
495
|
-
assert False, f"Invalid ModuleModel {self}"
|
|
496
|
-
|
|
497
|
-
@classmethod
|
|
498
|
-
def is_temperature_module_model(
|
|
499
|
-
cls, model: ModuleModel
|
|
500
|
-
) -> TypeGuard[TemperatureModuleModel]:
|
|
501
|
-
"""Whether a given model is a Temperature Module."""
|
|
502
|
-
return model in [cls.TEMPERATURE_MODULE_V1, cls.TEMPERATURE_MODULE_V2]
|
|
503
|
-
|
|
504
|
-
@classmethod
|
|
505
|
-
def is_magnetic_module_model(
|
|
506
|
-
cls, model: ModuleModel
|
|
507
|
-
) -> TypeGuard[MagneticModuleModel]:
|
|
508
|
-
"""Whether a given model is a Magnetic Module."""
|
|
509
|
-
return model in [cls.MAGNETIC_MODULE_V1, cls.MAGNETIC_MODULE_V2]
|
|
510
|
-
|
|
511
|
-
@classmethod
|
|
512
|
-
def is_thermocycler_module_model(
|
|
513
|
-
cls, model: ModuleModel
|
|
514
|
-
) -> TypeGuard[ThermocyclerModuleModel]:
|
|
515
|
-
"""Whether a given model is a Thermocycler Module."""
|
|
516
|
-
return model in [cls.THERMOCYCLER_MODULE_V1, cls.THERMOCYCLER_MODULE_V2]
|
|
517
|
-
|
|
518
|
-
@classmethod
|
|
519
|
-
def is_heater_shaker_module_model(
|
|
520
|
-
cls, model: ModuleModel
|
|
521
|
-
) -> TypeGuard[HeaterShakerModuleModel]:
|
|
522
|
-
"""Whether a given model is a Heater-Shaker Module."""
|
|
523
|
-
return model == cls.HEATER_SHAKER_MODULE_V1
|
|
524
|
-
|
|
525
|
-
@classmethod
|
|
526
|
-
def is_magnetic_block(cls, model: ModuleModel) -> TypeGuard[MagneticBlockModel]:
|
|
527
|
-
"""Whether a given model is a Magnetic block."""
|
|
528
|
-
return model == cls.MAGNETIC_BLOCK_V1
|
|
529
|
-
|
|
530
|
-
@classmethod
|
|
531
|
-
def is_absorbance_reader(
|
|
532
|
-
cls, model: ModuleModel
|
|
533
|
-
) -> TypeGuard[AbsorbanceReaderModel]:
|
|
534
|
-
"""Whether a given model is an Absorbance Plate Reader."""
|
|
535
|
-
return model == cls.ABSORBANCE_READER_V1
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
TemperatureModuleModel = Literal[
|
|
539
|
-
ModuleModel.TEMPERATURE_MODULE_V1, ModuleModel.TEMPERATURE_MODULE_V2
|
|
540
|
-
]
|
|
541
|
-
MagneticModuleModel = Literal[
|
|
542
|
-
ModuleModel.MAGNETIC_MODULE_V1, ModuleModel.MAGNETIC_MODULE_V2
|
|
543
|
-
]
|
|
544
|
-
ThermocyclerModuleModel = Literal[
|
|
545
|
-
ModuleModel.THERMOCYCLER_MODULE_V1, ModuleModel.THERMOCYCLER_MODULE_V2
|
|
546
|
-
]
|
|
547
|
-
HeaterShakerModuleModel = Literal[ModuleModel.HEATER_SHAKER_MODULE_V1]
|
|
548
|
-
MagneticBlockModel = Literal[ModuleModel.MAGNETIC_BLOCK_V1]
|
|
549
|
-
AbsorbanceReaderModel = Literal[ModuleModel.ABSORBANCE_READER_V1]
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
class ModuleDimensions(BaseModel):
|
|
553
|
-
"""Dimension type for modules."""
|
|
554
|
-
|
|
555
|
-
bareOverallHeight: float
|
|
556
|
-
overLabwareHeight: float
|
|
557
|
-
lidHeight: Optional[float] = None
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
class Vec3f(BaseModel):
|
|
561
|
-
"""A 3D vector of floats."""
|
|
562
|
-
|
|
563
|
-
x: float
|
|
564
|
-
y: float
|
|
565
|
-
z: float
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
# TODO(mm, 2022-11-07): Deduplicate with Vec3f.
|
|
569
|
-
class ModuleCalibrationPoint(BaseModel):
|
|
570
|
-
"""Calibration Point type for module definition."""
|
|
571
|
-
|
|
572
|
-
x: float
|
|
573
|
-
y: float
|
|
574
|
-
z: float
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
# TODO(mm, 2022-11-07): Deduplicate with Vec3f.
|
|
578
|
-
class LabwareOffsetVector(BaseModel):
|
|
579
|
-
"""Offset, in deck coordinates from nominal to actual position."""
|
|
580
|
-
|
|
581
|
-
x: float
|
|
582
|
-
y: float
|
|
583
|
-
z: float
|
|
584
|
-
|
|
585
|
-
def __add__(self, other: Any) -> LabwareOffsetVector:
|
|
586
|
-
"""Adds two vectors together."""
|
|
587
|
-
if not isinstance(other, LabwareOffsetVector):
|
|
588
|
-
return NotImplemented
|
|
589
|
-
return LabwareOffsetVector(
|
|
590
|
-
x=self.x + other.x, y=self.y + other.y, z=self.z + other.z
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
def __sub__(self, other: Any) -> LabwareOffsetVector:
|
|
594
|
-
"""Subtracts two vectors."""
|
|
595
|
-
if not isinstance(other, LabwareOffsetVector):
|
|
596
|
-
return NotImplemented
|
|
597
|
-
return LabwareOffsetVector(
|
|
598
|
-
x=self.x - other.x, y=self.y - other.y, z=self.z - other.z
|
|
599
|
-
)
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
# TODO(mm, 2022-11-07): Deduplicate with Vec3f.
|
|
603
|
-
class InstrumentOffsetVector(BaseModel):
|
|
604
|
-
"""Instrument Offset from home position to robot deck."""
|
|
605
|
-
|
|
606
|
-
x: float
|
|
607
|
-
y: float
|
|
608
|
-
z: float
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
# TODO(mm, 2022-11-07): Deduplicate with Vec3f.
|
|
612
|
-
class ModuleOffsetVector(BaseModel):
|
|
613
|
-
"""Offset, in deck coordinates, from nominal to actual position of labware on a module."""
|
|
614
|
-
|
|
615
|
-
x: float
|
|
616
|
-
y: float
|
|
617
|
-
z: float
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
@dataclass
|
|
621
|
-
class ModuleOffsetData:
|
|
622
|
-
"""Module calibration offset data."""
|
|
623
|
-
|
|
624
|
-
moduleOffsetVector: ModuleOffsetVector
|
|
625
|
-
location: DeckSlotLocation
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
class OverlapOffset(Vec3f):
|
|
629
|
-
"""Offset representing overlap space of one labware on top of another labware or module."""
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
class AddressableOffsetVector(Vec3f):
|
|
633
|
-
"""Offset, in deck coordinates, from nominal to actual position of an addressable area."""
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
class LabwareMovementOffsetData(BaseModel):
|
|
637
|
-
"""Offsets to be used during labware movement."""
|
|
638
|
-
|
|
639
|
-
pickUpOffset: LabwareOffsetVector
|
|
640
|
-
dropOffset: LabwareOffsetVector
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
# TODO(mm, 2023-04-13): Move to shared-data, so this binding can be maintained alongside the JSON
|
|
644
|
-
# schema that it's sourced from. We already do that for labware definitions and JSON protocols.
|
|
645
|
-
class ModuleDefinition(BaseModel):
|
|
646
|
-
"""A module definition conforming to module definition schema v3."""
|
|
647
|
-
|
|
648
|
-
# Note: This field is misleading.
|
|
649
|
-
#
|
|
650
|
-
# This class only models v3 definitions ("module/schemas/3"), not v2 ("module/schemas/2").
|
|
651
|
-
# labwareOffset is required to have a z-component, for example.
|
|
652
|
-
#
|
|
653
|
-
# When parsing from a schema v3 JSON definition into this model,
|
|
654
|
-
# the definition's `"$otSharedSchema": "module/schemas/3"` field will be thrown away
|
|
655
|
-
# because it has a dollar sign, which doesn't match this field.
|
|
656
|
-
# Then, this field will default to "module/schemas/2", because no value was provided.
|
|
657
|
-
#
|
|
658
|
-
# We should fix this field once Jira RSS-221 is resolved. RSS-221 makes it difficult to fix
|
|
659
|
-
# because robot-server has been storing and loading these bad fields in its database.
|
|
660
|
-
otSharedSchema: str = Field("module/schemas/2", description="The current schema.")
|
|
661
|
-
|
|
662
|
-
moduleType: ModuleType = Field(
|
|
663
|
-
...,
|
|
664
|
-
description="Module type (Temperature/Magnetic/Thermocycler)",
|
|
665
|
-
)
|
|
666
|
-
|
|
667
|
-
model: ModuleModel = Field(..., description="Model name of the module")
|
|
668
|
-
|
|
669
|
-
labwareOffset: LabwareOffsetVector = Field(
|
|
670
|
-
...,
|
|
671
|
-
description="Labware offset in x, y, z.",
|
|
672
|
-
)
|
|
673
|
-
|
|
674
|
-
dimensions: ModuleDimensions = Field(..., description="Module dimension")
|
|
675
|
-
|
|
676
|
-
calibrationPoint: ModuleCalibrationPoint = Field(
|
|
677
|
-
...,
|
|
678
|
-
description="Calibration point of module.",
|
|
679
|
-
)
|
|
680
|
-
|
|
681
|
-
displayName: str = Field(..., description="Display name.")
|
|
682
|
-
|
|
683
|
-
quirks: List[str] = Field(..., description="Module quirks")
|
|
684
|
-
|
|
685
|
-
# In releases prior to https://github.com/Opentrons/opentrons/pull/11873 (v6.3.0),
|
|
686
|
-
# the matrices in slotTransforms were 3x3.
|
|
687
|
-
# After, they are 4x4, even though there was no schema version bump.
|
|
688
|
-
#
|
|
689
|
-
# Because old objects of this class, with the 3x3 matrices, were stored in robot-server's
|
|
690
|
-
# database, this field needs to stay typed loosely enough to support both sizes.
|
|
691
|
-
# We can fix this once Jira RSS-221 is resolved.
|
|
692
|
-
slotTransforms: Dict[str, Any] = Field(
|
|
693
|
-
...,
|
|
694
|
-
description="Dictionary of transforms for each slot.",
|
|
695
|
-
)
|
|
696
|
-
|
|
697
|
-
compatibleWith: List[ModuleModel] = Field(
|
|
698
|
-
...,
|
|
699
|
-
description="List of module models this model is compatible with.",
|
|
700
|
-
)
|
|
701
|
-
gripperOffsets: Optional[Dict[str, LabwareMovementOffsetData]] = Field(
|
|
702
|
-
default_factory=dict,
|
|
703
|
-
description="Offsets to use for labware movement using gripper",
|
|
704
|
-
)
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
class LoadedModule(BaseModel):
|
|
708
|
-
"""A module that has been loaded."""
|
|
709
|
-
|
|
710
|
-
id: str
|
|
711
|
-
model: ModuleModel
|
|
712
|
-
location: Optional[DeckSlotLocation] = None
|
|
713
|
-
serialNumber: Optional[str] = None
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
class LabwareOffsetLocation(BaseModel):
|
|
717
|
-
"""Parameters describing when a given offset may apply to a given labware load."""
|
|
718
|
-
|
|
719
|
-
slotName: DeckSlotName = Field(
|
|
720
|
-
...,
|
|
721
|
-
description=(
|
|
722
|
-
"The deck slot where the protocol will load the labware."
|
|
723
|
-
" Or, if the protocol will load the labware on a module,"
|
|
724
|
-
" the deck slot where the protocol will load that module."
|
|
725
|
-
"\n\n"
|
|
726
|
-
# This description should be kept in sync with DeckSlotLocation.slotName.
|
|
727
|
-
'The plain numbers like `"5"` are for the OT-2,'
|
|
728
|
-
' and the coordinates like `"C2"` are for the Flex.'
|
|
729
|
-
"\n\n"
|
|
730
|
-
"When you provide one of these values, you can use either style."
|
|
731
|
-
" It will automatically be converted to match the robot."
|
|
732
|
-
"\n\n"
|
|
733
|
-
"When one of these values is returned, it will always match the robot."
|
|
734
|
-
),
|
|
735
|
-
)
|
|
736
|
-
moduleModel: Optional[ModuleModel] = Field(
|
|
737
|
-
None,
|
|
738
|
-
description=(
|
|
739
|
-
"The model of the module that the labware will be loaded onto,"
|
|
740
|
-
" if applicable."
|
|
741
|
-
"\n\n"
|
|
742
|
-
"Because of module compatibility, the model that the protocol requests"
|
|
743
|
-
" may not be exactly the same"
|
|
744
|
-
" as what it will find physically connected during execution."
|
|
745
|
-
" For this labware offset to apply,"
|
|
746
|
-
" this field must be the *requested* model, not the connected one."
|
|
747
|
-
" You can retrieve this from a `loadModule` command's `params.model`"
|
|
748
|
-
" in the protocol's analysis."
|
|
749
|
-
),
|
|
750
|
-
)
|
|
751
|
-
definitionUri: Optional[str] = Field(
|
|
752
|
-
None,
|
|
753
|
-
description=(
|
|
754
|
-
"The definition URI of a labware that a labware can be loaded onto,"
|
|
755
|
-
" if applicable."
|
|
756
|
-
"\n\n"
|
|
757
|
-
"This can be combined with moduleModel if the labware is loaded on top of"
|
|
758
|
-
" an adapter that is loaded on a module."
|
|
759
|
-
),
|
|
760
|
-
)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
class LabwareOffset(BaseModel):
|
|
764
|
-
"""An offset that the robot adds to a pipette's position when it moves to a labware.
|
|
765
|
-
|
|
766
|
-
During the run, if a labware is loaded whose definition URI and location
|
|
767
|
-
both match what's found here, the given offset will be added to all
|
|
768
|
-
pipette movements that use that labware as a reference point.
|
|
769
|
-
"""
|
|
770
|
-
|
|
771
|
-
id: str = Field(..., description="Unique labware offset record identifier.")
|
|
772
|
-
createdAt: datetime = Field(..., description="When this labware offset was added.")
|
|
773
|
-
definitionUri: str = Field(..., description="The URI for the labware's definition.")
|
|
774
|
-
location: LabwareOffsetLocation = Field(
|
|
775
|
-
...,
|
|
776
|
-
description="Where the labware is located on the robot.",
|
|
777
|
-
)
|
|
778
|
-
vector: LabwareOffsetVector = Field(
|
|
779
|
-
...,
|
|
780
|
-
description="The offset applied to matching labware.",
|
|
781
|
-
)
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
class LabwareOffsetCreate(BaseModel):
|
|
785
|
-
"""Create request data for a labware offset."""
|
|
786
|
-
|
|
787
|
-
definitionUri: str = Field(..., description="The URI for the labware's definition.")
|
|
788
|
-
location: LabwareOffsetLocation = Field(
|
|
789
|
-
...,
|
|
790
|
-
description="Where the labware is located on the robot.",
|
|
791
|
-
)
|
|
792
|
-
vector: LabwareOffsetVector = Field(
|
|
793
|
-
...,
|
|
794
|
-
description="The offset applied to matching labware.",
|
|
795
|
-
)
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
class LoadedLabware(BaseModel):
|
|
799
|
-
"""A labware that has been loaded."""
|
|
800
|
-
|
|
801
|
-
id: str
|
|
802
|
-
loadName: str
|
|
803
|
-
definitionUri: str
|
|
804
|
-
location: LabwareLocation = Field(
|
|
805
|
-
..., description="The labware's current location."
|
|
806
|
-
)
|
|
807
|
-
lid_id: Optional[str] = Field(
|
|
808
|
-
None,
|
|
809
|
-
description=("Labware ID of a Lid currently loaded on top of the labware."),
|
|
810
|
-
)
|
|
811
|
-
offsetId: Optional[str] = Field(
|
|
812
|
-
None,
|
|
813
|
-
description=(
|
|
814
|
-
"An ID referencing the labware offset"
|
|
815
|
-
" that applies to this labware placement."
|
|
816
|
-
" Null or undefined means no offset was provided for this load,"
|
|
817
|
-
" so the default of (0, 0, 0) will be used."
|
|
818
|
-
),
|
|
819
|
-
)
|
|
820
|
-
displayName: Optional[str] = Field(
|
|
821
|
-
None,
|
|
822
|
-
description="A user-specified display name for this labware, if provided.",
|
|
823
|
-
)
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
class HexColor(RootModel[str]):
|
|
827
|
-
"""Hex color representation."""
|
|
828
|
-
|
|
829
|
-
root: str = Field(pattern=r"^#(?:[0-9a-fA-F]{3,4}){1,2}$")
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
EmptyLiquidId = Literal["EMPTY"]
|
|
833
|
-
LiquidId = str | EmptyLiquidId
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
class Liquid(BaseModel):
|
|
837
|
-
"""Payload required to create a liquid."""
|
|
838
|
-
|
|
839
|
-
id: str
|
|
840
|
-
displayName: str
|
|
841
|
-
description: str
|
|
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
|
-
)
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
class SpeedRange(NamedTuple):
|
|
898
|
-
"""Minimum and maximum allowed speeds for a shaking module."""
|
|
899
|
-
|
|
900
|
-
min: int
|
|
901
|
-
max: int
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
class TemperatureRange(NamedTuple):
|
|
905
|
-
"""Minimum and maximum allowed temperatures for a heating module."""
|
|
906
|
-
|
|
907
|
-
min: float
|
|
908
|
-
max: float
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
class HeaterShakerLatchStatus(Enum):
|
|
912
|
-
"""Heater-Shaker latch status for determining pipette and labware movement errors."""
|
|
913
|
-
|
|
914
|
-
CLOSED = "closed"
|
|
915
|
-
OPEN = "open"
|
|
916
|
-
UNKNOWN = "unknown"
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
@dataclass(frozen=True)
|
|
920
|
-
class HeaterShakerMovementRestrictors:
|
|
921
|
-
"""Shaking status, latch status and slot location for determining movement restrictions."""
|
|
922
|
-
|
|
923
|
-
plate_shaking: bool
|
|
924
|
-
latch_status: HeaterShakerLatchStatus
|
|
925
|
-
deck_slot: int
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
class LabwareMovementStrategy(str, Enum):
|
|
929
|
-
"""Strategy to use for labware movement."""
|
|
930
|
-
|
|
931
|
-
USING_GRIPPER = "usingGripper"
|
|
932
|
-
MANUAL_MOVE_WITH_PAUSE = "manualMoveWithPause"
|
|
933
|
-
MANUAL_MOVE_WITHOUT_PAUSE = "manualMoveWithoutPause"
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
@dataclass(frozen=True)
|
|
937
|
-
class PotentialCutoutFixture:
|
|
938
|
-
"""Cutout and cutout fixture id associated with a potential cutout fixture that can be on the deck."""
|
|
939
|
-
|
|
940
|
-
cutout_id: str
|
|
941
|
-
cutout_fixture_id: str
|
|
942
|
-
provided_addressable_areas: FrozenSet[str]
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
class AreaType(Enum):
|
|
946
|
-
"""The type of addressable area."""
|
|
947
|
-
|
|
948
|
-
SLOT = "slot"
|
|
949
|
-
STAGING_SLOT = "stagingSlot"
|
|
950
|
-
MOVABLE_TRASH = "movableTrash"
|
|
951
|
-
FIXED_TRASH = "fixedTrash"
|
|
952
|
-
WASTE_CHUTE = "wasteChute"
|
|
953
|
-
THERMOCYCLER = "thermocycler"
|
|
954
|
-
HEATER_SHAKER = "heaterShaker"
|
|
955
|
-
TEMPERATURE = "temperatureModule"
|
|
956
|
-
MAGNETICBLOCK = "magneticBlock"
|
|
957
|
-
ABSORBANCE_READER = "absorbanceReader"
|
|
958
|
-
LID_DOCK = "lidDock"
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
@dataclass(frozen=True)
|
|
962
|
-
class AddressableArea:
|
|
963
|
-
"""Addressable area that has been loaded."""
|
|
964
|
-
|
|
965
|
-
area_name: str
|
|
966
|
-
area_type: AreaType
|
|
967
|
-
base_slot: DeckSlotName
|
|
968
|
-
display_name: str
|
|
969
|
-
bounding_box: Dimensions
|
|
970
|
-
position: AddressableOffsetVector
|
|
971
|
-
compatible_module_types: List[SharedDataModuleType]
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
class PostRunHardwareState(Enum):
|
|
975
|
-
"""State of robot gantry & motors after a stop is performed and the hardware API is reset.
|
|
976
|
-
|
|
977
|
-
HOME_AND_STAY_ENGAGED: home the gantry and keep all motors engaged. This allows the
|
|
978
|
-
robot to continue performing movement actions without re-homing
|
|
979
|
-
HOME_THEN_DISENGAGE: home the gantry and then disengage motors.
|
|
980
|
-
Reduces current consumption of the motors and prevents coil heating.
|
|
981
|
-
Re-homing is required to re-engage the motors and resume robot movement.
|
|
982
|
-
STAY_ENGAGED_IN_PLACE: do not home after the stop and keep the motors engaged.
|
|
983
|
-
Keeps gantry in the same position as prior to `stop()` execution
|
|
984
|
-
and allows the robot to execute movement commands without requiring to re-home first.
|
|
985
|
-
DISENGAGE_IN_PLACE: disengage motors and do not home the robot
|
|
986
|
-
Probable states for pipette:
|
|
987
|
-
- for 1- or 8-channel:
|
|
988
|
-
- HOME_AND_STAY_ENGAGED after protocol runs
|
|
989
|
-
- STAY_ENGAGED_IN_PLACE after maintenance runs
|
|
990
|
-
- for 96-channel:
|
|
991
|
-
- HOME_THEN_DISENGAGE after protocol runs
|
|
992
|
-
- DISENGAGE_IN_PLACE after maintenance runs
|
|
993
|
-
"""
|
|
994
|
-
|
|
995
|
-
HOME_AND_STAY_ENGAGED = "homeAndStayEngaged"
|
|
996
|
-
HOME_THEN_DISENGAGE = "homeThenDisengage"
|
|
997
|
-
STAY_ENGAGED_IN_PLACE = "stayEngagedInPlace"
|
|
998
|
-
DISENGAGE_IN_PLACE = "disengageInPlace"
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
NOZZLE_NAME_REGEX = r"[A-Z]\d{1,2}"
|
|
1002
|
-
PRIMARY_NOZZLE_LITERAL = Literal["A1", "H1", "A12", "H12"]
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
class AllNozzleLayoutConfiguration(BaseModel):
|
|
1006
|
-
"""All basemodel to represent a reset to the nozzle configuration. Sending no parameters resets to default."""
|
|
1007
|
-
|
|
1008
|
-
style: Literal["ALL"] = "ALL"
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
class SingleNozzleLayoutConfiguration(BaseModel):
|
|
1012
|
-
"""Minimum information required for a new nozzle configuration."""
|
|
1013
|
-
|
|
1014
|
-
style: Literal["SINGLE"] = "SINGLE"
|
|
1015
|
-
primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
|
|
1016
|
-
...,
|
|
1017
|
-
description="The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.",
|
|
1018
|
-
)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
class RowNozzleLayoutConfiguration(BaseModel):
|
|
1022
|
-
"""Minimum information required for a new nozzle configuration."""
|
|
1023
|
-
|
|
1024
|
-
style: Literal["ROW"] = "ROW"
|
|
1025
|
-
primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
|
|
1026
|
-
...,
|
|
1027
|
-
description="The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.",
|
|
1028
|
-
)
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
class ColumnNozzleLayoutConfiguration(BaseModel):
|
|
1032
|
-
"""Information required for nozzle configurations of type ROW and COLUMN."""
|
|
1033
|
-
|
|
1034
|
-
style: Literal["COLUMN"] = "COLUMN"
|
|
1035
|
-
primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
|
|
1036
|
-
...,
|
|
1037
|
-
description="The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.",
|
|
1038
|
-
)
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
class QuadrantNozzleLayoutConfiguration(BaseModel):
|
|
1042
|
-
"""Information required for nozzle configurations of type QUADRANT."""
|
|
1043
|
-
|
|
1044
|
-
style: Literal["QUADRANT"] = "QUADRANT"
|
|
1045
|
-
primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
|
|
1046
|
-
...,
|
|
1047
|
-
description="The primary nozzle to use in the layout configuration. This nozzle will update the critical point of the current pipette. For now, this is also the back left corner of your rectangle.",
|
|
1048
|
-
)
|
|
1049
|
-
frontRightNozzle: str = Field(
|
|
1050
|
-
...,
|
|
1051
|
-
pattern=NOZZLE_NAME_REGEX,
|
|
1052
|
-
description="The front right nozzle in your configuration.",
|
|
1053
|
-
)
|
|
1054
|
-
backLeftNozzle: str = Field(
|
|
1055
|
-
...,
|
|
1056
|
-
pattern=NOZZLE_NAME_REGEX,
|
|
1057
|
-
description="The back left nozzle in your configuration.",
|
|
1058
|
-
)
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
NozzleLayoutConfigurationType = Union[
|
|
1062
|
-
AllNozzleLayoutConfiguration,
|
|
1063
|
-
SingleNozzleLayoutConfiguration,
|
|
1064
|
-
ColumnNozzleLayoutConfiguration,
|
|
1065
|
-
RowNozzleLayoutConfiguration,
|
|
1066
|
-
QuadrantNozzleLayoutConfiguration,
|
|
1067
|
-
]
|
|
1068
|
-
|
|
1069
|
-
# TODO make the below some sort of better type
|
|
1070
|
-
# TODO This should instead contain a proper cutout fixture type
|
|
1071
|
-
DeckConfigurationType = List[
|
|
1072
|
-
Tuple[str, str, Optional[str]]
|
|
1073
|
-
] # cutout_id, cutout_fixture_id, opentrons_module_serial_number
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
class InstrumentSensorId(str, Enum):
|
|
1077
|
-
"""Primary and secondary sensor ids."""
|
|
1078
|
-
|
|
1079
|
-
PRIMARY = "primary"
|
|
1080
|
-
SECONDARY = "secondary"
|
|
1081
|
-
BOTH = "both"
|
|
1082
|
-
|
|
1083
|
-
def to_instrument_probe_type(self) -> InstrumentProbeType:
|
|
1084
|
-
"""Convert to InstrumentProbeType."""
|
|
1085
|
-
return {
|
|
1086
|
-
InstrumentSensorId.PRIMARY: InstrumentProbeType.PRIMARY,
|
|
1087
|
-
InstrumentSensorId.SECONDARY: InstrumentProbeType.SECONDARY,
|
|
1088
|
-
InstrumentSensorId.BOTH: InstrumentProbeType.BOTH,
|
|
1089
|
-
}[self]
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
class TipPresenceStatus(str, Enum):
|
|
1093
|
-
"""Tip presence status reported by a pipette."""
|
|
1094
|
-
|
|
1095
|
-
PRESENT = "present"
|
|
1096
|
-
ABSENT = "absent"
|
|
1097
|
-
UNKNOWN = "unknown"
|
|
1098
|
-
|
|
1099
|
-
def to_hw_state(self) -> HwTipStateType:
|
|
1100
|
-
"""Convert to hardware tip state."""
|
|
1101
|
-
assert self != TipPresenceStatus.UNKNOWN
|
|
1102
|
-
return {
|
|
1103
|
-
TipPresenceStatus.PRESENT: HwTipStateType.PRESENT,
|
|
1104
|
-
TipPresenceStatus.ABSENT: HwTipStateType.ABSENT,
|
|
1105
|
-
}[self]
|
|
1106
|
-
|
|
1107
|
-
@classmethod
|
|
1108
|
-
def from_hw_state(cls, state: HwTipStateType) -> "TipPresenceStatus":
|
|
1109
|
-
"""Convert from hardware tip state."""
|
|
1110
|
-
return {
|
|
1111
|
-
HwTipStateType.PRESENT: TipPresenceStatus.PRESENT,
|
|
1112
|
-
HwTipStateType.ABSENT: TipPresenceStatus.ABSENT,
|
|
1113
|
-
}[state]
|
|
1114
|
-
|
|
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
|
-
|
|
1192
|
-
# TODO (spp, 2024-04-02): move all RTP types to runner
|
|
1193
|
-
class RTPBase(BaseModel):
|
|
1194
|
-
"""Parameters defined in a protocol."""
|
|
1195
|
-
|
|
1196
|
-
displayName: StrictStr = Field(..., description="Display string for the parameter.")
|
|
1197
|
-
variableName: StrictStr = Field(
|
|
1198
|
-
..., description="Python variable name of the parameter."
|
|
1199
|
-
)
|
|
1200
|
-
description: Optional[StrictStr] = Field(
|
|
1201
|
-
None, description="Detailed description of the parameter."
|
|
1202
|
-
)
|
|
1203
|
-
suffix: Optional[StrictStr] = Field(
|
|
1204
|
-
None,
|
|
1205
|
-
description="Units (like mL, mm/sec, etc) or a custom suffix for the parameter.",
|
|
1206
|
-
)
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
class NumberParameter(RTPBase):
|
|
1210
|
-
"""An integer parameter defined in a protocol."""
|
|
1211
|
-
|
|
1212
|
-
type: Literal["int", "float"] = Field(
|
|
1213
|
-
..., description="String specifying whether the number is an int or float type."
|
|
1214
|
-
)
|
|
1215
|
-
min: Union[StrictInt, StrictFloat] = Field(
|
|
1216
|
-
..., description="Minimum value that the number param is allowed to have."
|
|
1217
|
-
)
|
|
1218
|
-
max: Union[StrictInt, StrictFloat] = Field(
|
|
1219
|
-
..., description="Maximum value that the number param is allowed to have."
|
|
1220
|
-
)
|
|
1221
|
-
value: Union[StrictInt, StrictFloat] = Field(
|
|
1222
|
-
...,
|
|
1223
|
-
description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
|
|
1224
|
-
)
|
|
1225
|
-
default: Union[StrictInt, StrictFloat] = Field(
|
|
1226
|
-
...,
|
|
1227
|
-
description="Default value of the parameter, to be used when there is no client-specified value.",
|
|
1228
|
-
)
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
class BooleanParameter(RTPBase):
|
|
1232
|
-
"""A boolean parameter defined in a protocol."""
|
|
1233
|
-
|
|
1234
|
-
type: Literal["bool"] = Field(
|
|
1235
|
-
default="bool", description="String specifying the type of this parameter"
|
|
1236
|
-
)
|
|
1237
|
-
value: StrictBool = Field(
|
|
1238
|
-
...,
|
|
1239
|
-
description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
|
|
1240
|
-
)
|
|
1241
|
-
default: StrictBool = Field(
|
|
1242
|
-
...,
|
|
1243
|
-
description="Default value of the parameter, to be used when there is no client-specified value.",
|
|
1244
|
-
)
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
class EnumChoice(BaseModel):
|
|
1248
|
-
"""Components of choices used in RTP Enum Parameters."""
|
|
1249
|
-
|
|
1250
|
-
displayName: StrictStr = Field(
|
|
1251
|
-
..., description="Display string for the param's choice."
|
|
1252
|
-
)
|
|
1253
|
-
value: Union[StrictInt, StrictFloat, StrictStr] = Field(
|
|
1254
|
-
..., description="Enum value of the param's choice."
|
|
1255
|
-
)
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
class EnumParameter(RTPBase):
|
|
1259
|
-
"""A string enum defined in a protocol."""
|
|
1260
|
-
|
|
1261
|
-
type: Literal["int", "float", "str"] = Field(
|
|
1262
|
-
...,
|
|
1263
|
-
description="String specifying whether the parameter is an int or float or string type.",
|
|
1264
|
-
)
|
|
1265
|
-
choices: List[EnumChoice] = Field(
|
|
1266
|
-
..., description="List of valid choices for this parameter."
|
|
1267
|
-
)
|
|
1268
|
-
value: Union[StrictInt, StrictFloat, StrictStr] = Field(
|
|
1269
|
-
...,
|
|
1270
|
-
description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
|
|
1271
|
-
)
|
|
1272
|
-
default: Union[StrictInt, StrictFloat, StrictStr] = Field(
|
|
1273
|
-
...,
|
|
1274
|
-
description="Default value of the parameter, to be used when there is no client-specified value.",
|
|
1275
|
-
)
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
class FileInfo(BaseModel):
|
|
1279
|
-
"""A file UUID descriptor."""
|
|
1280
|
-
|
|
1281
|
-
id: str = Field(
|
|
1282
|
-
...,
|
|
1283
|
-
description="The UUID identifier of the file stored on the robot.",
|
|
1284
|
-
)
|
|
1285
|
-
name: str = Field(..., description="Name of the file, including the extension.")
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
class CSVParameter(RTPBase):
|
|
1289
|
-
"""A CSV file parameter defined in a protocol."""
|
|
1290
|
-
|
|
1291
|
-
type: Literal["csv_file"] = Field(
|
|
1292
|
-
default="csv_file", description="String specifying the type of this parameter"
|
|
1293
|
-
)
|
|
1294
|
-
file: Optional[FileInfo] = Field(
|
|
1295
|
-
default=None,
|
|
1296
|
-
description="ID of the CSV file stored on the robot; to be used for fetching the CSV file."
|
|
1297
|
-
" For local analysis this will most likely be empty.",
|
|
1298
|
-
)
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
RunTimeParameter = Union[NumberParameter, EnumParameter, BooleanParameter, CSVParameter]
|
|
1302
|
-
|
|
1303
|
-
PrimitiveRunTimeParamValuesType = Mapping[
|
|
1304
|
-
StrictStr, Union[StrictInt, StrictFloat, StrictBool, StrictStr]
|
|
1305
|
-
] # update value types as more RTP types are added
|
|
1306
|
-
|
|
1307
|
-
CSVRunTimeParamFilesType = Mapping[StrictStr, StrictStr]
|
|
1308
|
-
CSVRuntimeParamPaths = Dict[str, Path]
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
ABSMeasureMode = Literal["single", "multi"]
|