opentrons 8.3.2__py2.py3-none-any.whl → 8.4.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

Files changed (196) 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 +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -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 +1356 -107
  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/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,19 @@
1
1
  """ProtocolEngine-based Well core implementations."""
2
- from typing import Optional
2
+ from typing import Optional, Union
3
3
 
4
4
  from opentrons_shared_data.labware.constants import WELL_NAME_PATTERN
5
5
 
6
+ from opentrons.types import Point, Mount, MountType
7
+
6
8
  from opentrons.protocol_engine import WellLocation, WellOrigin, WellOffset
7
9
  from opentrons.protocol_engine import commands as cmd
8
10
  from opentrons.protocol_engine.clients import SyncClient as EngineClient
9
11
  from opentrons.protocols.api_support.util import UnsupportedAPIError
10
- from opentrons.types import Point
12
+ from opentrons.protocol_engine.types.liquid_level_detection import (
13
+ SimulatedProbeResult,
14
+ LiquidTrackingType,
15
+ )
16
+ from opentrons.protocol_engine.errors import PipetteNotAttachedError
11
17
 
12
18
  from . import point_calculations
13
19
  from . import stringify
@@ -44,17 +50,27 @@ class WellCore(AbstractWellCore):
44
50
  @property
45
51
  def diameter(self) -> Optional[float]:
46
52
  """Get the well's diameter, if circular."""
47
- return self._definition.diameter
53
+ return (
54
+ self._definition.diameter if self._definition.shape == "circular" else None
55
+ )
48
56
 
49
57
  @property
50
58
  def length(self) -> Optional[float]:
51
59
  """Get the well's length, if rectangular."""
52
- return self._definition.xDimension
60
+ return (
61
+ self._definition.xDimension
62
+ if self._definition.shape == "rectangular"
63
+ else None
64
+ )
53
65
 
54
66
  @property
55
67
  def width(self) -> Optional[float]:
56
68
  """Get the well's width, if rectangular."""
57
- return self._definition.yDimension
69
+ return (
70
+ self._definition.yDimension
71
+ if self._definition.shape == "rectangular"
72
+ else None
73
+ )
58
74
 
59
75
  @property
60
76
  def depth(self) -> float:
@@ -125,6 +141,14 @@ class WellCore(AbstractWellCore):
125
141
  well_location=WellLocation(origin=WellOrigin.CENTER),
126
142
  )
127
143
 
144
+ def get_meniscus(self) -> Union[Point, SimulatedProbeResult]:
145
+ """Get the coordinate of the well's meniscus."""
146
+ current_liquid_height = self.current_liquid_height()
147
+ if isinstance(current_liquid_height, float):
148
+ return self.get_bottom(z_offset=current_liquid_height)
149
+ else:
150
+ return current_liquid_height
151
+
128
152
  def load_liquid(
129
153
  self,
130
154
  liquid: Liquid,
@@ -155,3 +179,47 @@ class WellCore(AbstractWellCore):
155
179
  y_ratio=y,
156
180
  z_ratio=z,
157
181
  )
182
+
183
+ def estimate_liquid_height_after_pipetting(
184
+ self,
185
+ mount: Mount | str,
186
+ operation_volume: float,
187
+ ) -> LiquidTrackingType:
188
+ """Return an estimate of liquid height after pipetting without raising an error."""
189
+ labware_id = self.labware_id
190
+ well_name = self._name
191
+ if isinstance(mount, Mount):
192
+ mount_type = MountType.from_hw_mount(mount)
193
+ else:
194
+ mount_type = MountType(mount)
195
+ pipette_from_mount = self._engine_client.state.pipettes.get_by_mount(mount_type)
196
+ if pipette_from_mount is None:
197
+ raise PipetteNotAttachedError(f"No pipette present on mount {mount}")
198
+ pipette_id = pipette_from_mount.id
199
+ starting_liquid_height = self.current_liquid_height()
200
+ projected_final_height = (
201
+ self._engine_client.state.geometry.get_well_height_after_liquid_handling(
202
+ labware_id=labware_id,
203
+ well_name=well_name,
204
+ pipette_id=pipette_id,
205
+ initial_height=starting_liquid_height,
206
+ volume=operation_volume,
207
+ )
208
+ )
209
+ return projected_final_height
210
+
211
+ def current_liquid_height(self) -> LiquidTrackingType:
212
+ """Return the current liquid height within a well."""
213
+ labware_id = self.labware_id
214
+ well_name = self._name
215
+ return self._engine_client.state.geometry.get_meniscus_height(
216
+ labware_id=labware_id, well_name=well_name
217
+ )
218
+
219
+ def get_liquid_volume(self) -> LiquidTrackingType:
220
+ """Return the current volume in a well."""
221
+ labware_id = self.labware_id
222
+ well_name = self._name
223
+ return self._engine_client.state.geometry.get_current_well_volume(
224
+ labware_id=labware_id, well_name=well_name
225
+ )
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from abc import abstractmethod, ABC
6
- from typing import Any, Generic, Optional, TypeVar, Union, List
6
+ from typing import Any, Generic, Optional, TypeVar, Union, List, Tuple, Literal
7
7
 
8
8
  from opentrons import types
9
9
  from opentrons.hardware_control.dev_types import PipetteDict
@@ -11,11 +11,14 @@ from opentrons.protocols.api_support.util import FlowRates
11
11
  from opentrons.protocols.advanced_control.transfers.common import TransferTipPolicyV2
12
12
  from opentrons.protocol_api._nozzle_layout import NozzleLayout
13
13
  from opentrons.protocol_api._liquid import LiquidClass
14
+ from opentrons.protocol_engine.types import LiquidTrackingType
15
+
14
16
  from ..disposal_locations import TrashBin, WasteChute
15
17
  from .well import WellCoreType
18
+ from .labware import LabwareCoreType
16
19
 
17
20
 
18
- class AbstractInstrument(ABC, Generic[WellCoreType]):
21
+ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
19
22
  @abstractmethod
20
23
  def get_default_speed(self) -> float:
21
24
  ...
@@ -25,11 +28,14 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
25
28
  ...
26
29
 
27
30
  @abstractmethod
28
- def air_gap_in_place(self, volume: float, flow_rate: float) -> None:
31
+ def air_gap_in_place(
32
+ self, volume: float, flow_rate: float, correction_volume: Optional[float] = None
33
+ ) -> None:
29
34
  """Aspirate a given volume of air from the current location of the pipette.
30
35
  Args:
31
36
  volume: The volume of air to aspirate, in microliters.
32
37
  flow_rate: The flow rate of air into the pipette, in microliters.
38
+ correction_volume: The correction volume in uL.
33
39
  """
34
40
 
35
41
  @abstractmethod
@@ -41,7 +47,8 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
41
47
  rate: float,
42
48
  flow_rate: float,
43
49
  in_place: bool,
44
- is_meniscus: Optional[bool] = None,
50
+ meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
51
+ correction_volume: Optional[float] = None,
45
52
  ) -> None:
46
53
  """Aspirate a given volume of liquid from the specified location.
47
54
  Args:
@@ -51,6 +58,8 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
51
58
  rate: The rate for how quickly to aspirate.
52
59
  flow_rate: The flow rate in µL/s to aspirate at.
53
60
  in_place: Whether this is in-place.
61
+ meniscus_tracking: Optional data about where to aspirate from.
62
+ correction_volume: The correction volume in uL
54
63
  """
55
64
  ...
56
65
 
@@ -64,7 +73,8 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
64
73
  flow_rate: float,
65
74
  in_place: bool,
66
75
  push_out: Optional[float],
67
- is_meniscus: Optional[bool] = None,
76
+ meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
77
+ correction_volume: Optional[float] = None,
68
78
  ) -> None:
69
79
  """Dispense a given volume of liquid into the specified location.
70
80
  Args:
@@ -75,6 +85,8 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
75
85
  flow_rate: The flow rate in µL/s to dispense at.
76
86
  in_place: Whether this is in-place.
77
87
  push_out: The amount to push the plunger below bottom position.
88
+ correction_volume: The correction volume in uL
89
+ meniscus_tracking: Optional data about where to dispense from.
78
90
  """
79
91
  ...
80
92
 
@@ -102,6 +114,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
102
114
  radius: float,
103
115
  z_offset: float,
104
116
  speed: float,
117
+ mm_from_edge: Optional[float] = None,
105
118
  ) -> None:
106
119
  ...
107
120
 
@@ -177,6 +190,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
177
190
  force_direct: bool,
178
191
  minimum_z_height: Optional[float],
179
192
  speed: Optional[float],
193
+ check_for_movement_conflicts: bool,
180
194
  ) -> None:
181
195
  ...
182
196
 
@@ -192,7 +206,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
192
206
  @abstractmethod
193
207
  def resin_tip_unseal(
194
208
  self,
195
- location: types.Location,
209
+ location: types.Location | None,
196
210
  well_core: WellCoreType,
197
211
  ) -> None:
198
212
  ...
@@ -239,10 +253,18 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
239
253
  def get_current_volume(self) -> float:
240
254
  ...
241
255
 
256
+ @abstractmethod
257
+ def get_has_clean_tip(self) -> bool:
258
+ ...
259
+
242
260
  @abstractmethod
243
261
  def get_available_volume(self) -> float:
244
262
  ...
245
263
 
264
+ @abstractmethod
265
+ def get_minimum_liquid_sense_height(self) -> float:
266
+ ...
267
+
246
268
  @abstractmethod
247
269
  def get_hardware_state(self) -> PipetteDict:
248
270
  """Get the current state of the pipette hardware as a dictionary."""
@@ -338,29 +360,57 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
338
360
  ...
339
361
 
340
362
  @abstractmethod
341
- def load_liquid_class(
363
+ def transfer_with_liquid_class(
342
364
  self,
343
365
  liquid_class: LiquidClass,
344
- pipette_load_name: str,
345
- tiprack_uri: str,
346
- ) -> str:
347
- """Load the liquid class properties of given pipette and tiprack into the engine.
366
+ volume: float,
367
+ source: List[Tuple[types.Location, WellCoreType]],
368
+ dest: List[Tuple[types.Location, WellCoreType]],
369
+ new_tip: TransferTipPolicyV2,
370
+ tip_racks: List[Tuple[types.Location, LabwareCoreType]],
371
+ starting_tip: Optional[WellCoreType],
372
+ trash_location: Union[types.Location, TrashBin, WasteChute],
373
+ return_tip: bool,
374
+ ) -> None:
375
+ """Transfer a liquid from source to dest according to liquid class properties."""
376
+ ...
348
377
 
349
- Returns: ID of the liquid class record
378
+ @abstractmethod
379
+ def distribute_with_liquid_class(
380
+ self,
381
+ liquid_class: LiquidClass,
382
+ volume: float,
383
+ source: Tuple[types.Location, WellCoreType],
384
+ dest: List[Tuple[types.Location, WellCoreType]],
385
+ new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
386
+ tip_racks: List[Tuple[types.Location, LabwareCoreType]],
387
+ starting_tip: Optional[WellCoreType],
388
+ trash_location: Union[types.Location, TrashBin, WasteChute],
389
+ return_tip: bool,
390
+ ) -> None:
391
+ """
392
+ Distribute a liquid from single source to multiple destinations
393
+ according to liquid class properties.
350
394
  """
351
395
  ...
352
396
 
353
397
  @abstractmethod
354
- def transfer_liquid(
398
+ def consolidate_with_liquid_class(
355
399
  self,
356
- liquid_class_id: str,
400
+ liquid_class: LiquidClass,
357
401
  volume: float,
358
- source: List[WellCoreType],
359
- dest: List[WellCoreType],
360
- new_tip: TransferTipPolicyV2,
361
- trash_location: Union[WellCoreType, types.Location, TrashBin, WasteChute],
402
+ source: List[Tuple[types.Location, WellCoreType]],
403
+ dest: Tuple[types.Location, WellCoreType],
404
+ new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
405
+ tip_racks: List[Tuple[types.Location, LabwareCoreType]],
406
+ starting_tip: Optional[WellCoreType],
407
+ trash_location: Union[types.Location, TrashBin, WasteChute],
408
+ return_tip: bool,
362
409
  ) -> None:
363
- """Transfer a liquid from source to dest according to liquid class properties."""
410
+ """
411
+ Consolidate liquid from multiple sources to a single destination
412
+ using the specified liquid class properties.
413
+ """
364
414
  ...
365
415
 
366
416
  @abstractmethod
@@ -388,7 +438,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
388
438
  @abstractmethod
389
439
  def liquid_probe_without_recovery(
390
440
  self, well_core: WellCoreType, loc: types.Location
391
- ) -> float:
441
+ ) -> LiquidTrackingType:
392
442
  """Do a liquid probe to find the level of the liquid in the well."""
393
443
  ...
394
444
 
@@ -397,4 +447,4 @@ class AbstractInstrument(ABC, Generic[WellCoreType]):
397
447
  """Check if the nozzle configuration currently supports LLD."""
398
448
 
399
449
 
400
- InstrumentCoreType = TypeVar("InstrumentCoreType", bound=AbstractInstrument[Any])
450
+ InstrumentCoreType = TypeVar("InstrumentCoreType", bound=AbstractInstrument[Any, Any])
@@ -7,7 +7,8 @@ from typing import Any, Generic, List, NamedTuple, Optional, TypeVar, Dict
7
7
 
8
8
  from opentrons_shared_data.labware.types import (
9
9
  LabwareUri,
10
- LabwareParameters as LabwareParametersDict,
10
+ LabwareParameters2,
11
+ LabwareParameters3,
11
12
  LabwareDefinition as LabwareDefinitionDict,
12
13
  )
13
14
 
@@ -17,6 +18,9 @@ from .._liquid import Liquid
17
18
  from .well import WellCoreType
18
19
 
19
20
 
21
+ _LabwareParametersDict = LabwareParameters2 | LabwareParameters3
22
+
23
+
20
24
  class LabwareLoadParams(NamedTuple):
21
25
  """Unique load parameters of a labware."""
22
26
 
@@ -75,7 +79,7 @@ class AbstractLabware(ABC, Generic[WellCoreType]):
75
79
  """Get the labware's definition as a plain dictionary."""
76
80
 
77
81
  @abstractmethod
78
- def get_parameters(self) -> LabwareParametersDict:
82
+ def get_parameters(self) -> _LabwareParametersDict:
79
83
  """Get the labware's definition's `parameters` field as a plain dictionary."""
80
84
 
81
85
  @abstractmethod
@@ -3,7 +3,11 @@ from dataclasses import dataclass
3
3
  from typing import Optional
4
4
 
5
5
  from opentrons.hardware_control.modules import ModuleModel as HardwareModuleModel
6
- from opentrons.protocol_engine import ProtocolEngine, LabwareOffsetLocation, ModuleModel
6
+ from opentrons.protocol_engine import (
7
+ ProtocolEngine,
8
+ LegacyLabwareOffsetLocation,
9
+ ModuleModel,
10
+ )
7
11
  from opentrons.types import DeckSlotName, Point
8
12
 
9
13
  from ..labware import LabwareLoadParams
@@ -81,9 +85,9 @@ class LabwareOffsetProvider(AbstractLabwareOffsetProvider):
81
85
 
82
86
  See the parent class for param details.
83
87
  """
84
- offset = self._labware_view.find_applicable_labware_offset(
88
+ offset = self._labware_view.find_applicable_labware_offset_by_legacy_location(
85
89
  definition_uri=load_params.as_uri(),
86
- location=LabwareOffsetLocation(
90
+ location=LegacyLabwareOffsetLocation(
87
91
  slotName=deck_slot,
88
92
  moduleModel=(
89
93
  None
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING, Optional, Union, List
4
+ from typing import TYPE_CHECKING, Optional, Union, List, Tuple, Literal
5
5
 
6
6
  from opentrons import types
7
7
  from opentrons.hardware_control import CriticalPoint
@@ -22,9 +22,12 @@ from opentrons.protocols.geometry import planning
22
22
  from opentrons.protocol_api._nozzle_layout import NozzleLayout
23
23
  from opentrons.protocol_api._liquid import LiquidClass
24
24
 
25
+ from opentrons.protocol_engine.types import LiquidTrackingType
26
+
25
27
  from ...disposal_locations import TrashBin, WasteChute
26
28
  from ..instrument import AbstractInstrument
27
29
  from .legacy_well_core import LegacyWellCore
30
+ from .legacy_labware_core import LegacyLabwareCore
28
31
  from .legacy_module_core import LegacyThermocyclerCore, LegacyHeaterShakerCore
29
32
 
30
33
  if TYPE_CHECKING:
@@ -37,7 +40,7 @@ _PRE_2_2_TIP_DROP_HEIGHT_MM = 10
37
40
  """In PAPIv2.1 and below, tips are always dropped 10 mm from the bottom of the well."""
38
41
 
39
42
 
40
- class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
43
+ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]):
41
44
  """Implementation of the InstrumentContext interface."""
42
45
 
43
46
  def __init__(
@@ -73,7 +76,9 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
73
76
  """Sets the speed at which the robot's gantry moves."""
74
77
  self._default_speed = speed
75
78
 
76
- def air_gap_in_place(self, volume: float, flow_rate: float) -> None:
79
+ def air_gap_in_place(
80
+ self, volume: float, flow_rate: float, correction_volume: Optional[float] = None
81
+ ) -> None:
77
82
  assert False, "Air gap tracking only available in API version 2.22 and later"
78
83
 
79
84
  def aspirate(
@@ -84,7 +89,8 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
84
89
  rate: float,
85
90
  flow_rate: float,
86
91
  in_place: bool,
87
- is_meniscus: Optional[bool] = None,
92
+ meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
93
+ correction_volume: Optional[float] = None,
88
94
  ) -> None:
89
95
  """Aspirate a given volume of liquid from the specified location.
90
96
  Args:
@@ -94,6 +100,8 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
94
100
  rate: The rate in µL/s to aspirate at.
95
101
  flow_rate: Not used in this core.
96
102
  in_place: Whether we should move_to location.
103
+ correction_volume: Not used in this core
104
+ meniscus_tracking: Optional data about where to aspirate from.
97
105
  """
98
106
  if self.get_current_volume() == 0:
99
107
  # Make sure we're at the top of the labware and clear of any
@@ -127,7 +135,8 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
127
135
  flow_rate: float,
128
136
  in_place: bool,
129
137
  push_out: Optional[float],
130
- is_meniscus: Optional[bool] = None,
138
+ meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
139
+ correction_volume: Optional[float] = None,
131
140
  ) -> None:
132
141
  """Dispense a given volume of liquid into the specified location.
133
142
  Args:
@@ -137,7 +146,9 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
137
146
  rate: The rate in µL/s to dispense at.
138
147
  flow_rate: Not used in this core.
139
148
  in_place: Whether we should move_to location.
149
+ correction_volume: Not used in this core.
140
150
  push_out: The amount to push the plunger below bottom position.
151
+ meniscus_tracking: Optional data about where to dispense from.
141
152
  """
142
153
  if isinstance(location, (TrashBin, WasteChute)):
143
154
  raise APIVersionError(
@@ -179,11 +190,14 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
179
190
  radius: float,
180
191
  z_offset: float,
181
192
  speed: float,
193
+ mm_from_edge: Optional[float] = None,
182
194
  ) -> None:
183
195
  """
184
196
  Touch the pipette tip to the sides of a well, with the intent of
185
197
  removing left-over droplets
186
198
  """
199
+ if mm_from_edge is not None:
200
+ raise APIVersionError(api_element="mm_from_edge argument")
187
201
  # TODO al 20201110 - build_edges relies on where being a Well. This is
188
202
  # an unpleasant compromise until refactoring build_edges to support
189
203
  # LegacyWellCore.
@@ -318,7 +332,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
318
332
 
319
333
  def resin_tip_unseal(
320
334
  self,
321
- location: types.Location,
335
+ location: types.Location | None,
322
336
  well_core: WellCore,
323
337
  ) -> None:
324
338
  raise APIVersionError(api_element="Unsealing resin tips.")
@@ -353,6 +367,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
353
367
  force_direct: bool = False,
354
368
  minimum_z_height: Optional[float] = None,
355
369
  speed: Optional[float] = None,
370
+ check_for_movement_conflicts: bool = False,
356
371
  ) -> None:
357
372
  """Move the instrument.
358
373
 
@@ -362,6 +377,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
362
377
  force_direct: Force a direct movement instead of an arc.
363
378
  minimum_z_height: Set a minimum travel height for a movement arc.
364
379
  speed: Override the travel speed in mm/s.
380
+ check_for_movement_conflicts: Not used in legacy implementation
365
381
 
366
382
  Raises:
367
383
  LabwareHeightError: An item on the deck is taller than
@@ -424,32 +440,6 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
424
440
  location=location, mount=location_cache_mount
425
441
  )
426
442
 
427
- def evotip_seal(
428
- self,
429
- location: types.Location,
430
- well_core: LegacyWellCore,
431
- in_place: Optional[bool] = False,
432
- ) -> None:
433
- """This will never be called because it was added in API 2.22."""
434
- assert False, "evotip_seal only supported in API 2.22 & later"
435
-
436
- def evotip_unseal(
437
- self, location: types.Location, well_core: WellCore, home_after: Optional[bool]
438
- ) -> None:
439
- """This will never be called because it was added in API 2.22."""
440
- assert False, "evotip_unseal only supported in API 2.22 & later"
441
-
442
- def evotip_dispense(
443
- self,
444
- location: types.Location,
445
- well_core: WellCore,
446
- volume: Optional[float] = None,
447
- flow_rate: Optional[float] = None,
448
- push_out: Optional[float] = None,
449
- ) -> None:
450
- """This will never be called because it was added in API 2.22."""
451
- assert False, "evotip_dispense only supported in API 2.22 & later"
452
-
453
443
  def get_mount(self) -> types.Mount:
454
444
  """Get the mount this pipette is attached to."""
455
445
  return self._mount
@@ -486,6 +476,10 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
486
476
  """Get the current volume."""
487
477
  return self.get_hardware_state()["current_volume"]
488
478
 
479
+ def get_has_clean_tip(self) -> bool:
480
+ """Get if has a clean tip, only used with LLD and engine commands."""
481
+ return False
482
+
489
483
  def get_available_volume(self) -> float:
490
484
  """Get the available volume."""
491
485
  return self.get_hardware_state()["available_volume"]
@@ -606,29 +600,51 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
606
600
  """This will never be called because it was added in API 2.16."""
607
601
  pass
608
602
 
609
- def load_liquid_class(
603
+ def transfer_with_liquid_class(
610
604
  self,
611
605
  liquid_class: LiquidClass,
612
- pipette_load_name: str,
613
- tiprack_uri: str,
614
- ) -> str:
615
- """This will never be called because it was added in .."""
616
- # TODO(spp, 2024-11-20): update the docstring and error to include API version
617
- assert False, "load_liquid_class is not supported in legacy context"
618
-
619
- def transfer_liquid(
620
- self,
621
- liquid_class_id: str,
622
606
  volume: float,
623
- source: List[LegacyWellCore],
624
- dest: List[LegacyWellCore],
607
+ source: List[Tuple[types.Location, LegacyWellCore]],
608
+ dest: List[Tuple[types.Location, LegacyWellCore]],
625
609
  new_tip: TransferTipPolicyV2,
626
- trash_location: Union[LegacyWellCore, types.Location, TrashBin, WasteChute],
610
+ tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
611
+ starting_tip: Optional[LegacyWellCore],
612
+ trash_location: Union[types.Location, TrashBin, WasteChute],
613
+ return_tip: bool,
627
614
  ) -> None:
628
- """This will never be called because it was added in .."""
629
- # TODO(spp, 2024-11-20): update the docstring and error to include API version
615
+ """This will never be called because it was added in API 2.23"""
630
616
  assert False, "transfer_liquid is not supported in legacy context"
631
617
 
618
+ def distribute_with_liquid_class(
619
+ self,
620
+ liquid_class: LiquidClass,
621
+ volume: float,
622
+ source: Tuple[types.Location, LegacyWellCore],
623
+ dest: List[Tuple[types.Location, LegacyWellCore]],
624
+ new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
625
+ tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
626
+ starting_tip: Optional[LegacyWellCore],
627
+ trash_location: Union[types.Location, TrashBin, WasteChute],
628
+ return_tip: bool,
629
+ ) -> None:
630
+ """This will never be called because it was added in API 2.23"""
631
+ assert False, "distribute_liquid is not supported in legacy context"
632
+
633
+ def consolidate_with_liquid_class(
634
+ self,
635
+ liquid_class: LiquidClass,
636
+ volume: float,
637
+ source: List[Tuple[types.Location, LegacyWellCore]],
638
+ dest: Tuple[types.Location, LegacyWellCore],
639
+ new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
640
+ tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
641
+ starting_tip: Optional[LegacyWellCore],
642
+ trash_location: Union[types.Location, TrashBin, WasteChute],
643
+ return_tip: bool,
644
+ ) -> None:
645
+ """This will never be called because it was added in API 2.23."""
646
+ assert False, "consolidate_liquid is not supported in legacy context"
647
+
632
648
  def get_active_channels(self) -> int:
633
649
  """This will never be called because it was added in API 2.16."""
634
650
  assert False, "get_active_channels only supported in API 2.16 & later"
@@ -657,7 +673,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
657
673
 
658
674
  def liquid_probe_without_recovery(
659
675
  self, well_core: WellCore, loc: types.Location
660
- ) -> float:
676
+ ) -> LiquidTrackingType:
661
677
  """This will never be called because it was added in API 2.20."""
662
678
  assert False, "liquid_probe_without_recovery only supported in API 2.20 & later"
663
679
 
@@ -667,3 +683,14 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
667
683
  def nozzle_configuration_valid_for_lld(self) -> bool:
668
684
  """Check if the nozzle configuration currently supports LLD."""
669
685
  return False
686
+
687
+ def get_minimum_liquid_sense_height(self) -> float:
688
+ return 0.0
689
+
690
+ def estimate_liquid_height(
691
+ self,
692
+ well_core: LegacyWellCore,
693
+ starting_liquid_height: float,
694
+ operation_volume: float,
695
+ ) -> float:
696
+ return 0.0
@@ -6,7 +6,7 @@ from opentrons.protocols.api_support.tip_tracker import TipTracker
6
6
 
7
7
  from opentrons.types import DeckSlotName, Location, Point, NozzleMapInterface
8
8
 
9
- from opentrons_shared_data.labware.types import LabwareParameters, LabwareDefinition
9
+ from opentrons_shared_data.labware.types import LabwareParameters2, LabwareDefinition2
10
10
 
11
11
  from ..._liquid import Liquid
12
12
  from ..labware import AbstractLabware, LabwareLoadParams
@@ -36,7 +36,11 @@ class LegacyLabwareCore(AbstractLabware[LegacyWellCore]):
36
36
 
37
37
  def __init__(
38
38
  self,
39
- definition: LabwareDefinition,
39
+ # We need labware schema 2, specifically, because schema 3 changes how positions
40
+ # are calculated, and we don't attempt to handle that here in
41
+ # `opentrons.protocol_api.core.legacy`. We do handle it in
42
+ # `opentrons.protocol_api.core.engine` and `opentrons.protocol_engine`.
43
+ definition: LabwareDefinition2,
40
44
  parent: Location,
41
45
  label: Optional[str] = None,
42
46
  ) -> None:
@@ -106,10 +110,10 @@ class LegacyLabwareCore(AbstractLabware[LegacyWellCore]):
106
110
  def set_name(self, new_name: str) -> None:
107
111
  self._name = new_name
108
112
 
109
- def get_definition(self) -> LabwareDefinition:
113
+ def get_definition(self) -> LabwareDefinition2:
110
114
  return self._definition
111
115
 
112
- def get_parameters(self) -> LabwareParameters:
116
+ def get_parameters(self) -> LabwareParameters2:
113
117
  return self._parameters
114
118
 
115
119
  def get_quirks(self) -> List[str]: