opentrons 8.4.0a3__py2.py3-none-any.whl → 8.4.0a5__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 (42) hide show
  1. opentrons/legacy_commands/commands.py +83 -2
  2. opentrons/legacy_commands/helpers.py +59 -1
  3. opentrons/legacy_commands/types.py +30 -0
  4. opentrons/protocol_api/core/engine/instrument.py +182 -115
  5. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  6. opentrons/protocol_api/core/engine/transfer_components_executor.py +30 -25
  7. opentrons/protocol_api/core/instrument.py +8 -4
  8. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +9 -30
  9. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +8 -4
  10. opentrons/protocol_api/core/well.py +1 -1
  11. opentrons/protocol_api/instrument_context.py +144 -73
  12. opentrons/protocol_api/labware.py +26 -44
  13. opentrons/protocol_api/protocol_context.py +18 -16
  14. opentrons/protocol_engine/commands/__init__.py +38 -38
  15. opentrons/protocol_engine/commands/aspirate_while_tracking.py +38 -65
  16. opentrons/protocol_engine/commands/command_unions.py +33 -33
  17. opentrons/protocol_engine/commands/dispense_while_tracking.py +36 -72
  18. opentrons/protocol_engine/commands/labware_handling_common.py +6 -1
  19. opentrons/protocol_engine/commands/liquid_probe.py +1 -2
  20. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  21. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +27 -27
  22. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +32 -27
  23. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +22 -22
  24. opentrons/protocol_engine/execution/pipetting.py +1 -0
  25. opentrons/protocol_engine/labware_offset_standardization.py +22 -1
  26. opentrons/protocol_engine/resources/deck_configuration_provider.py +8 -4
  27. opentrons/protocol_engine/state/frustum_helpers.py +12 -4
  28. opentrons/protocol_engine/state/geometry.py +121 -72
  29. opentrons/protocol_engine/state/update_types.py +1 -1
  30. opentrons/protocol_engine/state/wells.py +1 -1
  31. opentrons/protocol_engine/types/__init__.py +6 -0
  32. opentrons/protocol_engine/types/well_position.py +18 -1
  33. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
  34. opentrons/protocols/labware.py +23 -18
  35. opentrons/util/logging_config.py +94 -25
  36. opentrons/util/logging_queue_handler.py +61 -0
  37. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/METADATA +4 -4
  38. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/RECORD +42 -41
  39. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/LICENSE +0 -0
  40. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/WHEEL +0 -0
  41. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/entry_points.txt +0 -0
  42. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a5.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- """Seal evotip resin tip command request, result, and implementation models."""
1
+ """Seal tips to pipette command request, result, and implementation models."""
2
2
 
3
3
  from __future__ import annotations
4
4
  from pydantic import Field, BaseModel
@@ -7,7 +7,7 @@ from opentrons.types import MountType
7
7
  from opentrons.protocol_engine.types import MotorAxis
8
8
  from typing_extensions import Literal
9
9
 
10
- from ..resources import ModelUtils
10
+ from ..resources import ModelUtils, ensure_ot3_hardware
11
11
  from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
12
12
  from .pipetting_common import (
13
13
  PipetteIdMixin,
@@ -39,7 +39,7 @@ if TYPE_CHECKING:
39
39
  )
40
40
 
41
41
 
42
- EvotipSealPipetteCommandType = Literal["evotipSealPipette"]
42
+ SealPipetteToTipCommandType = Literal["sealPipetteToTip"]
43
43
  _CAM_PREP_DISTANCE_DEFAULT = 8.25
44
44
  _CAM_PRESS_DISTANCE_DEFAULT = 3.5
45
45
  _CAM_EJECTOR_PUSH_MM_DEFAULT = 7.0
@@ -64,7 +64,7 @@ class TipPickUpParams(BaseModel):
64
64
  )
65
65
 
66
66
 
67
- class EvotipSealPipetteParams(PipetteIdMixin):
67
+ class SealPipetteToTipParams(PipetteIdMixin):
68
68
  """Payload needed to seal resin tips to a pipette."""
69
69
 
70
70
  labwareId: str = Field(..., description="Identifier of labware to use.")
@@ -78,8 +78,8 @@ class EvotipSealPipetteParams(PipetteIdMixin):
78
78
  )
79
79
 
80
80
 
81
- class EvotipSealPipetteResult(DestinationPositionResult):
82
- """Result data from the execution of a EvotipSealPipette."""
81
+ class SealPipetteToTipResult(DestinationPositionResult):
82
+ """Result data from the execution of a SealPipetteToTip."""
83
83
 
84
84
  tipVolume: float = Field(
85
85
  0,
@@ -101,15 +101,15 @@ class EvotipSealPipetteResult(DestinationPositionResult):
101
101
 
102
102
 
103
103
  _ExecuteReturn = Union[
104
- SuccessData[EvotipSealPipetteResult],
104
+ SuccessData[SealPipetteToTipResult],
105
105
  DefinedErrorData[StallOrCollisionError],
106
106
  ]
107
107
 
108
108
 
109
- class EvotipSealPipetteImplementation(
110
- AbstractCommandImpl[EvotipSealPipetteParams, _ExecuteReturn]
109
+ class SealPipetteToTipImplementation(
110
+ AbstractCommandImpl[SealPipetteToTipParams, _ExecuteReturn]
111
111
  ):
112
- """Evotip seal pipette command implementation."""
112
+ """Seal pipette command implementation."""
113
113
 
114
114
  def __init__(
115
115
  self,
@@ -159,6 +159,11 @@ class EvotipSealPipetteImplementation(
159
159
  axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
160
160
  )
161
161
 
162
+ ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
163
+ await ot3_hardware_api.update_axis_position_estimations(
164
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
165
+ )
166
+
162
167
  async def cam_action_relative_pickup_tip(
163
168
  self,
164
169
  tip_pick_up_params: TipPickUpParams,
@@ -212,8 +217,8 @@ class EvotipSealPipetteImplementation(
212
217
  )
213
218
 
214
219
  async def execute(
215
- self, params: EvotipSealPipetteParams
216
- ) -> Union[SuccessData[EvotipSealPipetteResult], _ExecuteReturn]:
220
+ self, params: SealPipetteToTipParams
221
+ ) -> Union[SuccessData[SealPipetteToTipResult], _ExecuteReturn]:
217
222
  """Move to and pick up a tip using the requested pipette."""
218
223
  pipette_id = params.pipetteId
219
224
  labware_id = params.labwareId
@@ -289,7 +294,7 @@ class EvotipSealPipetteImplementation(
289
294
  fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
290
295
  )
291
296
  return SuccessData(
292
- public=EvotipSealPipetteResult(
297
+ public=SealPipetteToTipResult(
293
298
  tipVolume=tip_geometry.volume,
294
299
  tipLength=tip_geometry.length,
295
300
  tipDiameter=tip_geometry.diameter,
@@ -299,28 +304,28 @@ class EvotipSealPipetteImplementation(
299
304
  )
300
305
 
301
306
 
302
- class EvotipSealPipette(
307
+ class SealPipetteToTip(
303
308
  BaseCommand[
304
- EvotipSealPipetteParams,
305
- EvotipSealPipetteResult,
309
+ SealPipetteToTipParams,
310
+ SealPipetteToTipResult,
306
311
  StallOrCollisionError,
307
312
  ]
308
313
  ):
309
- """Seal evotip resin tip command model."""
314
+ """Seal tip command model."""
310
315
 
311
- commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
312
- params: EvotipSealPipetteParams
313
- result: Optional[EvotipSealPipetteResult] = None
316
+ commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
317
+ params: SealPipetteToTipParams
318
+ result: Optional[SealPipetteToTipResult] = None
314
319
 
315
320
  _ImplementationCls: Type[
316
- EvotipSealPipetteImplementation
317
- ] = EvotipSealPipetteImplementation
321
+ SealPipetteToTipImplementation
322
+ ] = SealPipetteToTipImplementation
318
323
 
319
324
 
320
- class EvotipSealPipetteCreate(BaseCommandCreate[EvotipSealPipetteParams]):
321
- """Seal evotip resin tip command creation request model."""
325
+ class SealPipetteToTipCreate(BaseCommandCreate[SealPipetteToTipParams]):
326
+ """Seal tip command creation request model."""
322
327
 
323
- commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
324
- params: EvotipSealPipetteParams
328
+ commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
329
+ params: SealPipetteToTipParams
325
330
 
326
- _CommandCls: Type[EvotipSealPipette] = EvotipSealPipette
331
+ _CommandCls: Type[SealPipetteToTip] = SealPipetteToTip
@@ -1,4 +1,4 @@
1
- """Unseal evotip resin tip command request, result, and implementation models."""
1
+ """Unseal tip from pipette command request, result, and implementation models."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -32,10 +32,10 @@ if TYPE_CHECKING:
32
32
  from ..execution import MovementHandler, TipHandler, GantryMover
33
33
 
34
34
 
35
- EvotipUnsealPipetteCommandType = Literal["evotipUnsealPipette"]
35
+ UnsealPipetteFromTipCommandType = Literal["unsealPipetteFromTip"]
36
36
 
37
37
 
38
- class EvotipUnsealPipetteParams(PipetteIdMixin):
38
+ class UnsealPipetteFromTipParams(PipetteIdMixin):
39
39
  """Payload required to drop a tip in a specific well."""
40
40
 
41
41
  labwareId: str = Field(..., description="Identifier of labware to use.")
@@ -46,19 +46,19 @@ class EvotipUnsealPipetteParams(PipetteIdMixin):
46
46
  )
47
47
 
48
48
 
49
- class EvotipUnsealPipetteResult(DestinationPositionResult):
49
+ class UnsealPipetteFromTipResult(DestinationPositionResult):
50
50
  """Result data from the execution of a DropTip command."""
51
51
 
52
52
  pass
53
53
 
54
54
 
55
55
  _ExecuteReturn = (
56
- SuccessData[EvotipUnsealPipetteResult] | DefinedErrorData[StallOrCollisionError]
56
+ SuccessData[UnsealPipetteFromTipResult] | DefinedErrorData[StallOrCollisionError]
57
57
  )
58
58
 
59
59
 
60
- class EvotipUnsealPipetteImplementation(
61
- AbstractCommandImpl[EvotipUnsealPipetteParams, _ExecuteReturn]
60
+ class UnsealPipetteFromTipImplementation(
61
+ AbstractCommandImpl[UnsealPipetteFromTipParams, _ExecuteReturn]
62
62
  ):
63
63
  """Drop tip command implementation."""
64
64
 
@@ -77,7 +77,7 @@ class EvotipUnsealPipetteImplementation(
77
77
  self._model_utils = model_utils
78
78
  self._gantry_mover = gantry_mover
79
79
 
80
- async def execute(self, params: EvotipUnsealPipetteParams) -> _ExecuteReturn:
80
+ async def execute(self, params: UnsealPipetteFromTipParams) -> _ExecuteReturn:
81
81
  """Move to and drop a tip using the requested pipette."""
82
82
  pipette_id = params.pipetteId
83
83
  labware_id = params.labwareId
@@ -122,33 +122,33 @@ class EvotipUnsealPipetteImplementation(
122
122
  )
123
123
 
124
124
  return SuccessData(
125
- public=EvotipUnsealPipetteResult(position=move_result.public.position),
125
+ public=UnsealPipetteFromTipResult(position=move_result.public.position),
126
126
  state_update=move_result.state_update.set_fluid_unknown(
127
127
  pipette_id=pipette_id
128
128
  ).update_pipette_tip_state(pipette_id=params.pipetteId, tip_geometry=None),
129
129
  )
130
130
 
131
131
 
132
- class EvotipUnsealPipette(
132
+ class UnsealPipetteFromTip(
133
133
  BaseCommand[
134
- EvotipUnsealPipetteParams, EvotipUnsealPipetteResult, StallOrCollisionError
134
+ UnsealPipetteFromTipParams, UnsealPipetteFromTipResult, StallOrCollisionError
135
135
  ]
136
136
  ):
137
- """Evotip unseal command model."""
137
+ """Unseal pipette command model."""
138
138
 
139
- commandType: EvotipUnsealPipetteCommandType = "evotipUnsealPipette"
140
- params: EvotipUnsealPipetteParams
141
- result: Optional[EvotipUnsealPipetteResult] = None
139
+ commandType: UnsealPipetteFromTipCommandType = "unsealPipetteFromTip"
140
+ params: UnsealPipetteFromTipParams
141
+ result: Optional[UnsealPipetteFromTipResult] = None
142
142
 
143
143
  _ImplementationCls: Type[
144
- EvotipUnsealPipetteImplementation
145
- ] = EvotipUnsealPipetteImplementation
144
+ UnsealPipetteFromTipImplementation
145
+ ] = UnsealPipetteFromTipImplementation
146
146
 
147
147
 
148
- class EvotipUnsealPipetteCreate(BaseCommandCreate[EvotipUnsealPipetteParams]):
149
- """Evotip unseal command creation request model."""
148
+ class UnsealPipetteFromTipCreate(BaseCommandCreate[UnsealPipetteFromTipParams]):
149
+ """Unseal pipette command creation request model."""
150
150
 
151
- commandType: EvotipUnsealPipetteCommandType = "evotipUnsealPipette"
152
- params: EvotipUnsealPipetteParams
151
+ commandType: UnsealPipetteFromTipCommandType = "unsealPipetteFromTip"
152
+ params: UnsealPipetteFromTipParams
153
153
 
154
- _CommandCls: Type[EvotipUnsealPipette] = EvotipUnsealPipette
154
+ _CommandCls: Type[UnsealPipetteFromTip] = UnsealPipetteFromTip
@@ -246,6 +246,7 @@ class HardwarePipettingHandler(PipettingHandler):
246
246
  flow_rate=flow_rate,
247
247
  volume=adjusted_volume,
248
248
  push_out=push_out,
249
+ is_full_dispense=is_full_dispense,
249
250
  )
250
251
  return adjusted_volume
251
252
 
@@ -53,7 +53,28 @@ def legacy_offset_location_to_offset_location_sequence(
53
53
  cutout_id = deck_configuration_provider.get_cutout_id_by_deck_slot_name(
54
54
  location.slotName
55
55
  )
56
- possible_cutout_fixture_id = location.moduleModel.value
56
+
57
+ # Given a module model, try to figure out the equivalent cutout fixture.
58
+ #
59
+ # The Thermocycler is special. A single Thermocycler is represented in a deck
60
+ # configuration as two separate cutout fixtures, because it spans two separate
61
+ # cutouts. This makes it the only module whose module model string does not map
62
+ # 1:1 with a cutout fixture ID string.
63
+ #
64
+ # TODO(mm, 2025-04-11): This is fragile, and the failure mode when it does the
65
+ # wrong thing can mean labware offsets don't apply, which is pretty bad. We
66
+ # either need a more explicit module<->cutout-fixture mapping, or we need to
67
+ # avoid this mapping entirely.
68
+ if (
69
+ # Check for v2 specifically because v1 is OT-2-only and OT-2s don't have
70
+ # modules in their deck definitions; and v3 does not exist at the time of writing.
71
+ location.moduleModel
72
+ == ModuleModel.THERMOCYCLER_MODULE_V2
73
+ ):
74
+ possible_cutout_fixture_id = "thermocyclerModuleV2Front"
75
+ else:
76
+ possible_cutout_fixture_id = location.moduleModel.value
77
+
57
78
  try:
58
79
  addressable_area = deck_configuration_provider.get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
59
80
  cutout_id, possible_cutout_fixture_id, deck_definition
@@ -144,16 +144,20 @@ def get_deck_slot_for_cutout_id(cutout_id: str) -> DeckSlotName:
144
144
  """Get the corresponding deck slot for an addressable area."""
145
145
  try:
146
146
  return CUTOUT_TO_DECK_SLOT_MAP[cutout_id]
147
- except KeyError:
148
- raise CutoutDoesNotExistError(f"Could not find data for cutout {cutout_id}")
147
+ except KeyError as e:
148
+ raise CutoutDoesNotExistError(
149
+ f"Could not find data for cutout {cutout_id}"
150
+ ) from e
149
151
 
150
152
 
151
153
  def get_cutout_id_by_deck_slot_name(slot_name: DeckSlotName) -> str:
152
154
  """Get the Cutout ID of a given Deck Slot by Deck Slot Name."""
153
155
  try:
154
156
  return DECK_SLOT_TO_CUTOUT_MAP[slot_name]
155
- except KeyError:
156
- raise SlotDoesNotExistError(f"Could not find data for slot {slot_name.value}")
157
+ except KeyError as e:
158
+ raise SlotDoesNotExistError(
159
+ f"Could not find data for slot {slot_name.value}"
160
+ ) from e
157
161
 
158
162
 
159
163
  def get_labware_hosting_addressable_area_name_for_cutout_and_cutout_fixture(
@@ -353,8 +353,9 @@ def _find_volume_in_partial_frustum(
353
353
  section_height=section_height,
354
354
  )
355
355
  # if we've looked through all sections and can't find the target volume, raise an error
356
+ # this code should never be reached- an error should be raised by find_volume_at_well_height
356
357
  raise InvalidLiquidHeightFound(
357
- f"Unable to find volume at given well-height {target_height}."
358
+ f"Target height {target_height} mm exceeds the well height."
358
359
  )
359
360
 
360
361
 
@@ -369,7 +370,9 @@ def find_volume_at_well_height(
369
370
  volumetric_capacity = get_well_volumetric_capacity(well_geometry)
370
371
  max_height = volumetric_capacity[-1][0]
371
372
  if target_height < 0 or target_height > max_height:
372
- raise InvalidLiquidHeightFound("Invalid target height.")
373
+ raise InvalidLiquidHeightFound(
374
+ "Invalid target height {target_height} mm; max well height is {max_height} mm."
375
+ )
373
376
  # volumes in volumetric_capacity are relative to each frustum,
374
377
  # so we have to find the volume of all the full sections enclosed
375
378
  # beneath the target height
@@ -417,9 +420,12 @@ def _find_height_in_partial_frustum(
417
420
  # viewed section
418
421
  bottom_section_volume += section_volume
419
422
 
423
+ # if we finish looping through the whole well, bottom_section will be the well's volume
424
+ total_well_volume = bottom_section_volume
420
425
  # if we've looked through all sections and can't find the target volume, raise an error
426
+ # also this code should never be reached bc an error should be raised by find_height_at_well_volume
421
427
  raise InvalidLiquidHeightFound(
422
- f"Unable to find height at given volume {target_volume}."
428
+ f"Target volume {target_volume} uL exceeds the well volume {total_well_volume} uL."
423
429
  )
424
430
 
425
431
 
@@ -439,7 +445,9 @@ def find_height_at_well_volume(
439
445
 
440
446
  if raise_error_if_result_invalid:
441
447
  if target_volume < 0 or target_volume > max_volume:
442
- raise InvalidLiquidHeightFound("Invalid target volume.")
448
+ raise InvalidLiquidHeightFound(
449
+ f"Invalid target volume {target_volume} uL; max volume is {max_volume} uL"
450
+ )
443
451
 
444
452
  sorted_well = sorted(well_geometry.sections, key=lambda section: section.topHeight)
445
453
  # find the section the target volume is in and compute the height