opentrons 8.3.2a0__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.2a0.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2a0.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.2a0.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2a0.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -1,16 +1,17 @@
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
5
5
  from typing_extensions import Literal
6
6
 
7
- from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
8
7
  from .pipetting_common import (
9
8
  PipetteIdMixin,
10
9
  FlowRateMixin,
11
10
  DispenseVolumeMixin,
12
11
  BaseLiquidHandlingResult,
13
12
  dispense_in_place,
13
+ increase_evo_disp_count,
14
+ DEFAULT_CORRECTION_VOLUME,
14
15
  )
15
16
  from .movement_common import (
16
17
  LiquidHandlingWellLocationMixin,
@@ -26,7 +27,6 @@ from .command import (
26
27
  DefinedErrorData,
27
28
  )
28
29
  from ..state.update_types import StateUpdate
29
- from ..resources import labware_validation
30
30
  from ..errors import ProtocolEngineError
31
31
 
32
32
  if TYPE_CHECKING:
@@ -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,17 +78,12 @@ 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
86
86
 
87
- labware_definition = self._state_view.labware.get_definition(params.labwareId)
88
- if not labware_validation.is_evotips(labware_definition.parameters.loadName):
89
- raise UnsupportedLabwareForActionError(
90
- f"Cannot use command: `EvotipDispense` with labware: {labware_definition.parameters.loadName}"
91
- )
92
87
  move_result = await move_to_well(
93
88
  movement=self._movement,
94
89
  model_utils=self._model_utils,
@@ -101,6 +96,9 @@ class EvotipDispenseImplementation(
101
96
  return move_result
102
97
 
103
98
  current_position = await self._gantry_mover.get_position(params.pipetteId)
99
+ await increase_evo_disp_count(
100
+ pipette_id=params.pipetteId, pipetting=self._pipetting
101
+ )
104
102
  result = await dispense_in_place(
105
103
  pipette_id=params.pipetteId,
106
104
  volume=params.volume,
@@ -115,6 +113,7 @@ class EvotipDispenseImplementation(
115
113
  },
116
114
  pipetting=self._pipetting,
117
115
  model_utils=self._model_utils,
116
+ correction_volume=params.correctionVolume or DEFAULT_CORRECTION_VOLUME,
118
117
  )
119
118
  if isinstance(result, DefinedErrorData):
120
119
  # TODO (chb, 2025-01-29): Remove this and the OverpressureError returns once disabled for this function
@@ -122,35 +121,35 @@ class EvotipDispenseImplementation(
122
121
  message="Overpressure Error during Resin Tip Dispense Command."
123
122
  )
124
123
  return SuccessData(
125
- public=EvotipDispenseResult(volume=result.public.volume),
124
+ public=PressureDispenseResult(volume=result.public.volume),
126
125
  state_update=StateUpdate.reduce(
127
126
  move_result.state_update, result.state_update
128
127
  ),
129
128
  )
130
129
 
131
130
 
132
- class EvotipDispense(
131
+ class PressureDispense(
133
132
  BaseCommand[
134
- EvotipDispenseParams,
135
- EvotipDispenseResult,
133
+ PressureDispenseParams,
134
+ PressureDispenseResult,
136
135
  StallOrCollisionError,
137
136
  ]
138
137
  ):
139
- """DispenseInPlace command model."""
138
+ """PressureDispense command model."""
140
139
 
141
- commandType: EvotipDispenseCommandType = "evotipDispense"
142
- params: EvotipDispenseParams
143
- result: Optional[EvotipDispenseResult] = None
140
+ commandType: PressureDispenseCommandType = "pressureDispense"
141
+ params: PressureDispenseParams
142
+ result: Optional[PressureDispenseResult] = None
144
143
 
145
144
  _ImplementationCls: Type[
146
- EvotipDispenseImplementation
147
- ] = EvotipDispenseImplementation
145
+ PressureDispenseImplementation
146
+ ] = PressureDispenseImplementation
148
147
 
149
148
 
150
- class EvotipDispenseCreate(BaseCommandCreate[EvotipDispenseParams]):
151
- """DispenseInPlace command request model."""
149
+ class PressureDispenseCreate(BaseCommandCreate[PressureDispenseParams]):
150
+ """PressureDispense command request model."""
152
151
 
153
- commandType: EvotipDispenseCommandType = "evotipDispense"
154
- params: EvotipDispenseParams
152
+ commandType: PressureDispenseCommandType = "pressureDispense"
153
+ params: PressureDispenseParams
155
154
 
156
- _CommandCls: Type[EvotipDispense] = EvotipDispense
155
+ _CommandCls: Type[PressureDispense] = PressureDispense
@@ -1,9 +1,11 @@
1
1
  """Reload labware command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
3
4
  from pydantic import BaseModel, Field
4
5
  from typing import TYPE_CHECKING, Optional, Type
5
6
  from typing_extensions import Literal
6
7
 
8
+ from .labware_handling_common import LabwarePositionResultMixin
7
9
  from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
8
10
  from ..errors.error_occurrence import ErrorOccurrence
9
11
  from ..state.update_types import StateUpdate
@@ -24,27 +26,9 @@ class ReloadLabwareParams(BaseModel):
24
26
  )
25
27
 
26
28
 
27
- class ReloadLabwareResult(BaseModel):
29
+ class ReloadLabwareResult(LabwarePositionResultMixin):
28
30
  """Result data from the execution of a LoadLabware command."""
29
31
 
30
- labwareId: str = Field(
31
- ...,
32
- description="An ID to reference this labware in subsequent commands. Same as the one in the parameters.",
33
- )
34
- offsetId: Optional[str] = Field(
35
- # Default `None` instead of `...` so this field shows up as non-required in
36
- # OpenAPI. The server is allowed to omit it or make it null.
37
- None,
38
- description=(
39
- "An ID referencing the labware offset that will apply"
40
- " to the reloaded labware."
41
- " This offset will be in effect until the labware is moved"
42
- " with a `moveLabware` command."
43
- " Null or undefined means no offset applies,"
44
- " so the default of (0, 0, 0) will be used."
45
- ),
46
- )
47
-
48
32
 
49
33
  class ReloadLabwareImplementation(
50
34
  AbstractCommandImpl[ReloadLabwareParams, SuccessData[ReloadLabwareResult]]
@@ -77,6 +61,9 @@ class ReloadLabwareImplementation(
77
61
  public=ReloadLabwareResult(
78
62
  labwareId=params.labwareId,
79
63
  offsetId=reloaded_labware.offsetId,
64
+ locationSequence=self._state_view.geometry.get_predicted_location_sequence(
65
+ reloaded_labware.location
66
+ ),
80
67
  ),
81
68
  state_update=state_update,
82
69
  )
@@ -1,14 +1,16 @@
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
- from pydantic import Field, BaseModel
5
4
  from typing import TYPE_CHECKING, Optional, Type, Union
6
- from opentrons.types import MountType
7
- from opentrons.protocol_engine.types import MotorAxis
5
+
8
6
  from typing_extensions import Literal
7
+ from pydantic import Field, BaseModel
9
8
 
10
- from opentrons.protocol_engine.errors import UnsupportedLabwareForActionError
11
- from ..resources import ModelUtils, labware_validation
9
+ from opentrons_shared_data.errors.exceptions import PositionUnknownError
10
+
11
+ from opentrons.types import MountType
12
+ from opentrons.protocol_engine.types import MotorAxis
13
+ from ..resources import ModelUtils, ensure_ot3_hardware
12
14
  from ..types import PickUpTipWellLocation, FluidKind, AspiratedFluid
13
15
  from .pipetting_common import (
14
16
  PipetteIdMixin,
@@ -28,7 +30,6 @@ from .command import (
28
30
 
29
31
  from opentrons.hardware_control import HardwareControlAPI
30
32
  from opentrons.hardware_control.types import Axis
31
- from ..state.update_types import StateUpdate
32
33
 
33
34
  if TYPE_CHECKING:
34
35
  from ..state.state import StateView
@@ -40,11 +41,14 @@ if TYPE_CHECKING:
40
41
  )
41
42
 
42
43
 
43
- EvotipSealPipetteCommandType = Literal["evotipSealPipette"]
44
- _PREP_DISTANCE_DEFAULT = 8.25
45
- _PRESS_DISTANCE_DEFAULT = 3.5
46
- _EJECTOR_PUSH_MM_DEFAULT = 7.0
47
- _SAFE_TOP_VOLUME = 400
44
+ SealPipetteToTipCommandType = Literal["sealPipetteToTip"]
45
+ _CAM_PREP_DISTANCE_DEFAULT = 8.25
46
+ _CAM_PRESS_DISTANCE_DEFAULT = 3.5
47
+ _CAM_EJECTOR_PUSH_MM_DEFAULT = 7.0
48
+ _PRESS_FIT_PREP_DISTANCE_DEFAULT = 0
49
+ _PRESS_FIT_PRESS_DISTANCE_DEFAULT = -11.0
50
+ _PRESS_FIT_EJECTOR_PUSH_MM_DEFAULT = 0
51
+ _SAFE_TOP_VOLUME = 1000
48
52
 
49
53
 
50
54
  class TipPickUpParams(BaseModel):
@@ -62,7 +66,7 @@ class TipPickUpParams(BaseModel):
62
66
  )
63
67
 
64
68
 
65
- class EvotipSealPipetteParams(PipetteIdMixin):
69
+ class SealPipetteToTipParams(PipetteIdMixin):
66
70
  """Payload needed to seal resin tips to a pipette."""
67
71
 
68
72
  labwareId: str = Field(..., description="Identifier of labware to use.")
@@ -76,8 +80,8 @@ class EvotipSealPipetteParams(PipetteIdMixin):
76
80
  )
77
81
 
78
82
 
79
- class EvotipSealPipetteResult(DestinationPositionResult):
80
- """Result data from the execution of a EvotipSealPipette."""
83
+ class SealPipetteToTipResult(DestinationPositionResult):
84
+ """Result data from the execution of a SealPipetteToTip."""
81
85
 
82
86
  tipVolume: float = Field(
83
87
  0,
@@ -99,15 +103,15 @@ class EvotipSealPipetteResult(DestinationPositionResult):
99
103
 
100
104
 
101
105
  _ExecuteReturn = Union[
102
- SuccessData[EvotipSealPipetteResult],
106
+ SuccessData[SealPipetteToTipResult],
103
107
  DefinedErrorData[StallOrCollisionError],
104
108
  ]
105
109
 
106
110
 
107
- class EvotipSealPipetteImplementation(
108
- AbstractCommandImpl[EvotipSealPipetteParams, _ExecuteReturn]
111
+ class SealPipetteToTipImplementation(
112
+ AbstractCommandImpl[SealPipetteToTipParams, _ExecuteReturn]
109
113
  ):
110
- """Evotip seal pipette command implementation."""
114
+ """Seal pipette command implementation."""
111
115
 
112
116
  def __init__(
113
117
  self,
@@ -136,25 +140,52 @@ class EvotipSealPipetteImplementation(
136
140
  """A relative press-fit pick up command using gantry moves."""
137
141
  prep_distance = tip_pick_up_params.prepDistance
138
142
  press_distance = tip_pick_up_params.pressDistance
139
- retract_distance = -1 * (prep_distance + press_distance)
143
+ retract_distance = -1 * (press_distance) / 2
140
144
 
141
145
  mount_axis = MotorAxis.LEFT_Z if mount == MountType.LEFT else MotorAxis.RIGHT_Z
142
-
146
+ ot3_hardware_api = ensure_ot3_hardware(self._hardware_api)
143
147
  # TODO chb, 2025-01-29): Factor out the movement constants and relocate this logic into the hardware controller
144
- await self._gantry_mover.move_axes(
145
- axis_map={mount_axis: prep_distance}, speed=10, relative_move=True
148
+ try:
149
+ await self._gantry_mover.move_axes(
150
+ axis_map={mount_axis: prep_distance},
151
+ speed=10,
152
+ relative_move=True,
153
+ expect_stalls=True,
154
+ )
155
+ except PositionUnknownError:
156
+ # if this happens it's from the get position after the move and we can ignore it
157
+ pass
158
+
159
+ await ot3_hardware_api.update_axis_position_estimations(
160
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
146
161
  )
147
162
 
148
163
  # Drive mount down for press-fit
149
- await self._gantry_mover.move_axes(
150
- axis_map={mount_axis: press_distance},
151
- speed=10.0,
152
- relative_move=True,
153
- expect_stalls=True,
164
+ try:
165
+ await self._gantry_mover.move_axes(
166
+ axis_map={mount_axis: press_distance},
167
+ speed=10.0,
168
+ relative_move=True,
169
+ expect_stalls=True,
170
+ )
171
+ except PositionUnknownError:
172
+ # if this happens it's from the get position after the move and we can ignore it
173
+ pass
174
+
175
+ await ot3_hardware_api.update_axis_position_estimations(
176
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
154
177
  )
155
- # retract cam : 11.05
156
- await self._gantry_mover.move_axes(
157
- axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
178
+
179
+ try:
180
+ await self._gantry_mover.move_axes(
181
+ axis_map={mount_axis: retract_distance}, speed=5.5, relative_move=True
182
+ )
183
+ except PositionUnknownError:
184
+ # if this happens it's from the get position after the move and we can ignore it
185
+ pass
186
+
187
+ await ot3_hardware_api.update_axis_position_estimations(
188
+ self._gantry_mover.motor_axes_to_present_hardware_axes([mount_axis])
158
189
  )
159
190
 
160
191
  async def cam_action_relative_pickup_tip(
@@ -210,19 +241,13 @@ class EvotipSealPipetteImplementation(
210
241
  )
211
242
 
212
243
  async def execute(
213
- self, params: EvotipSealPipetteParams
214
- ) -> Union[SuccessData[EvotipSealPipetteResult], _ExecuteReturn]:
244
+ self, params: SealPipetteToTipParams
245
+ ) -> Union[SuccessData[SealPipetteToTipResult], _ExecuteReturn]:
215
246
  """Move to and pick up a tip using the requested pipette."""
216
247
  pipette_id = params.pipetteId
217
248
  labware_id = params.labwareId
218
249
  well_name = params.wellName
219
250
 
220
- labware_definition = self._state_view.labware.get_definition(params.labwareId)
221
- if not labware_validation.is_evotips(labware_definition.parameters.loadName):
222
- raise UnsupportedLabwareForActionError(
223
- f"Cannot use command: `EvotipSealPipette` with labware: {labware_definition.parameters.loadName}"
224
- )
225
-
226
251
  well_location = self._state_view.geometry.convert_pick_up_tip_well_location(
227
252
  well_location=params.wellLocation
228
253
  )
@@ -251,29 +276,28 @@ class EvotipSealPipetteImplementation(
251
276
  channels = self._state_view.tips.get_pipette_active_channels(pipette_id)
252
277
  mount = self._state_view.pipettes.get_mount(pipette_id)
253
278
  tip_pick_up_params = params.tipPickUpParams
254
- if tip_pick_up_params is None:
255
- tip_pick_up_params = TipPickUpParams(
256
- prepDistance=_PREP_DISTANCE_DEFAULT,
257
- pressDistance=_PRESS_DISTANCE_DEFAULT,
258
- ejectorPushMm=_EJECTOR_PUSH_MM_DEFAULT,
259
- )
260
279
 
261
- if channels != 96:
262
- await self.relative_pickup_tip(
263
- tip_pick_up_params=tip_pick_up_params,
264
- mount=mount,
265
- )
266
- elif channels == 96:
280
+ if channels == 96:
281
+ if tip_pick_up_params is None:
282
+ tip_pick_up_params = TipPickUpParams(
283
+ prepDistance=_CAM_PREP_DISTANCE_DEFAULT,
284
+ pressDistance=_CAM_PRESS_DISTANCE_DEFAULT,
285
+ ejectorPushMm=_CAM_EJECTOR_PUSH_MM_DEFAULT,
286
+ )
267
287
  await self.cam_action_relative_pickup_tip(
268
288
  tip_pick_up_params=tip_pick_up_params,
269
289
  mount=mount,
270
290
  )
271
291
  else:
272
- tip_geometry = await self._tip_handler.pick_up_tip(
273
- pipette_id=pipette_id,
274
- labware_id=labware_id,
275
- well_name=well_name,
276
- do_not_ignore_tip_presence=True,
292
+ if tip_pick_up_params is None:
293
+ tip_pick_up_params = TipPickUpParams(
294
+ prepDistance=_PRESS_FIT_PREP_DISTANCE_DEFAULT,
295
+ pressDistance=_PRESS_FIT_PRESS_DISTANCE_DEFAULT,
296
+ ejectorPushMm=_PRESS_FIT_EJECTOR_PUSH_MM_DEFAULT,
297
+ )
298
+ await self.relative_pickup_tip(
299
+ tip_pick_up_params=tip_pick_up_params,
300
+ mount=mount,
277
301
  )
278
302
 
279
303
  # cache_tip
@@ -283,18 +307,15 @@ class EvotipSealPipetteImplementation(
283
307
  if hw_instr is not None:
284
308
  hw_instr.set_current_volume(_SAFE_TOP_VOLUME)
285
309
 
286
- state_update = StateUpdate()
287
- state_update.update_pipette_tip_state(
310
+ state_update = move_result.state_update.update_pipette_tip_state(
288
311
  pipette_id=pipette_id,
289
312
  tip_geometry=tip_geometry,
290
- )
291
-
292
- state_update.set_fluid_aspirated(
313
+ ).set_fluid_aspirated(
293
314
  pipette_id=pipette_id,
294
315
  fluid=AspiratedFluid(kind=FluidKind.LIQUID, volume=_SAFE_TOP_VOLUME),
295
316
  )
296
317
  return SuccessData(
297
- public=EvotipSealPipetteResult(
318
+ public=SealPipetteToTipResult(
298
319
  tipVolume=tip_geometry.volume,
299
320
  tipLength=tip_geometry.length,
300
321
  tipDiameter=tip_geometry.diameter,
@@ -304,28 +325,28 @@ class EvotipSealPipetteImplementation(
304
325
  )
305
326
 
306
327
 
307
- class EvotipSealPipette(
328
+ class SealPipetteToTip(
308
329
  BaseCommand[
309
- EvotipSealPipetteParams,
310
- EvotipSealPipetteResult,
330
+ SealPipetteToTipParams,
331
+ SealPipetteToTipResult,
311
332
  StallOrCollisionError,
312
333
  ]
313
334
  ):
314
- """Seal evotip resin tip command model."""
335
+ """Seal tip command model."""
315
336
 
316
- commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
317
- params: EvotipSealPipetteParams
318
- result: Optional[EvotipSealPipetteResult] = None
337
+ commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
338
+ params: SealPipetteToTipParams
339
+ result: Optional[SealPipetteToTipResult] = None
319
340
 
320
341
  _ImplementationCls: Type[
321
- EvotipSealPipetteImplementation
322
- ] = EvotipSealPipetteImplementation
342
+ SealPipetteToTipImplementation
343
+ ] = SealPipetteToTipImplementation
323
344
 
324
345
 
325
- class EvotipSealPipetteCreate(BaseCommandCreate[EvotipSealPipetteParams]):
326
- """Seal evotip resin tip command creation request model."""
346
+ class SealPipetteToTipCreate(BaseCommandCreate[SealPipetteToTipParams]):
347
+ """Seal tip command creation request model."""
327
348
 
328
- commandType: EvotipSealPipetteCommandType = "evotipSealPipette"
329
- params: EvotipSealPipetteParams
349
+ commandType: SealPipetteToTipCommandType = "sealPipetteToTip"
350
+ params: SealPipetteToTipParams
330
351
 
331
- _CommandCls: Type[EvotipSealPipette] = EvotipSealPipette
352
+ _CommandCls: Type[SealPipetteToTip] = SealPipetteToTip
@@ -69,7 +69,9 @@ class UnsafeBlowOutInPlaceImplementation(
69
69
  )
70
70
  state_update = update_types.StateUpdate()
71
71
  state_update.set_fluid_empty(pipette_id=params.pipetteId)
72
-
72
+ state_update.set_pipette_ready_to_aspirate(
73
+ pipette_id=params.pipetteId, ready_to_aspirate=False
74
+ )
73
75
  return SuccessData(
74
76
  public=UnsafeBlowOutInPlaceResult(), state_update=state_update
75
77
  )
@@ -78,7 +78,12 @@ class UnsafeDropTipInPlaceImplementation(
78
78
  [Axis.of_main_tool_actuator(pipette_location.mount.to_hw_mount())]
79
79
  )
80
80
  await self._tip_handler.drop_tip(
81
- pipette_id=params.pipetteId, home_after=params.homeAfter
81
+ pipette_id=params.pipetteId,
82
+ home_after=params.homeAfter,
83
+ ignore_plunger=(
84
+ self._state_view.tips.get_pipette_active_channels(params.pipetteId)
85
+ == 96
86
+ ),
82
87
  )
83
88
 
84
89
  state_update = StateUpdate()