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.
Files changed (192) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +19 -3
  39. opentrons/legacy_commands/helpers.py +15 -0
  40. opentrons/legacy_commands/types.py +3 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1233 -65
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/protocol.py +253 -11
  52. opentrons/protocol_api/core/engine/stringify.py +19 -8
  53. opentrons/protocol_api/core/engine/transfer_components_executor.py +853 -0
  54. opentrons/protocol_api/core/engine/well.py +60 -5
  55. opentrons/protocol_api/core/instrument.py +65 -19
  56. opentrons/protocol_api/core/labware.py +6 -2
  57. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  58. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +69 -21
  59. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  60. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  61. opentrons/protocol_api/core/legacy/legacy_well_core.py +25 -1
  62. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  63. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  64. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  65. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +67 -21
  66. opentrons/protocol_api/core/module.py +43 -0
  67. opentrons/protocol_api/core/protocol.py +33 -0
  68. opentrons/protocol_api/core/well.py +21 -1
  69. opentrons/protocol_api/instrument_context.py +246 -123
  70. opentrons/protocol_api/labware.py +75 -11
  71. opentrons/protocol_api/module_contexts.py +140 -0
  72. opentrons/protocol_api/protocol_context.py +156 -16
  73. opentrons/protocol_api/validation.py +51 -41
  74. opentrons/protocol_engine/__init__.py +21 -2
  75. opentrons/protocol_engine/actions/actions.py +5 -5
  76. opentrons/protocol_engine/clients/sync_client.py +6 -0
  77. opentrons/protocol_engine/commands/__init__.py +30 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  79. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  80. opentrons/protocol_engine/commands/aspirate.py +6 -2
  81. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  82. opentrons/protocol_engine/commands/aspirate_while_tracking.py +237 -0
  83. opentrons/protocol_engine/commands/blow_out.py +2 -0
  84. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  85. opentrons/protocol_engine/commands/command_unions.py +69 -0
  86. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  87. opentrons/protocol_engine/commands/dispense.py +3 -1
  88. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  89. opentrons/protocol_engine/commands/dispense_while_tracking.py +240 -0
  90. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  91. opentrons/protocol_engine/commands/evotip_dispense.py +6 -7
  92. opentrons/protocol_engine/commands/evotip_seal_pipette.py +24 -29
  93. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +1 -7
  94. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  95. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  96. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  97. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  98. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  99. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  100. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  101. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  102. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  103. opentrons/protocol_engine/commands/flex_stacker/store.py +288 -0
  104. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  105. opentrons/protocol_engine/commands/labware_handling_common.py +24 -0
  106. opentrons/protocol_engine/commands/liquid_probe.py +21 -12
  107. opentrons/protocol_engine/commands/load_labware.py +42 -39
  108. opentrons/protocol_engine/commands/load_lid.py +21 -13
  109. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  110. opentrons/protocol_engine/commands/load_module.py +18 -17
  111. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  112. opentrons/protocol_engine/commands/move_labware.py +139 -20
  113. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  114. opentrons/protocol_engine/commands/pipetting_common.py +154 -7
  115. opentrons/protocol_engine/commands/prepare_to_aspirate.py +17 -2
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  118. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  119. opentrons/protocol_engine/errors/__init__.py +8 -0
  120. opentrons/protocol_engine/errors/exceptions.py +50 -0
  121. opentrons/protocol_engine/execution/equipment.py +123 -106
  122. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  123. opentrons/protocol_engine/execution/pipetting.py +233 -26
  124. opentrons/protocol_engine/execution/tip_handler.py +14 -5
  125. opentrons/protocol_engine/labware_offset_standardization.py +173 -0
  126. opentrons/protocol_engine/protocol_engine.py +22 -13
  127. opentrons/protocol_engine/resources/deck_configuration_provider.py +94 -2
  128. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  129. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  130. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  131. opentrons/protocol_engine/slot_standardization.py +11 -23
  132. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  133. opentrons/protocol_engine/state/frustum_helpers.py +26 -10
  134. opentrons/protocol_engine/state/geometry.py +683 -100
  135. opentrons/protocol_engine/state/labware.py +252 -55
  136. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  137. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  138. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  139. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  140. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  141. opentrons/protocol_engine/state/modules.py +178 -52
  142. opentrons/protocol_engine/state/pipettes.py +54 -0
  143. opentrons/protocol_engine/state/state.py +1 -1
  144. opentrons/protocol_engine/state/tips.py +14 -0
  145. opentrons/protocol_engine/state/update_types.py +180 -25
  146. opentrons/protocol_engine/state/wells.py +54 -8
  147. opentrons/protocol_engine/types/__init__.py +292 -0
  148. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  149. opentrons/protocol_engine/types/command_annotations.py +53 -0
  150. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  151. opentrons/protocol_engine/types/execution.py +96 -0
  152. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  153. opentrons/protocol_engine/types/instrument.py +47 -0
  154. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  155. opentrons/protocol_engine/types/labware.py +110 -0
  156. opentrons/protocol_engine/types/labware_movement.py +22 -0
  157. opentrons/protocol_engine/types/labware_offset_location.py +108 -0
  158. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  159. opentrons/protocol_engine/types/liquid.py +40 -0
  160. opentrons/protocol_engine/types/liquid_class.py +59 -0
  161. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  162. opentrons/protocol_engine/types/liquid_level_detection.py +137 -0
  163. opentrons/protocol_engine/types/location.py +193 -0
  164. opentrons/protocol_engine/types/module.py +269 -0
  165. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  166. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  167. opentrons/protocol_engine/types/tip.py +18 -0
  168. opentrons/protocol_engine/types/util.py +21 -0
  169. opentrons/protocol_engine/types/well_position.py +107 -0
  170. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  171. opentrons/protocol_reader/file_format_validator.py +5 -3
  172. opentrons/protocol_runner/json_translator.py +4 -2
  173. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  174. opentrons/protocol_runner/run_orchestrator.py +4 -1
  175. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  176. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  177. opentrons/protocols/api_support/definitions.py +1 -1
  178. opentrons/protocols/api_support/instrument.py +16 -3
  179. opentrons/protocols/labware.py +5 -6
  180. opentrons/protocols/models/__init__.py +0 -21
  181. opentrons/simulate.py +4 -2
  182. opentrons/types.py +15 -6
  183. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/METADATA +4 -4
  184. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/RECORD +188 -148
  185. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  186. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  187. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  188. opentrons/protocol_engine/types.py +0 -1311
  189. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/LICENSE +0 -0
  190. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/WHEEL +0 -0
  191. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/entry_points.txt +0 -0
  192. {opentrons-8.3.1a1.dist-info → opentrons-8.4.0a1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,193 @@
1
+ """Protocol engine types to deal with locating things on the deck."""
2
+
3
+ from __future__ import annotations
4
+ from typing import Literal, Union, TypeGuard
5
+
6
+ from pydantic import BaseModel, Field
7
+
8
+ from opentrons.types import DeckSlotName, StagingSlotName
9
+
10
+
11
+ class DeckSlotLocation(BaseModel):
12
+ """The location of something placed in a single deck slot."""
13
+
14
+ slotName: DeckSlotName = Field(
15
+ ...,
16
+ description=(
17
+ # This description should be kept in sync with LegacyLabwareOffsetLocation.slotName.
18
+ "A slot on the robot's deck."
19
+ "\n\n"
20
+ 'The plain numbers like `"5"` are for the OT-2,'
21
+ ' and the coordinates like `"C2"` are for the Flex.'
22
+ "\n\n"
23
+ "When you provide one of these values, you can use either style."
24
+ " It will automatically be converted to match the robot."
25
+ "\n\n"
26
+ "When one of these values is returned, it will always match the robot."
27
+ ),
28
+ )
29
+
30
+
31
+ class StagingSlotLocation(BaseModel):
32
+ """The location of something placed in a single staging slot."""
33
+
34
+ slotName: StagingSlotName = Field(
35
+ ...,
36
+ description=(
37
+ # This description should be kept in sync with LegacyLabwareOffsetLocation.slotName.
38
+ "A slot on the robot's staging area."
39
+ "\n\n"
40
+ "These apply only to the Flex. The OT-2 has no staging slots."
41
+ ),
42
+ )
43
+
44
+
45
+ class AddressableAreaLocation(BaseModel):
46
+ """The location of something place in an addressable area. This is a superset of deck slots."""
47
+
48
+ addressableAreaName: str = Field(
49
+ ...,
50
+ description=(
51
+ "The name of the addressable area that you want to use."
52
+ " Valid values are the `id`s of `addressableArea`s in the"
53
+ " [deck definition](https://github.com/Opentrons/opentrons/tree/edge/shared-data/deck)."
54
+ ),
55
+ )
56
+
57
+
58
+ class ModuleLocation(BaseModel):
59
+ """The location of something placed atop a hardware module."""
60
+
61
+ moduleId: str = Field(
62
+ ...,
63
+ description="The ID of a loaded module from a prior `loadModule` command.",
64
+ )
65
+
66
+
67
+ class OnLabwareLocation(BaseModel):
68
+ """The location of something placed atop another labware."""
69
+
70
+ labwareId: str = Field(
71
+ ...,
72
+ description="The ID of a loaded Labware from a prior `loadLabware` command.",
73
+ )
74
+
75
+
76
+ class InStackerHopperLocation(BaseModel):
77
+ """The location of a labware in a stacker hopper."""
78
+
79
+ kind: Literal["inStackerHopper"] = "inStackerHopper"
80
+ moduleId: str = Field(
81
+ ..., description="The ID of the stacker in which this labware is."
82
+ )
83
+
84
+
85
+ _OffDeckLocationType = Literal["offDeck"]
86
+ _SystemLocationType = Literal["systemLocation"]
87
+ OFF_DECK_LOCATION: _OffDeckLocationType = "offDeck"
88
+ SYSTEM_LOCATION: _SystemLocationType = "systemLocation"
89
+
90
+
91
+ def labware_location_is_off_deck(
92
+ location: LabwareLocation,
93
+ ) -> TypeGuard[_OffDeckLocationType]:
94
+ """Check if a location is an off deck location."""
95
+ return isinstance(location, str) and location == OFF_DECK_LOCATION
96
+
97
+
98
+ def labware_location_is_system(
99
+ location: LabwareLocation,
100
+ ) -> TypeGuard[_SystemLocationType]:
101
+ """Check if a location is the system location."""
102
+ return isinstance(location, str) and location == SYSTEM_LOCATION
103
+
104
+
105
+ class OnLabwareLocationSequenceComponent(BaseModel):
106
+ """Labware on another labware."""
107
+
108
+ kind: Literal["onLabware"] = "onLabware"
109
+ labwareId: str
110
+ lidId: str | None
111
+
112
+
113
+ class OnModuleLocationSequenceComponent(BaseModel):
114
+ """Labware on a module."""
115
+
116
+ kind: Literal["onModule"] = "onModule"
117
+ moduleId: str
118
+
119
+
120
+ class OnAddressableAreaLocationSequenceComponent(BaseModel):
121
+ """Labware on an addressable area."""
122
+
123
+ kind: Literal["onAddressableArea"] = "onAddressableArea"
124
+ addressableAreaName: str
125
+
126
+
127
+ class OnCutoutFixtureLocationSequenceComponent(BaseModel):
128
+ """Something on a deck cutout fixture."""
129
+
130
+ kind: Literal["onCutoutFixture"] = "onCutoutFixture"
131
+ possibleCutoutFixtureIds: list[str]
132
+ cutoutId: str
133
+
134
+
135
+ class NotOnDeckLocationSequenceComponent(BaseModel):
136
+ """Labware on a system location."""
137
+
138
+ kind: Literal["notOnDeck"] = "notOnDeck"
139
+ logicalLocationName: _OffDeckLocationType | _SystemLocationType
140
+
141
+
142
+ LabwareLocationSequence = list[
143
+ OnLabwareLocationSequenceComponent
144
+ | OnModuleLocationSequenceComponent
145
+ | OnAddressableAreaLocationSequenceComponent
146
+ | NotOnDeckLocationSequenceComponent
147
+ | OnCutoutFixtureLocationSequenceComponent
148
+ | InStackerHopperLocation
149
+ ]
150
+ """Labware location specifier."""
151
+
152
+ LabwareLocation = Union[
153
+ DeckSlotLocation,
154
+ ModuleLocation,
155
+ OnLabwareLocation,
156
+ _OffDeckLocationType,
157
+ _SystemLocationType,
158
+ AddressableAreaLocation,
159
+ InStackerHopperLocation,
160
+ ]
161
+ """Union of all locations where it's legal to keep a labware."""
162
+
163
+ LoadableLabwareLocation = Union[
164
+ DeckSlotLocation,
165
+ ModuleLocation,
166
+ OnLabwareLocation,
167
+ _OffDeckLocationType,
168
+ _SystemLocationType,
169
+ AddressableAreaLocation,
170
+ ]
171
+ """Union of all locations where it's legal to load a labware."""
172
+
173
+ OnDeckLabwareLocation = Union[
174
+ DeckSlotLocation, ModuleLocation, OnLabwareLocation, AddressableAreaLocation
175
+ ]
176
+
177
+ NonStackedLocation = Union[
178
+ DeckSlotLocation,
179
+ AddressableAreaLocation,
180
+ ModuleLocation,
181
+ _OffDeckLocationType,
182
+ _SystemLocationType,
183
+ ]
184
+ """Union of all locations where it's legal to keep a labware that can't be stacked on another labware"""
185
+
186
+
187
+ # TODO(mm, 2022-11-07): Deduplicate with Vec3f.
188
+ class DeckPoint(BaseModel):
189
+ """Coordinates of a point in deck space."""
190
+
191
+ x: float
192
+ y: float
193
+ z: float
@@ -0,0 +1,269 @@
1
+ """Protocol engine types to do with modules."""
2
+
3
+ from __future__ import annotations
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from typing import (
7
+ TypeGuard,
8
+ Literal,
9
+ Optional,
10
+ List,
11
+ Dict,
12
+ Any,
13
+ NamedTuple,
14
+ )
15
+
16
+ from pydantic import BaseModel, Field
17
+
18
+ from opentrons.hardware_control.modules import (
19
+ ModuleType as ModuleType,
20
+ )
21
+
22
+ from .location import DeckSlotLocation
23
+ from .labware_offset_vector import LabwareOffsetVector
24
+ from .labware_movement import LabwareMovementOffsetData
25
+
26
+
27
+ # TODO(mc, 2022-01-18): use opentrons_shared_data.module.types.ModuleModel
28
+ class ModuleModel(str, Enum):
29
+ """All available modules' models."""
30
+
31
+ TEMPERATURE_MODULE_V1 = "temperatureModuleV1"
32
+ TEMPERATURE_MODULE_V2 = "temperatureModuleV2"
33
+ MAGNETIC_MODULE_V1 = "magneticModuleV1"
34
+ MAGNETIC_MODULE_V2 = "magneticModuleV2"
35
+ THERMOCYCLER_MODULE_V1 = "thermocyclerModuleV1"
36
+ THERMOCYCLER_MODULE_V2 = "thermocyclerModuleV2"
37
+ HEATER_SHAKER_MODULE_V1 = "heaterShakerModuleV1"
38
+ MAGNETIC_BLOCK_V1 = "magneticBlockV1"
39
+ ABSORBANCE_READER_V1 = "absorbanceReaderV1"
40
+ FLEX_STACKER_MODULE_V1 = "flexStackerModuleV1"
41
+
42
+ def as_type(self) -> ModuleType:
43
+ """Get the ModuleType of this model."""
44
+ if ModuleModel.is_temperature_module_model(self):
45
+ return ModuleType.TEMPERATURE
46
+ elif ModuleModel.is_magnetic_module_model(self):
47
+ return ModuleType.MAGNETIC
48
+ elif ModuleModel.is_thermocycler_module_model(self):
49
+ return ModuleType.THERMOCYCLER
50
+ elif ModuleModel.is_heater_shaker_module_model(self):
51
+ return ModuleType.HEATER_SHAKER
52
+ elif ModuleModel.is_magnetic_block(self):
53
+ return ModuleType.MAGNETIC_BLOCK
54
+ elif ModuleModel.is_absorbance_reader(self):
55
+ return ModuleType.ABSORBANCE_READER
56
+ elif ModuleModel.is_flex_stacker(self):
57
+ return ModuleType.FLEX_STACKER
58
+
59
+ assert False, f"Invalid ModuleModel {self}"
60
+
61
+ @classmethod
62
+ def is_temperature_module_model(
63
+ cls, model: ModuleModel
64
+ ) -> TypeGuard[TemperatureModuleModel]:
65
+ """Whether a given model is a Temperature Module."""
66
+ return model in [cls.TEMPERATURE_MODULE_V1, cls.TEMPERATURE_MODULE_V2]
67
+
68
+ @classmethod
69
+ def is_magnetic_module_model(
70
+ cls, model: ModuleModel
71
+ ) -> TypeGuard[MagneticModuleModel]:
72
+ """Whether a given model is a Magnetic Module."""
73
+ return model in [cls.MAGNETIC_MODULE_V1, cls.MAGNETIC_MODULE_V2]
74
+
75
+ @classmethod
76
+ def is_thermocycler_module_model(
77
+ cls, model: ModuleModel
78
+ ) -> TypeGuard[ThermocyclerModuleModel]:
79
+ """Whether a given model is a Thermocycler Module."""
80
+ return model in [cls.THERMOCYCLER_MODULE_V1, cls.THERMOCYCLER_MODULE_V2]
81
+
82
+ @classmethod
83
+ def is_heater_shaker_module_model(
84
+ cls, model: ModuleModel
85
+ ) -> TypeGuard[HeaterShakerModuleModel]:
86
+ """Whether a given model is a Heater-Shaker Module."""
87
+ return model == cls.HEATER_SHAKER_MODULE_V1
88
+
89
+ @classmethod
90
+ def is_magnetic_block(cls, model: ModuleModel) -> TypeGuard[MagneticBlockModel]:
91
+ """Whether a given model is a Magnetic block."""
92
+ return model == cls.MAGNETIC_BLOCK_V1
93
+
94
+ @classmethod
95
+ def is_absorbance_reader(
96
+ cls, model: ModuleModel
97
+ ) -> TypeGuard[AbsorbanceReaderModel]:
98
+ """Whether a given model is an Absorbance Plate Reader."""
99
+ return model == cls.ABSORBANCE_READER_V1
100
+
101
+ @classmethod
102
+ def is_flex_stacker(cls, model: ModuleModel) -> TypeGuard[FlexStackerModuleModel]:
103
+ """Whether a given model is a Flex Stacker.."""
104
+ return model == cls.FLEX_STACKER_MODULE_V1
105
+
106
+
107
+ TemperatureModuleModel = Literal[
108
+ ModuleModel.TEMPERATURE_MODULE_V1, ModuleModel.TEMPERATURE_MODULE_V2
109
+ ]
110
+ MagneticModuleModel = Literal[
111
+ ModuleModel.MAGNETIC_MODULE_V1, ModuleModel.MAGNETIC_MODULE_V2
112
+ ]
113
+ ThermocyclerModuleModel = Literal[
114
+ ModuleModel.THERMOCYCLER_MODULE_V1, ModuleModel.THERMOCYCLER_MODULE_V2
115
+ ]
116
+ HeaterShakerModuleModel = Literal[ModuleModel.HEATER_SHAKER_MODULE_V1]
117
+ MagneticBlockModel = Literal[ModuleModel.MAGNETIC_BLOCK_V1]
118
+ AbsorbanceReaderModel = Literal[ModuleModel.ABSORBANCE_READER_V1]
119
+ FlexStackerModuleModel = Literal[ModuleModel.FLEX_STACKER_MODULE_V1]
120
+
121
+
122
+ class ModuleDimensions(BaseModel):
123
+ """Dimension type for modules."""
124
+
125
+ bareOverallHeight: float
126
+ overLabwareHeight: float
127
+ lidHeight: Optional[float] = None
128
+ maxStackerFillHeight: Optional[float] = None
129
+ maxStackerRetrievableHeight: Optional[float] = None
130
+
131
+
132
+ # TODO(mm, 2022-11-07): Deduplicate with Vec3f.
133
+ class ModuleCalibrationPoint(BaseModel):
134
+ """Calibration Point type for module definition."""
135
+
136
+ x: float
137
+ y: float
138
+ z: float
139
+
140
+
141
+ # TODO(mm, 2023-04-13): Move to shared-data, so this binding can be maintained alongside the JSON
142
+ # schema that it's sourced from. We already do that for labware definitions and JSON protocols.
143
+ class ModuleDefinition(BaseModel):
144
+ """A module definition conforming to module definition schema v3."""
145
+
146
+ # Note: This field is misleading.
147
+ #
148
+ # This class only models v3 definitions ("module/schemas/3"), not v2 ("module/schemas/2").
149
+ # labwareOffset is required to have a z-component, for example.
150
+ #
151
+ # When parsing from a schema v3 JSON definition into this model,
152
+ # the definition's `"$otSharedSchema": "module/schemas/3"` field will be thrown away
153
+ # because it has a dollar sign, which doesn't match this field.
154
+ # Then, this field will default to "module/schemas/2", because no value was provided.
155
+ #
156
+ # We should fix this field once Jira RSS-221 is resolved. RSS-221 makes it difficult to fix
157
+ # because robot-server has been storing and loading these bad fields in its database.
158
+ otSharedSchema: str = Field("module/schemas/2", description="The current schema.")
159
+
160
+ moduleType: ModuleType = Field(
161
+ ...,
162
+ description="Module type (Temperature/Magnetic/Thermocycler)",
163
+ )
164
+
165
+ model: ModuleModel = Field(..., description="Model name of the module")
166
+
167
+ labwareOffset: LabwareOffsetVector = Field(
168
+ ...,
169
+ description="Labware offset in x, y, z.",
170
+ )
171
+
172
+ dimensions: ModuleDimensions = Field(..., description="Module dimension")
173
+
174
+ calibrationPoint: ModuleCalibrationPoint = Field(
175
+ ...,
176
+ description="Calibration point of module.",
177
+ )
178
+
179
+ displayName: str = Field(..., description="Display name.")
180
+
181
+ quirks: List[str] = Field(..., description="Module quirks")
182
+
183
+ # In releases prior to https://github.com/Opentrons/opentrons/pull/11873 (v6.3.0),
184
+ # the matrices in slotTransforms were 3x3.
185
+ # After, they are 4x4, even though there was no schema version bump.
186
+ #
187
+ # Because old objects of this class, with the 3x3 matrices, were stored in robot-server's
188
+ # database, this field needs to stay typed loosely enough to support both sizes.
189
+ # We can fix this once Jira RSS-221 is resolved.
190
+ slotTransforms: Dict[str, Any] = Field(
191
+ ...,
192
+ description="Dictionary of transforms for each slot.",
193
+ )
194
+
195
+ compatibleWith: List[ModuleModel] = Field(
196
+ ...,
197
+ description="List of module models this model is compatible with.",
198
+ )
199
+ gripperOffsets: Optional[Dict[str, LabwareMovementOffsetData]] = Field(
200
+ default_factory=dict,
201
+ description="Offsets to use for labware movement using gripper",
202
+ )
203
+
204
+
205
+ class LoadedModule(BaseModel):
206
+ """A module that has been loaded."""
207
+
208
+ id: str
209
+ model: ModuleModel
210
+ location: Optional[DeckSlotLocation] = None
211
+ serialNumber: Optional[str] = None
212
+
213
+
214
+ class SpeedRange(NamedTuple):
215
+ """Minimum and maximum allowed speeds for a shaking module."""
216
+
217
+ min: int
218
+ max: int
219
+
220
+
221
+ class TemperatureRange(NamedTuple):
222
+ """Minimum and maximum allowed temperatures for a heating module."""
223
+
224
+ min: float
225
+ max: float
226
+
227
+
228
+ class HeaterShakerLatchStatus(Enum):
229
+ """Heater-Shaker latch status for determining pipette and labware movement errors."""
230
+
231
+ CLOSED = "closed"
232
+ OPEN = "open"
233
+ UNKNOWN = "unknown"
234
+
235
+
236
+ @dataclass(frozen=True)
237
+ class HeaterShakerMovementRestrictors:
238
+ """Shaking status, latch status and slot location for determining movement restrictions."""
239
+
240
+ plate_shaking: bool
241
+ latch_status: HeaterShakerLatchStatus
242
+ deck_slot: int
243
+
244
+
245
+ ABSMeasureMode = Literal["single", "multi"]
246
+
247
+
248
+ # TODO(mm, 2022-11-07): Deduplicate with Vec3f.
249
+ class ModuleOffsetVector(BaseModel):
250
+ """Offset, in deck coordinates, from nominal to actual position of labware on a module."""
251
+
252
+ x: float
253
+ y: float
254
+ z: float
255
+
256
+
257
+ @dataclass
258
+ class ModuleOffsetData:
259
+ """Module calibration offset data."""
260
+
261
+ moduleOffsetVector: ModuleOffsetVector
262
+ location: DeckSlotLocation
263
+
264
+
265
+ class StackerFillEmptyStrategy(str, Enum):
266
+ """Strategy to use for filling or emptying a stacker."""
267
+
268
+ MANUAL_WITH_PAUSE = "manualWithPause"
269
+ LOGICAL = "logical"
@@ -0,0 +1,76 @@
1
+ """Protocol engine types to do with partial tip configurations."""
2
+
3
+ from typing import Literal, Union
4
+
5
+ from pydantic import (
6
+ BaseModel,
7
+ Field,
8
+ )
9
+
10
+ NOZZLE_NAME_REGEX = r"[A-Z]\d{1,2}"
11
+ PRIMARY_NOZZLE_LITERAL = Literal["A1", "H1", "A12", "H12"]
12
+
13
+
14
+ class AllNozzleLayoutConfiguration(BaseModel):
15
+ """All basemodel to represent a reset to the nozzle configuration. Sending no parameters resets to default."""
16
+
17
+ style: Literal["ALL"] = "ALL"
18
+
19
+
20
+ class SingleNozzleLayoutConfiguration(BaseModel):
21
+ """Minimum information required for a new nozzle configuration."""
22
+
23
+ style: Literal["SINGLE"] = "SINGLE"
24
+ primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
25
+ ...,
26
+ 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.",
27
+ )
28
+
29
+
30
+ class RowNozzleLayoutConfiguration(BaseModel):
31
+ """Minimum information required for a new nozzle configuration."""
32
+
33
+ style: Literal["ROW"] = "ROW"
34
+ primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
35
+ ...,
36
+ 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.",
37
+ )
38
+
39
+
40
+ class ColumnNozzleLayoutConfiguration(BaseModel):
41
+ """Information required for nozzle configurations of type ROW and COLUMN."""
42
+
43
+ style: Literal["COLUMN"] = "COLUMN"
44
+ primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
45
+ ...,
46
+ 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.",
47
+ )
48
+
49
+
50
+ class QuadrantNozzleLayoutConfiguration(BaseModel):
51
+ """Information required for nozzle configurations of type QUADRANT."""
52
+
53
+ style: Literal["QUADRANT"] = "QUADRANT"
54
+ primaryNozzle: PRIMARY_NOZZLE_LITERAL = Field(
55
+ ...,
56
+ 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.",
57
+ )
58
+ frontRightNozzle: str = Field(
59
+ ...,
60
+ pattern=NOZZLE_NAME_REGEX,
61
+ description="The front right nozzle in your configuration.",
62
+ )
63
+ backLeftNozzle: str = Field(
64
+ ...,
65
+ pattern=NOZZLE_NAME_REGEX,
66
+ description="The back left nozzle in your configuration.",
67
+ )
68
+
69
+
70
+ NozzleLayoutConfigurationType = Union[
71
+ AllNozzleLayoutConfiguration,
72
+ SingleNozzleLayoutConfiguration,
73
+ ColumnNozzleLayoutConfiguration,
74
+ RowNozzleLayoutConfiguration,
75
+ QuadrantNozzleLayoutConfiguration,
76
+ ]
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Protocol Engine types that have to do with runtime parameters."""
4
+
5
+ from pathlib import Path
6
+ from typing import Optional, Mapping, Union, Literal, Dict, List
7
+ from pydantic import (
8
+ BaseModel,
9
+ Field,
10
+ StrictBool,
11
+ StrictFloat,
12
+ StrictInt,
13
+ StrictStr,
14
+ )
15
+
16
+
17
+ # TODO (spp, 2024-04-02): move all RTP types to runner
18
+ class RTPBase(BaseModel):
19
+ """Parameters defined in a protocol."""
20
+
21
+ displayName: StrictStr = Field(..., description="Display string for the parameter.")
22
+ variableName: StrictStr = Field(
23
+ ..., description="Python variable name of the parameter."
24
+ )
25
+ description: Optional[StrictStr] = Field(
26
+ None, description="Detailed description of the parameter."
27
+ )
28
+ suffix: Optional[StrictStr] = Field(
29
+ None,
30
+ description="Units (like mL, mm/sec, etc) or a custom suffix for the parameter.",
31
+ )
32
+
33
+
34
+ class NumberParameter(RTPBase):
35
+ """An integer parameter defined in a protocol."""
36
+
37
+ type: Literal["int", "float"] = Field(
38
+ ..., description="String specifying whether the number is an int or float type."
39
+ )
40
+ min: Union[StrictInt, StrictFloat] = Field(
41
+ ..., description="Minimum value that the number param is allowed to have."
42
+ )
43
+ max: Union[StrictInt, StrictFloat] = Field(
44
+ ..., description="Maximum value that the number param is allowed to have."
45
+ )
46
+ value: Union[StrictInt, StrictFloat] = Field(
47
+ ...,
48
+ description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
49
+ )
50
+ default: Union[StrictInt, StrictFloat] = Field(
51
+ ...,
52
+ description="Default value of the parameter, to be used when there is no client-specified value.",
53
+ )
54
+
55
+
56
+ class BooleanParameter(RTPBase):
57
+ """A boolean parameter defined in a protocol."""
58
+
59
+ type: Literal["bool"] = Field(
60
+ default="bool", description="String specifying the type of this parameter"
61
+ )
62
+ value: StrictBool = Field(
63
+ ...,
64
+ description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
65
+ )
66
+ default: StrictBool = Field(
67
+ ...,
68
+ description="Default value of the parameter, to be used when there is no client-specified value.",
69
+ )
70
+
71
+
72
+ class EnumChoice(BaseModel):
73
+ """Components of choices used in RTP Enum Parameters."""
74
+
75
+ displayName: StrictStr = Field(
76
+ ..., description="Display string for the param's choice."
77
+ )
78
+ value: Union[StrictInt, StrictFloat, StrictStr] = Field(
79
+ ..., description="Enum value of the param's choice."
80
+ )
81
+
82
+
83
+ class EnumParameter(RTPBase):
84
+ """A string enum defined in a protocol."""
85
+
86
+ type: Literal["int", "float", "str"] = Field(
87
+ ...,
88
+ description="String specifying whether the parameter is an int or float or string type.",
89
+ )
90
+ choices: List[EnumChoice] = Field(
91
+ ..., description="List of valid choices for this parameter."
92
+ )
93
+ value: Union[StrictInt, StrictFloat, StrictStr] = Field(
94
+ ...,
95
+ description="The value assigned to the parameter; if not supplied by the client, will be assigned the default value.",
96
+ )
97
+ default: Union[StrictInt, StrictFloat, StrictStr] = Field(
98
+ ...,
99
+ description="Default value of the parameter, to be used when there is no client-specified value.",
100
+ )
101
+
102
+
103
+ class FileInfo(BaseModel):
104
+ """A file UUID descriptor."""
105
+
106
+ id: str = Field(
107
+ ...,
108
+ description="The UUID identifier of the file stored on the robot.",
109
+ )
110
+ name: str = Field(..., description="Name of the file, including the extension.")
111
+
112
+
113
+ class CSVParameter(RTPBase):
114
+ """A CSV file parameter defined in a protocol."""
115
+
116
+ type: Literal["csv_file"] = Field(
117
+ default="csv_file", description="String specifying the type of this parameter"
118
+ )
119
+ file: Optional[FileInfo] = Field(
120
+ default=None,
121
+ description="ID of the CSV file stored on the robot; to be used for fetching the CSV file."
122
+ " For local analysis this will most likely be empty.",
123
+ )
124
+
125
+
126
+ RunTimeParameter = Union[NumberParameter, EnumParameter, BooleanParameter, CSVParameter]
127
+
128
+ PrimitiveRunTimeParamValuesType = Mapping[
129
+ StrictStr, Union[StrictInt, StrictFloat, StrictBool, StrictStr]
130
+ ] # update value types as more RTP types are added
131
+
132
+ CSVRunTimeParamFilesType = Mapping[StrictStr, StrictStr]
133
+ CSVRuntimeParamPaths = Dict[str, Path]
@@ -0,0 +1,18 @@
1
+ """Protocol Engine types to deal with tips."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class TipGeometry:
8
+ """Tip geometry data.
9
+
10
+ Props:
11
+ length: The effective length (total length minus overlap) of a tip in mm.
12
+ diameter: Tip diameter in mm.
13
+ volume: Maximum volume in µL.
14
+ """
15
+
16
+ length: float
17
+ diameter: float
18
+ volume: float