opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a1__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. opentrons/calibration_storage/deck_configuration.py +3 -3
  2. opentrons/calibration_storage/file_operators.py +3 -3
  3. opentrons/calibration_storage/helpers.py +3 -1
  4. opentrons/calibration_storage/ot2/models/v1.py +16 -29
  5. opentrons/calibration_storage/ot2/tip_length.py +7 -4
  6. opentrons/calibration_storage/ot3/models/v1.py +14 -23
  7. opentrons/cli/analyze.py +18 -6
  8. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  9. opentrons/drivers/asyncio/communication/errors.py +16 -3
  10. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  11. opentrons/drivers/command_builder.py +2 -2
  12. opentrons/drivers/flex_stacker/__init__.py +9 -0
  13. opentrons/drivers/flex_stacker/abstract.py +89 -0
  14. opentrons/drivers/flex_stacker/driver.py +260 -0
  15. opentrons/drivers/flex_stacker/simulator.py +109 -0
  16. opentrons/drivers/flex_stacker/types.py +138 -0
  17. opentrons/drivers/heater_shaker/driver.py +18 -3
  18. opentrons/drivers/temp_deck/driver.py +13 -3
  19. opentrons/drivers/thermocycler/driver.py +17 -3
  20. opentrons/execute.py +3 -1
  21. opentrons/hardware_control/__init__.py +1 -2
  22. opentrons/hardware_control/api.py +28 -20
  23. opentrons/hardware_control/backends/flex_protocol.py +4 -6
  24. opentrons/hardware_control/backends/ot3controller.py +177 -59
  25. opentrons/hardware_control/backends/ot3simulator.py +10 -8
  26. opentrons/hardware_control/backends/ot3utils.py +3 -13
  27. opentrons/hardware_control/dev_types.py +2 -0
  28. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  29. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  30. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  31. opentrons/hardware_control/emulation/settings.py +3 -4
  32. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  33. opentrons/hardware_control/instruments/ot2/pipette.py +9 -21
  34. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  35. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  36. opentrons/hardware_control/instruments/ot3/pipette.py +13 -22
  37. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  38. opentrons/hardware_control/modules/mod_abc.py +2 -2
  39. opentrons/hardware_control/motion_utilities.py +68 -0
  40. opentrons/hardware_control/nozzle_manager.py +39 -41
  41. opentrons/hardware_control/ot3_calibration.py +1 -1
  42. opentrons/hardware_control/ot3api.py +34 -22
  43. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  44. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  45. opentrons/hardware_control/protocols/liquid_handler.py +18 -0
  46. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  47. opentrons/hardware_control/robot_calibration.py +1 -1
  48. opentrons/hardware_control/types.py +61 -0
  49. opentrons/protocol_api/__init__.py +20 -1
  50. opentrons/protocol_api/_liquid.py +24 -49
  51. opentrons/protocol_api/_liquid_properties.py +754 -0
  52. opentrons/protocol_api/_types.py +24 -0
  53. opentrons/protocol_api/core/common.py +2 -0
  54. opentrons/protocol_api/core/engine/instrument.py +67 -10
  55. opentrons/protocol_api/core/engine/labware.py +29 -7
  56. opentrons/protocol_api/core/engine/protocol.py +130 -5
  57. opentrons/protocol_api/core/engine/robot.py +139 -0
  58. opentrons/protocol_api/core/engine/well.py +4 -1
  59. opentrons/protocol_api/core/instrument.py +42 -4
  60. opentrons/protocol_api/core/labware.py +13 -4
  61. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +34 -3
  62. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  63. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  64. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  65. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +34 -3
  66. opentrons/protocol_api/core/protocol.py +34 -1
  67. opentrons/protocol_api/core/robot.py +51 -0
  68. opentrons/protocol_api/instrument_context.py +145 -43
  69. opentrons/protocol_api/labware.py +231 -7
  70. opentrons/protocol_api/module_contexts.py +21 -17
  71. opentrons/protocol_api/protocol_context.py +125 -4
  72. opentrons/protocol_api/robot_context.py +204 -32
  73. opentrons/protocol_api/validation.py +261 -3
  74. opentrons/protocol_engine/__init__.py +4 -0
  75. opentrons/protocol_engine/actions/actions.py +2 -3
  76. opentrons/protocol_engine/clients/sync_client.py +18 -0
  77. opentrons/protocol_engine/commands/__init__.py +81 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  79. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  80. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  81. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  82. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  83. opentrons/protocol_engine/commands/aspirate.py +103 -53
  84. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  85. opentrons/protocol_engine/commands/blow_out.py +44 -39
  86. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  87. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  88. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  89. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  90. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  91. opentrons/protocol_engine/commands/command.py +73 -66
  92. opentrons/protocol_engine/commands/command_unions.py +101 -1
  93. opentrons/protocol_engine/commands/comment.py +1 -1
  94. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  95. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  96. opentrons/protocol_engine/commands/custom.py +6 -12
  97. opentrons/protocol_engine/commands/dispense.py +82 -48
  98. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  99. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  100. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  101. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  102. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  103. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  104. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  111. opentrons/protocol_engine/commands/home.py +13 -4
  112. opentrons/protocol_engine/commands/liquid_probe.py +60 -25
  113. opentrons/protocol_engine/commands/load_labware.py +29 -7
  114. opentrons/protocol_engine/commands/load_lid.py +146 -0
  115. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  116. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  117. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  118. opentrons/protocol_engine/commands/load_module.py +31 -10
  119. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  120. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  121. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  122. opentrons/protocol_engine/commands/move_labware.py +19 -6
  123. opentrons/protocol_engine/commands/move_relative.py +35 -25
  124. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  125. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  126. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  127. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  128. opentrons/protocol_engine/commands/movement_common.py +338 -0
  129. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  130. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  131. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  132. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  133. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  134. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  135. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  136. opentrons/protocol_engine/commands/robot/common.py +18 -0
  137. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  139. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  140. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  141. opentrons/protocol_engine/commands/save_position.py +14 -5
  142. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  143. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  144. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  147. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  148. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  152. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  153. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  154. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  155. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  158. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  159. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  160. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  161. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  162. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  163. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  164. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  165. opentrons/protocol_engine/errors/__init__.py +8 -0
  166. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  167. opentrons/protocol_engine/errors/exceptions.py +50 -0
  168. opentrons/protocol_engine/execution/command_executor.py +1 -1
  169. opentrons/protocol_engine/execution/equipment.py +73 -5
  170. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  171. opentrons/protocol_engine/execution/movement.py +27 -0
  172. opentrons/protocol_engine/execution/pipetting.py +5 -1
  173. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  174. opentrons/protocol_engine/notes/notes.py +1 -1
  175. opentrons/protocol_engine/protocol_engine.py +7 -6
  176. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  177. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  178. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  179. opentrons/protocol_engine/resources/pipette_data_provider.py +12 -0
  180. opentrons/protocol_engine/slot_standardization.py +9 -9
  181. opentrons/protocol_engine/state/_move_types.py +9 -5
  182. opentrons/protocol_engine/state/_well_math.py +193 -0
  183. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  184. opentrons/protocol_engine/state/command_history.py +12 -0
  185. opentrons/protocol_engine/state/commands.py +17 -13
  186. opentrons/protocol_engine/state/files.py +10 -12
  187. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  188. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  189. opentrons/protocol_engine/state/geometry.py +47 -1
  190. opentrons/protocol_engine/state/labware.py +79 -25
  191. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  192. opentrons/protocol_engine/state/liquids.py +16 -4
  193. opentrons/protocol_engine/state/modules.py +52 -70
  194. opentrons/protocol_engine/state/motion.py +6 -1
  195. opentrons/protocol_engine/state/pipettes.py +135 -58
  196. opentrons/protocol_engine/state/state.py +21 -2
  197. opentrons/protocol_engine/state/state_summary.py +4 -2
  198. opentrons/protocol_engine/state/tips.py +11 -44
  199. opentrons/protocol_engine/state/update_types.py +343 -48
  200. opentrons/protocol_engine/state/wells.py +19 -11
  201. opentrons/protocol_engine/types.py +176 -28
  202. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  203. opentrons/protocol_reader/file_format_validator.py +5 -5
  204. opentrons/protocol_runner/json_file_reader.py +9 -3
  205. opentrons/protocol_runner/json_translator.py +51 -25
  206. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  207. opentrons/protocol_runner/protocol_runner.py +35 -4
  208. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  209. opentrons/protocol_runner/run_orchestrator.py +13 -3
  210. opentrons/protocols/advanced_control/common.py +38 -0
  211. opentrons/protocols/advanced_control/mix.py +1 -1
  212. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  213. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  214. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  215. opentrons/protocols/api_support/definitions.py +1 -1
  216. opentrons/protocols/api_support/instrument.py +1 -1
  217. opentrons/protocols/api_support/util.py +10 -0
  218. opentrons/protocols/labware.py +39 -6
  219. opentrons/protocols/models/json_protocol.py +5 -9
  220. opentrons/simulate.py +3 -1
  221. opentrons/types.py +162 -2
  222. opentrons/util/logging_config.py +1 -1
  223. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  224. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +228 -201
  225. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  226. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  227. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  228. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,11 @@
1
1
  """Command models to read absorbance."""
2
2
  from __future__ import annotations
3
3
  from datetime import datetime
4
- from typing import Optional, Dict, TYPE_CHECKING, List
5
- from typing_extensions import Literal, Type
4
+ from typing import Optional, Dict, TYPE_CHECKING, List, Any
6
5
 
6
+ from typing_extensions import Literal, Type
7
7
  from pydantic import BaseModel, Field
8
+ from pydantic.json_schema import SkipJsonSchema
8
9
 
9
10
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
10
11
  from ...errors import CannotPerformModuleAction, StorageLimitReachedError
@@ -16,12 +17,17 @@ from ...resources.file_provider import (
16
17
  MAXIMUM_CSV_FILE_LIMIT,
17
18
  )
18
19
  from ...resources import FileProvider
20
+ from ...state import update_types
19
21
 
20
22
  if TYPE_CHECKING:
21
23
  from opentrons.protocol_engine.state.state import StateView
22
24
  from opentrons.protocol_engine.execution import EquipmentHandler
23
25
 
24
26
 
27
+ def _remove_default(s: dict[str, Any]) -> None:
28
+ s.pop("default", None)
29
+
30
+
25
31
  ReadAbsorbanceCommandType = Literal["absorbanceReader/read"]
26
32
 
27
33
 
@@ -29,9 +35,10 @@ class ReadAbsorbanceParams(BaseModel):
29
35
  """Input parameters for an absorbance reading."""
30
36
 
31
37
  moduleId: str = Field(..., description="Unique ID of the Absorbance Reader.")
32
- fileName: Optional[str] = Field(
38
+ fileName: str | SkipJsonSchema[None] = Field(
33
39
  None,
34
40
  description="Optional file name to use when storing the results of a measurement.",
41
+ json_schema_extra=_remove_default,
35
42
  )
36
43
 
37
44
 
@@ -67,6 +74,7 @@ class ReadAbsorbanceImpl(
67
74
  self, params: ReadAbsorbanceParams
68
75
  ) -> SuccessData[ReadAbsorbanceResult]:
69
76
  """Initiate an absorbance measurement."""
77
+ state_update = update_types.StateUpdate()
70
78
  abs_reader_substate = self._state_view.modules.get_absorbance_reader_substate(
71
79
  module_id=params.moduleId
72
80
  )
@@ -117,7 +125,9 @@ class ReadAbsorbanceImpl(
117
125
  )
118
126
  asbsorbance_result[wavelength] = converted_values
119
127
  transform_results.append(
120
- ReadData.construct(wavelength=wavelength, data=converted_values)
128
+ ReadData.model_construct(
129
+ wavelength=wavelength, data=converted_values
130
+ )
121
131
  )
122
132
  # Handle the virtual module case for data creation (all zeroes)
123
133
  elif self._state_view.config.use_virtual_modules:
@@ -131,22 +141,27 @@ class ReadAbsorbanceImpl(
131
141
  )
132
142
  asbsorbance_result[wavelength] = converted_values
133
143
  transform_results.append(
134
- ReadData.construct(wavelength=wavelength, data=converted_values)
144
+ ReadData.model_construct(
145
+ wavelength=wavelength, data=converted_values
146
+ )
135
147
  )
136
148
  else:
137
149
  raise CannotPerformModuleAction(
138
150
  "Plate Reader data cannot be requested with a module that has not been initialized."
139
151
  )
140
152
 
153
+ state_update.set_absorbance_reader_data(
154
+ module_id=abs_reader_substate.module_id, read_result=asbsorbance_result
155
+ )
141
156
  # TODO (cb, 10-17-2024): FILE PROVIDER - Some day we may want to break the file provider behavior into a seperate API function.
142
157
  # When this happens, we probably will to have the change the command results handler we utilize to track file IDs in engine.
143
158
  # Today, the action handler for the FileStore looks for a ReadAbsorbanceResult command action, this will need to be delinked.
144
159
 
145
160
  # Begin interfacing with the file provider if the user provided a filename
146
- file_ids = []
161
+ file_ids: list[str] = []
147
162
  if params.fileName is not None:
148
163
  # Create the Plate Reader Transform
149
- plate_read_result = PlateReaderData.construct(
164
+ plate_read_result = PlateReaderData.model_construct(
150
165
  read_results=transform_results,
151
166
  reference_wavelength=abs_reader_substate.reference_wavelength,
152
167
  start_time=start_time,
@@ -170,12 +185,20 @@ class ReadAbsorbanceImpl(
170
185
  # Return success data to api
171
186
  return SuccessData(
172
187
  public=ReadAbsorbanceResult(
173
- data=asbsorbance_result, fileIds=file_ids
188
+ data=asbsorbance_result,
189
+ fileIds=file_ids,
174
190
  ),
191
+ state_update=state_update,
175
192
  )
176
193
 
194
+ state_update.files_added = update_types.FilesAddedUpdate(file_ids=file_ids)
195
+
177
196
  return SuccessData(
178
- public=ReadAbsorbanceResult(data=asbsorbance_result, fileIds=file_ids),
197
+ public=ReadAbsorbanceResult(
198
+ data=asbsorbance_result,
199
+ fileIds=file_ids,
200
+ ),
201
+ state_update=state_update,
179
202
  )
180
203
 
181
204
 
@@ -0,0 +1,160 @@
1
+ """AirGap in place command request, result, and implementation models."""
2
+
3
+ from __future__ import annotations
4
+ from typing import TYPE_CHECKING, Optional, Type, Union
5
+ from typing_extensions import Literal
6
+
7
+ from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
8
+
9
+ from opentrons.hardware_control import HardwareControlAPI
10
+
11
+ from .pipetting_common import (
12
+ PipetteIdMixin,
13
+ AspirateVolumeMixin,
14
+ FlowRateMixin,
15
+ BaseLiquidHandlingResult,
16
+ OverpressureError,
17
+ )
18
+ from .command import (
19
+ AbstractCommandImpl,
20
+ BaseCommand,
21
+ BaseCommandCreate,
22
+ SuccessData,
23
+ DefinedErrorData,
24
+ )
25
+ from ..errors.error_occurrence import ErrorOccurrence
26
+ from ..errors.exceptions import PipetteNotReadyToAspirateError
27
+ from ..state.update_types import StateUpdate
28
+ from ..types import AspiratedFluid, FluidKind
29
+
30
+ if TYPE_CHECKING:
31
+ from ..execution import PipettingHandler, GantryMover
32
+ from ..resources import ModelUtils
33
+ from ..state.state import StateView
34
+ from ..notes import CommandNoteAdder
35
+
36
+ AirGapInPlaceCommandType = Literal["airGapInPlace"]
37
+
38
+
39
+ class AirGapInPlaceParams(PipetteIdMixin, AspirateVolumeMixin, FlowRateMixin):
40
+ """Payload required to air gap in place."""
41
+
42
+ pass
43
+
44
+
45
+ class AirGapInPlaceResult(BaseLiquidHandlingResult):
46
+ """Result data from the execution of a AirGapInPlace command."""
47
+
48
+ pass
49
+
50
+
51
+ _ExecuteReturn = Union[
52
+ SuccessData[AirGapInPlaceResult],
53
+ DefinedErrorData[OverpressureError],
54
+ ]
55
+
56
+
57
+ class AirGapInPlaceImplementation(
58
+ AbstractCommandImpl[AirGapInPlaceParams, _ExecuteReturn]
59
+ ):
60
+ """AirGapInPlace command implementation."""
61
+
62
+ def __init__(
63
+ self,
64
+ pipetting: PipettingHandler,
65
+ hardware_api: HardwareControlAPI,
66
+ state_view: StateView,
67
+ command_note_adder: CommandNoteAdder,
68
+ model_utils: ModelUtils,
69
+ gantry_mover: GantryMover,
70
+ **kwargs: object,
71
+ ) -> None:
72
+ self._pipetting = pipetting
73
+ self._state_view = state_view
74
+ self._hardware_api = hardware_api
75
+ self._command_note_adder = command_note_adder
76
+ self._model_utils = model_utils
77
+ self._gantry_mover = gantry_mover
78
+
79
+ async def execute(self, params: AirGapInPlaceParams) -> _ExecuteReturn:
80
+ """Air gap without moving the pipette.
81
+
82
+ Raises:
83
+ TipNotAttachedError: if no tip is attached to the pipette.
84
+ PipetteNotReadyToAirGapError: pipette plunger is not ready.
85
+ """
86
+ ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
87
+ pipette_id=params.pipetteId,
88
+ )
89
+
90
+ if not ready_to_aspirate:
91
+ raise PipetteNotReadyToAspirateError(
92
+ "Pipette cannot air gap in place because of a previous blow out."
93
+ " The first aspirate following a blow-out must be from a specific well"
94
+ " so the plunger can be reset in a known safe position."
95
+ )
96
+
97
+ state_update = StateUpdate()
98
+
99
+ try:
100
+ current_position = await self._gantry_mover.get_position(params.pipetteId)
101
+ volume = await self._pipetting.aspirate_in_place(
102
+ pipette_id=params.pipetteId,
103
+ volume=params.volume,
104
+ flow_rate=params.flowRate,
105
+ command_note_adder=self._command_note_adder,
106
+ )
107
+ except PipetteOverpressureError as e:
108
+ return DefinedErrorData(
109
+ public=OverpressureError(
110
+ id=self._model_utils.generate_id(),
111
+ createdAt=self._model_utils.get_timestamp(),
112
+ wrappedErrors=[
113
+ ErrorOccurrence.from_failed(
114
+ id=self._model_utils.generate_id(),
115
+ createdAt=self._model_utils.get_timestamp(),
116
+ error=e,
117
+ )
118
+ ],
119
+ errorInfo=(
120
+ {
121
+ "retryLocation": (
122
+ current_position.x,
123
+ current_position.y,
124
+ current_position.z,
125
+ )
126
+ }
127
+ ),
128
+ ),
129
+ state_update=state_update,
130
+ )
131
+ else:
132
+ state_update.set_fluid_aspirated(
133
+ pipette_id=params.pipetteId,
134
+ fluid=AspiratedFluid(kind=FluidKind.AIR, volume=volume),
135
+ )
136
+ return SuccessData(
137
+ public=AirGapInPlaceResult(volume=volume),
138
+ state_update=state_update,
139
+ )
140
+
141
+
142
+ class AirGapInPlace(
143
+ BaseCommand[AirGapInPlaceParams, AirGapInPlaceResult, OverpressureError]
144
+ ):
145
+ """AirGapInPlace command model."""
146
+
147
+ commandType: AirGapInPlaceCommandType = "airGapInPlace"
148
+ params: AirGapInPlaceParams
149
+ result: Optional[AirGapInPlaceResult]
150
+
151
+ _ImplementationCls: Type[AirGapInPlaceImplementation] = AirGapInPlaceImplementation
152
+
153
+
154
+ class AirGapInPlaceCreate(BaseCommandCreate[AirGapInPlaceParams]):
155
+ """AirGapInPlace command request model."""
156
+
157
+ commandType: AirGapInPlaceCommandType = "airGapInPlace"
158
+ params: AirGapInPlaceParams
159
+
160
+ _CommandCls: Type[AirGapInPlace] = AirGapInPlace
@@ -1,7 +1,7 @@
1
1
  """Aspirate command request, result, and implementation models."""
2
+
2
3
  from __future__ import annotations
3
4
  from typing import TYPE_CHECKING, Optional, Type, Union
4
- from opentrons_shared_data.errors.exceptions import PipetteOverpressureError
5
5
  from typing_extensions import Literal
6
6
 
7
7
  from .pipetting_common import (
@@ -9,9 +9,15 @@ from .pipetting_common import (
9
9
  PipetteIdMixin,
10
10
  AspirateVolumeMixin,
11
11
  FlowRateMixin,
12
- LiquidHandlingWellLocationMixin,
13
12
  BaseLiquidHandlingResult,
13
+ aspirate_in_place,
14
+ prepare_for_aspirate,
15
+ )
16
+ from .movement_common import (
17
+ LiquidHandlingWellLocationMixin,
14
18
  DestinationPositionResult,
19
+ StallOrCollisionError,
20
+ move_to_well,
15
21
  )
16
22
  from .command import (
17
23
  AbstractCommandImpl,
@@ -20,12 +26,15 @@ from .command import (
20
26
  DefinedErrorData,
21
27
  SuccessData,
22
28
  )
23
- from ..errors.error_occurrence import ErrorOccurrence
24
29
 
25
30
  from opentrons.hardware_control import HardwareControlAPI
26
31
 
27
32
  from ..state.update_types import StateUpdate, CLEAR
28
- from ..types import WellLocation, WellOrigin, CurrentWell, DeckPoint
33
+ from ..types import (
34
+ WellLocation,
35
+ WellOrigin,
36
+ CurrentWell,
37
+ )
29
38
 
30
39
  if TYPE_CHECKING:
31
40
  from ..execution import MovementHandler, PipettingHandler
@@ -53,7 +62,7 @@ class AspirateResult(BaseLiquidHandlingResult, DestinationPositionResult):
53
62
 
54
63
  _ExecuteReturn = Union[
55
64
  SuccessData[AspirateResult],
56
- DefinedErrorData[OverpressureError],
65
+ DefinedErrorData[OverpressureError] | DefinedErrorData[StallOrCollisionError],
57
66
  ]
58
67
 
59
68
 
@@ -86,23 +95,51 @@ class AspirateImplementation(AbstractCommandImpl[AspirateParams, _ExecuteReturn]
86
95
  pipette_id = params.pipetteId
87
96
  labware_id = params.labwareId
88
97
  well_name = params.wellName
98
+ well_location = params.wellLocation
99
+
100
+ state_update = StateUpdate()
101
+
102
+ final_location = self._state_view.geometry.get_well_position(
103
+ labware_id=labware_id,
104
+ well_name=well_name,
105
+ well_location=well_location,
106
+ operation_volume=-params.volume,
107
+ pipette_id=pipette_id,
108
+ )
89
109
 
90
110
  ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
91
111
  pipette_id=pipette_id
92
112
  )
93
113
 
94
114
  current_well = None
95
- state_update = StateUpdate()
96
115
 
97
116
  if not ready_to_aspirate:
98
- await self._movement.move_to_well(
117
+ move_result = await move_to_well(
118
+ movement=self._movement,
119
+ model_utils=self._model_utils,
99
120
  pipette_id=pipette_id,
100
121
  labware_id=labware_id,
101
122
  well_name=well_name,
102
123
  well_location=WellLocation(origin=WellOrigin.TOP),
103
124
  )
125
+ state_update.append(move_result.state_update)
126
+ if isinstance(move_result, DefinedErrorData):
127
+ return DefinedErrorData(move_result.public, state_update=state_update)
104
128
 
105
- await self._pipetting.prepare_for_aspirate(pipette_id=pipette_id)
129
+ prepare_result = await prepare_for_aspirate(
130
+ pipette_id=pipette_id,
131
+ pipetting=self._pipetting,
132
+ model_utils=self._model_utils,
133
+ # Note that the retryLocation is the final location, inside the liquid,
134
+ # because that's where we'd want the client to try re-aspirating if this
135
+ # command fails and the run enters error recovery.
136
+ location_if_error={"retryLocation": final_location},
137
+ )
138
+ state_update.append(prepare_result.state_update)
139
+ if isinstance(prepare_result, DefinedErrorData):
140
+ return DefinedErrorData(
141
+ public=prepare_result.public, state_update=state_update
142
+ )
106
143
 
107
144
  # set our current deck location to the well now that we've made
108
145
  # an intermediate move for the "prepare for aspirate" step
@@ -112,71 +149,84 @@ class AspirateImplementation(AbstractCommandImpl[AspirateParams, _ExecuteReturn]
112
149
  well_name=well_name,
113
150
  )
114
151
 
115
- position = await self._movement.move_to_well(
152
+ move_result = await move_to_well(
153
+ movement=self._movement,
154
+ model_utils=self._model_utils,
116
155
  pipette_id=pipette_id,
117
156
  labware_id=labware_id,
118
157
  well_name=well_name,
119
- well_location=params.wellLocation,
158
+ well_location=well_location,
120
159
  current_well=current_well,
121
160
  operation_volume=-params.volume,
122
161
  )
123
- deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
124
- state_update.set_pipette_location(
162
+ state_update.append(move_result.state_update)
163
+ if isinstance(move_result, DefinedErrorData):
164
+ return DefinedErrorData(
165
+ public=move_result.public, state_update=state_update
166
+ )
167
+
168
+ aspirate_result = await aspirate_in_place(
125
169
  pipette_id=pipette_id,
126
- new_labware_id=labware_id,
127
- new_well_name=well_name,
128
- new_deck_point=deck_point,
170
+ volume=params.volume,
171
+ flow_rate=params.flowRate,
172
+ location_if_error={
173
+ "retryLocation": (
174
+ move_result.public.position.x,
175
+ move_result.public.position.y,
176
+ move_result.public.position.z,
177
+ )
178
+ },
179
+ command_note_adder=self._command_note_adder,
180
+ pipetting=self._pipetting,
181
+ model_utils=self._model_utils,
129
182
  )
130
-
131
- try:
132
- volume_aspirated = await self._pipetting.aspirate_in_place(
133
- pipette_id=pipette_id,
134
- volume=params.volume,
135
- flow_rate=params.flowRate,
136
- command_note_adder=self._command_note_adder,
137
- )
138
- except PipetteOverpressureError as e:
183
+ state_update.append(aspirate_result.state_update)
184
+ if isinstance(aspirate_result, DefinedErrorData):
139
185
  state_update.set_liquid_operated(
140
186
  labware_id=labware_id,
141
- well_name=well_name,
187
+ well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
188
+ labware_id,
189
+ well_name,
190
+ params.pipetteId,
191
+ ),
142
192
  volume_added=CLEAR,
143
193
  )
144
194
  return DefinedErrorData(
145
- public=OverpressureError(
146
- id=self._model_utils.generate_id(),
147
- createdAt=self._model_utils.get_timestamp(),
148
- wrappedErrors=[
149
- ErrorOccurrence.from_failed(
150
- id=self._model_utils.generate_id(),
151
- createdAt=self._model_utils.get_timestamp(),
152
- error=e,
153
- )
154
- ],
155
- errorInfo={"retryLocation": (position.x, position.y, position.z)},
156
- ),
157
- state_update=state_update,
158
- )
159
- else:
160
- state_update.set_liquid_operated(
161
- labware_id=labware_id,
162
- well_name=well_name,
163
- volume_added=-volume_aspirated,
164
- )
165
- return SuccessData(
166
- public=AspirateResult(
167
- volume=volume_aspirated,
168
- position=deck_point,
169
- ),
170
- state_update=state_update,
195
+ public=aspirate_result.public, state_update=state_update
171
196
  )
172
197
 
198
+ state_update.set_liquid_operated(
199
+ labware_id=labware_id,
200
+ well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
201
+ labware_id, well_name, pipette_id
202
+ ),
203
+ volume_added=-aspirate_result.public.volume
204
+ * self._state_view.geometry.get_nozzles_per_well(
205
+ labware_id,
206
+ well_name,
207
+ params.pipetteId,
208
+ ),
209
+ )
173
210
 
174
- class Aspirate(BaseCommand[AspirateParams, AspirateResult, OverpressureError]):
211
+ return SuccessData(
212
+ public=AspirateResult(
213
+ volume=aspirate_result.public.volume,
214
+ position=move_result.public.position,
215
+ ),
216
+ state_update=state_update,
217
+ )
218
+
219
+
220
+ class Aspirate(
221
+ BaseCommand[
222
+ AspirateParams, AspirateResult, OverpressureError | StallOrCollisionError
223
+ ]
224
+ ):
175
225
  """Aspirate command model."""
176
226
 
177
227
  commandType: AspirateCommandType = "aspirate"
178
228
  params: AspirateParams
179
- result: Optional[AspirateResult]
229
+ result: Optional[AspirateResult] = None
180
230
 
181
231
  _ImplementationCls: Type[AspirateImplementation] = AspirateImplementation
182
232
 
@@ -4,8 +4,6 @@ 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_shared_data.errors.exceptions import PipetteOverpressureError
8
-
9
7
  from opentrons.hardware_control import HardwareControlAPI
10
8
 
11
9
  from .pipetting_common import (
@@ -14,6 +12,7 @@ from .pipetting_common import (
14
12
  FlowRateMixin,
15
13
  BaseLiquidHandlingResult,
16
14
  OverpressureError,
15
+ aspirate_in_place,
17
16
  )
18
17
  from .command import (
19
18
  AbstractCommandImpl,
@@ -22,9 +21,8 @@ from .command import (
22
21
  SuccessData,
23
22
  DefinedErrorData,
24
23
  )
25
- from ..errors.error_occurrence import ErrorOccurrence
26
24
  from ..errors.exceptions import PipetteNotReadyToAspirateError
27
- from ..state.update_types import StateUpdate, CLEAR
25
+ from ..state.update_types import CLEAR
28
26
  from ..types import CurrentWell
29
27
 
30
28
  if TYPE_CHECKING:
@@ -83,8 +81,6 @@ class AspirateInPlaceImplementation(
83
81
  TipNotAttachedError: if no tip is attached to the pipette.
84
82
  PipetteNotReadyToAspirateError: pipette plunger is not ready.
85
83
  """
86
- state_update = StateUpdate()
87
-
88
84
  ready_to_aspirate = self._pipetting.get_is_ready_to_aspirate(
89
85
  pipette_id=params.pipetteId,
90
86
  )
@@ -95,63 +91,71 @@ class AspirateInPlaceImplementation(
95
91
  " so the plunger can be reset in a known safe position."
96
92
  )
97
93
 
98
- current_location = self._state_view.pipettes.get_current_location()
99
94
  current_position = await self._gantry_mover.get_position(params.pipetteId)
95
+ current_location = self._state_view.pipettes.get_current_location()
100
96
 
101
- try:
102
- volume = await self._pipetting.aspirate_in_place(
103
- pipette_id=params.pipetteId,
104
- volume=params.volume,
105
- flow_rate=params.flowRate,
106
- command_note_adder=self._command_note_adder,
107
- )
108
- except PipetteOverpressureError as e:
97
+ result = await aspirate_in_place(
98
+ pipette_id=params.pipetteId,
99
+ volume=params.volume,
100
+ flow_rate=params.flowRate,
101
+ location_if_error={
102
+ "retryLocation": (
103
+ current_position.x,
104
+ current_position.y,
105
+ current_position.z,
106
+ )
107
+ },
108
+ command_note_adder=self._command_note_adder,
109
+ pipetting=self._pipetting,
110
+ model_utils=self._model_utils,
111
+ )
112
+ if isinstance(result, DefinedErrorData):
109
113
  if (
110
114
  isinstance(current_location, CurrentWell)
111
115
  and current_location.pipette_id == params.pipetteId
112
116
  ):
113
- state_update.set_liquid_operated(
114
- labware_id=current_location.labware_id,
115
- well_name=current_location.well_name,
116
- volume_added=CLEAR,
117
- )
118
- return DefinedErrorData(
119
- public=OverpressureError(
120
- id=self._model_utils.generate_id(),
121
- createdAt=self._model_utils.get_timestamp(),
122
- wrappedErrors=[
123
- ErrorOccurrence.from_failed(
124
- id=self._model_utils.generate_id(),
125
- createdAt=self._model_utils.get_timestamp(),
126
- error=e,
127
- )
128
- ],
129
- errorInfo=(
130
- {
131
- "retryLocation": (
132
- current_position.x,
133
- current_position.y,
134
- current_position.z,
135
- )
136
- }
117
+ return DefinedErrorData(
118
+ public=result.public,
119
+ state_update=result.state_update.set_liquid_operated(
120
+ labware_id=current_location.labware_id,
121
+ well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
122
+ current_location.labware_id,
123
+ current_location.well_name,
124
+ params.pipetteId,
125
+ ),
126
+ volume_added=CLEAR,
137
127
  ),
138
- ),
139
- state_update=state_update,
140
- )
128
+ state_update_if_false_positive=result.state_update_if_false_positive,
129
+ )
130
+ else:
131
+ return result
141
132
  else:
142
133
  if (
143
134
  isinstance(current_location, CurrentWell)
144
135
  and current_location.pipette_id == params.pipetteId
145
136
  ):
146
- state_update.set_liquid_operated(
147
- labware_id=current_location.labware_id,
148
- well_name=current_location.well_name,
149
- volume_added=-volume,
137
+ return SuccessData(
138
+ public=AspirateInPlaceResult(volume=result.public.volume),
139
+ state_update=result.state_update.set_liquid_operated(
140
+ labware_id=current_location.labware_id,
141
+ well_names=self._state_view.geometry.get_wells_covered_by_pipette_with_active_well(
142
+ current_location.labware_id,
143
+ current_location.well_name,
144
+ params.pipetteId,
145
+ ),
146
+ volume_added=-result.public.volume
147
+ * self._state_view.geometry.get_nozzles_per_well(
148
+ current_location.labware_id,
149
+ current_location.well_name,
150
+ params.pipetteId,
151
+ ),
152
+ ),
153
+ )
154
+ else:
155
+ return SuccessData(
156
+ public=AspirateInPlaceResult(volume=result.public.volume),
157
+ state_update=result.state_update,
150
158
  )
151
- return SuccessData(
152
- public=AspirateInPlaceResult(volume=volume),
153
- state_update=state_update,
154
- )
155
159
 
156
160
 
157
161
  class AspirateInPlace(
@@ -161,7 +165,7 @@ class AspirateInPlace(
161
165
 
162
166
  commandType: AspirateInPlaceCommandType = "aspirateInPlace"
163
167
  params: AspirateInPlaceParams
164
- result: Optional[AspirateInPlaceResult]
168
+ result: Optional[AspirateInPlaceResult] = None
165
169
 
166
170
  _ImplementationCls: Type[
167
171
  AspirateInPlaceImplementation