opentrons 8.4.0a3__py2.py3-none-any.whl → 8.4.0a4__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 (39) 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 +154 -83
  5. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  6. opentrons/protocol_api/core/engine/transfer_components_executor.py +12 -23
  7. opentrons/protocol_api/core/instrument.py +4 -1
  8. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +4 -27
  9. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +4 -1
  10. opentrons/protocol_api/core/well.py +1 -1
  11. opentrons/protocol_api/instrument_context.py +116 -60
  12. opentrons/protocol_api/labware.py +7 -6
  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 +0 -6
  16. opentrons/protocol_engine/commands/command_unions.py +33 -33
  17. opentrons/protocol_engine/commands/dispense_while_tracking.py +1 -6
  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/labware_offset_standardization.py +22 -1
  25. opentrons/protocol_engine/resources/deck_configuration_provider.py +8 -4
  26. opentrons/protocol_engine/state/frustum_helpers.py +12 -4
  27. opentrons/protocol_engine/state/geometry.py +121 -72
  28. opentrons/protocol_engine/state/update_types.py +1 -1
  29. opentrons/protocol_engine/state/wells.py +1 -1
  30. opentrons/protocol_engine/types/__init__.py +6 -0
  31. opentrons/protocol_engine/types/well_position.py +18 -1
  32. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +1 -1
  33. opentrons/protocols/labware.py +23 -18
  34. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/METADATA +4 -4
  35. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/RECORD +39 -39
  36. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/LICENSE +0 -0
  37. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/WHEEL +0 -0
  38. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/entry_points.txt +0 -0
  39. {opentrons-8.4.0a3.dist-info → opentrons-8.4.0a4.dist-info}/top_level.txt +0 -0
@@ -378,28 +378,28 @@ from .liquid_probe import (
378
378
  TryLiquidProbeCommandType,
379
379
  )
380
380
 
381
- from .evotip_seal_pipette import (
382
- EvotipSealPipette,
383
- EvotipSealPipetteParams,
384
- EvotipSealPipetteCreate,
385
- EvotipSealPipetteResult,
386
- EvotipSealPipetteCommandType,
381
+ from .seal_pipette_to_tip import (
382
+ SealPipetteToTip,
383
+ SealPipetteToTipParams,
384
+ SealPipetteToTipCreate,
385
+ SealPipetteToTipResult,
386
+ SealPipetteToTipCommandType,
387
387
  )
388
388
 
389
- from .evotip_dispense import (
390
- EvotipDispense,
391
- EvotipDispenseParams,
392
- EvotipDispenseCreate,
393
- EvotipDispenseResult,
394
- EvotipDispenseCommandType,
389
+ from .pressure_dispense import (
390
+ PressureDispense,
391
+ PressureDispenseParams,
392
+ PressureDispenseCreate,
393
+ PressureDispenseResult,
394
+ PressureDispenseCommandType,
395
395
  )
396
396
 
397
- from .evotip_unseal_pipette import (
398
- EvotipUnsealPipette,
399
- EvotipUnsealPipetteParams,
400
- EvotipUnsealPipetteCreate,
401
- EvotipUnsealPipetteResult,
402
- EvotipUnsealPipetteCommandType,
397
+ from .unseal_pipette_from_tip import (
398
+ UnsealPipetteFromTip,
399
+ UnsealPipetteFromTipParams,
400
+ UnsealPipetteFromTipCreate,
401
+ UnsealPipetteFromTipResult,
402
+ UnsealPipetteFromTipCommandType,
403
403
  )
404
404
 
405
405
  Command = Annotated[
@@ -448,9 +448,9 @@ Command = Annotated[
448
448
  GetNextTip,
449
449
  LiquidProbe,
450
450
  TryLiquidProbe,
451
- EvotipSealPipette,
452
- EvotipDispense,
453
- EvotipUnsealPipette,
451
+ SealPipetteToTip,
452
+ PressureDispense,
453
+ UnsealPipetteFromTip,
454
454
  heater_shaker.WaitForTemperature,
455
455
  heater_shaker.SetTargetTemperature,
456
456
  heater_shaker.DeactivateHeater,
@@ -549,9 +549,9 @@ CommandParams = Union[
549
549
  GetNextTipParams,
550
550
  LiquidProbeParams,
551
551
  TryLiquidProbeParams,
552
- EvotipSealPipetteParams,
553
- EvotipDispenseParams,
554
- EvotipUnsealPipetteParams,
552
+ SealPipetteToTipParams,
553
+ PressureDispenseParams,
554
+ UnsealPipetteFromTipParams,
555
555
  heater_shaker.WaitForTemperatureParams,
556
556
  heater_shaker.SetTargetTemperatureParams,
557
557
  heater_shaker.DeactivateHeaterParams,
@@ -648,9 +648,9 @@ CommandType = Union[
648
648
  GetNextTipCommandType,
649
649
  LiquidProbeCommandType,
650
650
  TryLiquidProbeCommandType,
651
- EvotipSealPipetteCommandType,
652
- EvotipDispenseCommandType,
653
- EvotipUnsealPipetteCommandType,
651
+ SealPipetteToTipCommandType,
652
+ PressureDispenseCommandType,
653
+ UnsealPipetteFromTipCommandType,
654
654
  heater_shaker.WaitForTemperatureCommandType,
655
655
  heater_shaker.SetTargetTemperatureCommandType,
656
656
  heater_shaker.DeactivateHeaterCommandType,
@@ -748,9 +748,9 @@ CommandCreate = Annotated[
748
748
  GetNextTipCreate,
749
749
  LiquidProbeCreate,
750
750
  TryLiquidProbeCreate,
751
- EvotipSealPipetteCreate,
752
- EvotipDispenseCreate,
753
- EvotipUnsealPipetteCreate,
751
+ SealPipetteToTipCreate,
752
+ PressureDispenseCreate,
753
+ UnsealPipetteFromTipCreate,
754
754
  heater_shaker.WaitForTemperatureCreate,
755
755
  heater_shaker.SetTargetTemperatureCreate,
756
756
  heater_shaker.DeactivateHeaterCreate,
@@ -856,9 +856,9 @@ CommandResult = Union[
856
856
  GetNextTipResult,
857
857
  LiquidProbeResult,
858
858
  TryLiquidProbeResult,
859
- EvotipSealPipetteResult,
860
- EvotipDispenseResult,
861
- EvotipUnsealPipetteResult,
859
+ SealPipetteToTipResult,
860
+ PressureDispenseResult,
861
+ UnsealPipetteFromTipResult,
862
862
  heater_shaker.WaitForTemperatureResult,
863
863
  heater_shaker.SetTargetTemperatureResult,
864
864
  heater_shaker.DeactivateHeaterResult,
@@ -101,11 +101,7 @@ class DispenseWhileTrackingImplementation(
101
101
 
102
102
  current_location = self._state_view.pipettes.get_current_location()
103
103
  current_position = await self._gantry_mover.get_position(params.pipetteId)
104
- current_well = CurrentWell(
105
- pipette_id=params.pipetteId,
106
- labware_id=params.labwareId,
107
- well_name=params.wellName,
108
- )
104
+
109
105
  state_update = StateUpdate()
110
106
  move_result = await move_to_well(
111
107
  movement=self._movement,
@@ -114,7 +110,6 @@ class DispenseWhileTrackingImplementation(
114
110
  labware_id=params.labwareId,
115
111
  well_name=params.wellName,
116
112
  well_location=params.wellLocation,
117
- current_well=current_well,
118
113
  operation_volume=-params.volume,
119
114
  )
120
115
  state_update.append(move_result.state_update)
@@ -11,7 +11,12 @@ class LabwareHandlingResultMixin(BaseModel):
11
11
  labwareId: str = Field(..., description="The id of the labware.")
12
12
  locationSequence: LabwareLocationSequence | None = Field(
13
13
  None,
14
- description="The full location down to the deck on which this labware exists.",
14
+ description=(
15
+ "The full location down to the deck on which this labware exists."
16
+ " The reason this can be `null` or omitted is just backwards compatibility,"
17
+ " for older runs and analyses. This should always be present"
18
+ " for new runs and analyses, even for labware whose location is off-deck."
19
+ ),
15
20
  )
16
21
 
17
22
 
@@ -22,8 +22,7 @@ from opentrons_shared_data.errors.exceptions import (
22
22
  PipetteOverpressureError,
23
23
  )
24
24
 
25
- from ..types import DeckPoint
26
- from ..types.liquid_level_detection import LiquidTrackingType
25
+ from ..types import DeckPoint, LiquidTrackingType
27
26
  from .pipetting_common import (
28
27
  LiquidNotFoundError,
29
28
  PipetteIdMixin,
@@ -8,7 +8,7 @@ from .pipetting_common import (
8
8
  PipetteIdMixin,
9
9
  )
10
10
  from .movement_common import (
11
- WellLocationMixin,
11
+ LiquidHandlingWellLocationMixin,
12
12
  MovementMixin,
13
13
  DestinationPositionResult,
14
14
  StallOrCollisionError,
@@ -21,7 +21,6 @@ from .command import (
21
21
  SuccessData,
22
22
  DefinedErrorData,
23
23
  )
24
- from ..errors import LabwareIsTipRackError
25
24
 
26
25
  if TYPE_CHECKING:
27
26
  from ..execution import MovementHandler
@@ -31,7 +30,7 @@ if TYPE_CHECKING:
31
30
  MoveToWellCommandType = Literal["moveToWell"]
32
31
 
33
32
 
34
- class MoveToWellParams(PipetteIdMixin, WellLocationMixin, MovementMixin):
33
+ class MoveToWellParams(PipetteIdMixin, LiquidHandlingWellLocationMixin, MovementMixin):
35
34
  """Payload required to move a pipette to a specific well."""
36
35
 
37
36
  pass
@@ -70,14 +69,9 @@ class MoveToWellImplementation(
70
69
  labware_id = params.labwareId
71
70
  well_name = params.wellName
72
71
  well_location = params.wellLocation
73
-
74
- if (
75
- self._state_view.labware.is_tiprack(labware_id)
76
- and well_location.volumeOffset
77
- ):
78
- raise LabwareIsTipRackError(
79
- "Cannot specify a WellLocation with a volumeOffset with movement to a tip rack"
80
- )
72
+ # TODO(cm): implement move_to_well with meniscus + volume offset
73
+ if well_location.volumeOffset and well_location.volumeOffset != 0:
74
+ raise ValueError("volume offset not supported with MoveToWell")
81
75
 
82
76
  move_result = await move_to_well(
83
77
  model_utils=self._model_utils,
@@ -1,4 +1,4 @@
1
- """Evotip Dispense-in-place command request, result, and implementation models."""
1
+ """Pressure Dispense-in-place command request, result, and implementation models."""
2
2
 
3
3
  from __future__ import annotations
4
4
  from typing import TYPE_CHECKING, Optional, Type, Union
@@ -35,33 +35,33 @@ if TYPE_CHECKING:
35
35
  from ..state.state import StateView
36
36
 
37
37
 
38
- EvotipDispenseCommandType = Literal["evotipDispense"]
38
+ PressureDispenseCommandType = Literal["pressureDispense"]
39
39
 
40
40
 
41
- class EvotipDispenseParams(
41
+ class PressureDispenseParams(
42
42
  PipetteIdMixin, DispenseVolumeMixin, FlowRateMixin, LiquidHandlingWellLocationMixin
43
43
  ):
44
- """Payload required to dispense in place."""
44
+ """Payload required to pressure dispense in place."""
45
45
 
46
46
  pass
47
47
 
48
48
 
49
- class EvotipDispenseResult(BaseLiquidHandlingResult):
50
- """Result data from the execution of a DispenseInPlace command."""
49
+ class PressureDispenseResult(BaseLiquidHandlingResult):
50
+ """Result data from the execution of a PressureDispense command."""
51
51
 
52
52
  pass
53
53
 
54
54
 
55
55
  _ExecuteReturn = Union[
56
- SuccessData[EvotipDispenseResult],
56
+ SuccessData[PressureDispenseResult],
57
57
  DefinedErrorData[StallOrCollisionError],
58
58
  ]
59
59
 
60
60
 
61
- class EvotipDispenseImplementation(
62
- AbstractCommandImpl[EvotipDispenseParams, _ExecuteReturn]
61
+ class PressureDispenseImplementation(
62
+ AbstractCommandImpl[PressureDispenseParams, _ExecuteReturn]
63
63
  ):
64
- """DispenseInPlace command implementation."""
64
+ """Pressure dispense command implementation."""
65
65
 
66
66
  def __init__(
67
67
  self,
@@ -78,8 +78,8 @@ class EvotipDispenseImplementation(
78
78
  self._model_utils = model_utils
79
79
  self._movement = movement
80
80
 
81
- async def execute(self, params: EvotipDispenseParams) -> _ExecuteReturn:
82
- """Move to and dispense to the requested well."""
81
+ async def execute(self, params: PressureDispenseParams) -> _ExecuteReturn:
82
+ """Move to and pressure dispense to the requested well."""
83
83
  well_location = params.wellLocation
84
84
  labware_id = params.labwareId
85
85
  well_name = params.wellName
@@ -121,35 +121,35 @@ class EvotipDispenseImplementation(
121
121
  message="Overpressure Error during Resin Tip Dispense Command."
122
122
  )
123
123
  return SuccessData(
124
- public=EvotipDispenseResult(volume=result.public.volume),
124
+ public=PressureDispenseResult(volume=result.public.volume),
125
125
  state_update=StateUpdate.reduce(
126
126
  move_result.state_update, result.state_update
127
127
  ),
128
128
  )
129
129
 
130
130
 
131
- class EvotipDispense(
131
+ class PressureDispense(
132
132
  BaseCommand[
133
- EvotipDispenseParams,
134
- EvotipDispenseResult,
133
+ PressureDispenseParams,
134
+ PressureDispenseResult,
135
135
  StallOrCollisionError,
136
136
  ]
137
137
  ):
138
- """DispenseInPlace command model."""
138
+ """PressureDispense command model."""
139
139
 
140
- commandType: EvotipDispenseCommandType = "evotipDispense"
141
- params: EvotipDispenseParams
142
- result: Optional[EvotipDispenseResult] = None
140
+ commandType: PressureDispenseCommandType = "pressureDispense"
141
+ params: PressureDispenseParams
142
+ result: Optional[PressureDispenseResult] = None
143
143
 
144
144
  _ImplementationCls: Type[
145
- EvotipDispenseImplementation
146
- ] = EvotipDispenseImplementation
145
+ PressureDispenseImplementation
146
+ ] = PressureDispenseImplementation
147
147
 
148
148
 
149
- class EvotipDispenseCreate(BaseCommandCreate[EvotipDispenseParams]):
150
- """DispenseInPlace command request model."""
149
+ class PressureDispenseCreate(BaseCommandCreate[PressureDispenseParams]):
150
+ """PressureDispense command request model."""
151
151
 
152
- commandType: EvotipDispenseCommandType = "evotipDispense"
153
- params: EvotipDispenseParams
152
+ commandType: PressureDispenseCommandType = "pressureDispense"
153
+ params: PressureDispenseParams
154
154
 
155
- _CommandCls: Type[EvotipDispense] = EvotipDispense
155
+ _CommandCls: Type[PressureDispense] = PressureDispense
@@ -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
@@ -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