opentrons 8.2.0a4__py2.py3-none-any.whl → 8.3.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.
Files changed (238) 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/config/defaults_ot3.py +1 -0
  9. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  10. opentrons/drivers/asyncio/communication/errors.py +16 -3
  11. opentrons/drivers/asyncio/communication/serial_connection.py +24 -9
  12. opentrons/drivers/command_builder.py +2 -2
  13. opentrons/drivers/flex_stacker/__init__.py +9 -0
  14. opentrons/drivers/flex_stacker/abstract.py +89 -0
  15. opentrons/drivers/flex_stacker/driver.py +260 -0
  16. opentrons/drivers/flex_stacker/simulator.py +109 -0
  17. opentrons/drivers/flex_stacker/types.py +138 -0
  18. opentrons/drivers/heater_shaker/driver.py +18 -3
  19. opentrons/drivers/temp_deck/driver.py +13 -3
  20. opentrons/drivers/thermocycler/driver.py +17 -3
  21. opentrons/execute.py +3 -1
  22. opentrons/hardware_control/__init__.py +1 -2
  23. opentrons/hardware_control/api.py +33 -21
  24. opentrons/hardware_control/backends/flex_protocol.py +17 -7
  25. opentrons/hardware_control/backends/ot3controller.py +213 -63
  26. opentrons/hardware_control/backends/ot3simulator.py +18 -9
  27. opentrons/hardware_control/backends/ot3utils.py +43 -15
  28. opentrons/hardware_control/dev_types.py +4 -0
  29. opentrons/hardware_control/emulation/heater_shaker.py +4 -0
  30. opentrons/hardware_control/emulation/module_server/client.py +1 -1
  31. opentrons/hardware_control/emulation/module_server/server.py +5 -3
  32. opentrons/hardware_control/emulation/settings.py +3 -4
  33. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +2 -1
  34. opentrons/hardware_control/instruments/ot2/pipette.py +15 -22
  35. opentrons/hardware_control/instruments/ot2/pipette_handler.py +8 -1
  36. opentrons/hardware_control/instruments/ot3/gripper.py +2 -2
  37. opentrons/hardware_control/instruments/ot3/pipette.py +23 -22
  38. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -1
  39. opentrons/hardware_control/modules/mod_abc.py +2 -2
  40. opentrons/hardware_control/motion_utilities.py +68 -0
  41. opentrons/hardware_control/nozzle_manager.py +39 -41
  42. opentrons/hardware_control/ot3_calibration.py +1 -1
  43. opentrons/hardware_control/ot3api.py +78 -31
  44. opentrons/hardware_control/protocols/gripper_controller.py +3 -0
  45. opentrons/hardware_control/protocols/hardware_manager.py +5 -1
  46. opentrons/hardware_control/protocols/liquid_handler.py +22 -1
  47. opentrons/hardware_control/protocols/motion_controller.py +7 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/legacy_commands/commands.py +37 -0
  51. opentrons/legacy_commands/types.py +39 -0
  52. opentrons/protocol_api/__init__.py +20 -1
  53. opentrons/protocol_api/_liquid.py +24 -49
  54. opentrons/protocol_api/_liquid_properties.py +754 -0
  55. opentrons/protocol_api/_types.py +24 -0
  56. opentrons/protocol_api/core/common.py +2 -0
  57. opentrons/protocol_api/core/engine/instrument.py +191 -10
  58. opentrons/protocol_api/core/engine/labware.py +29 -7
  59. opentrons/protocol_api/core/engine/protocol.py +130 -5
  60. opentrons/protocol_api/core/engine/robot.py +139 -0
  61. opentrons/protocol_api/core/engine/well.py +4 -1
  62. opentrons/protocol_api/core/instrument.py +73 -4
  63. opentrons/protocol_api/core/labware.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +87 -3
  65. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  66. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  67. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  68. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +61 -3
  69. opentrons/protocol_api/core/protocol.py +34 -1
  70. opentrons/protocol_api/core/robot.py +51 -0
  71. opentrons/protocol_api/instrument_context.py +299 -44
  72. opentrons/protocol_api/labware.py +248 -9
  73. opentrons/protocol_api/module_contexts.py +21 -17
  74. opentrons/protocol_api/protocol_context.py +125 -4
  75. opentrons/protocol_api/robot_context.py +204 -32
  76. opentrons/protocol_api/validation.py +262 -3
  77. opentrons/protocol_engine/__init__.py +4 -0
  78. opentrons/protocol_engine/actions/actions.py +2 -3
  79. opentrons/protocol_engine/clients/sync_client.py +18 -0
  80. opentrons/protocol_engine/commands/__init__.py +121 -0
  81. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +1 -3
  82. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +20 -6
  83. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +1 -2
  84. opentrons/protocol_engine/commands/absorbance_reader/read.py +36 -10
  85. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  86. opentrons/protocol_engine/commands/aspirate.py +103 -53
  87. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  88. opentrons/protocol_engine/commands/blow_out.py +44 -39
  89. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  90. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  91. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  92. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  93. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  94. opentrons/protocol_engine/commands/command.py +73 -66
  95. opentrons/protocol_engine/commands/command_unions.py +140 -1
  96. opentrons/protocol_engine/commands/comment.py +1 -1
  97. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  98. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  99. opentrons/protocol_engine/commands/custom.py +6 -12
  100. opentrons/protocol_engine/commands/dispense.py +82 -48
  101. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  102. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  103. opentrons/protocol_engine/commands/drop_tip_in_place.py +79 -8
  104. opentrons/protocol_engine/commands/evotip_dispense.py +156 -0
  105. opentrons/protocol_engine/commands/evotip_seal_pipette.py +331 -0
  106. opentrons/protocol_engine/commands/evotip_unseal_pipette.py +160 -0
  107. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  108. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  109. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  112. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  113. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  114. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  115. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  116. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  117. opentrons/protocol_engine/commands/home.py +13 -4
  118. opentrons/protocol_engine/commands/liquid_probe.py +125 -31
  119. opentrons/protocol_engine/commands/load_labware.py +33 -6
  120. opentrons/protocol_engine/commands/load_lid.py +146 -0
  121. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  122. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  123. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  124. opentrons/protocol_engine/commands/load_module.py +31 -10
  125. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  126. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  127. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  128. opentrons/protocol_engine/commands/move_labware.py +28 -6
  129. opentrons/protocol_engine/commands/move_relative.py +35 -25
  130. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  131. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  132. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  133. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  134. opentrons/protocol_engine/commands/movement_common.py +338 -0
  135. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  136. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  137. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  138. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  139. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  140. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  141. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  142. opentrons/protocol_engine/commands/robot/common.py +18 -0
  143. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  144. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  145. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  146. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  147. opentrons/protocol_engine/commands/save_position.py +14 -5
  148. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  149. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  150. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  151. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  152. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  153. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  154. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  155. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +9 -3
  158. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  159. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  160. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  161. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  162. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  163. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  164. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +5 -2
  165. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +13 -4
  166. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +2 -5
  167. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +1 -1
  168. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +1 -1
  169. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +2 -5
  170. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  171. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  172. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  173. opentrons/protocol_engine/errors/__init__.py +12 -0
  174. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  175. opentrons/protocol_engine/errors/exceptions.py +76 -0
  176. opentrons/protocol_engine/execution/command_executor.py +1 -1
  177. opentrons/protocol_engine/execution/equipment.py +73 -5
  178. opentrons/protocol_engine/execution/gantry_mover.py +369 -8
  179. opentrons/protocol_engine/execution/hardware_stopper.py +7 -7
  180. opentrons/protocol_engine/execution/movement.py +27 -0
  181. opentrons/protocol_engine/execution/pipetting.py +5 -1
  182. opentrons/protocol_engine/execution/tip_handler.py +34 -15
  183. opentrons/protocol_engine/notes/notes.py +1 -1
  184. opentrons/protocol_engine/protocol_engine.py +7 -6
  185. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  186. opentrons/protocol_engine/resources/labware_validation.py +18 -0
  187. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  188. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  189. opentrons/protocol_engine/slot_standardization.py +9 -9
  190. opentrons/protocol_engine/state/_move_types.py +9 -5
  191. opentrons/protocol_engine/state/_well_math.py +193 -0
  192. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  193. opentrons/protocol_engine/state/command_history.py +12 -0
  194. opentrons/protocol_engine/state/commands.py +22 -14
  195. opentrons/protocol_engine/state/files.py +10 -12
  196. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  197. opentrons/protocol_engine/state/frustum_helpers.py +63 -69
  198. opentrons/protocol_engine/state/geometry.py +47 -1
  199. opentrons/protocol_engine/state/labware.py +92 -26
  200. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  201. opentrons/protocol_engine/state/liquids.py +16 -4
  202. opentrons/protocol_engine/state/modules.py +52 -70
  203. opentrons/protocol_engine/state/motion.py +6 -1
  204. opentrons/protocol_engine/state/pipettes.py +149 -58
  205. opentrons/protocol_engine/state/state.py +21 -2
  206. opentrons/protocol_engine/state/state_summary.py +4 -2
  207. opentrons/protocol_engine/state/tips.py +11 -44
  208. opentrons/protocol_engine/state/update_types.py +343 -48
  209. opentrons/protocol_engine/state/wells.py +19 -11
  210. opentrons/protocol_engine/types.py +176 -28
  211. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  212. opentrons/protocol_reader/file_format_validator.py +5 -5
  213. opentrons/protocol_runner/json_file_reader.py +9 -3
  214. opentrons/protocol_runner/json_translator.py +51 -25
  215. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  216. opentrons/protocol_runner/protocol_runner.py +35 -4
  217. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  218. opentrons/protocol_runner/run_orchestrator.py +13 -3
  219. opentrons/protocols/advanced_control/common.py +38 -0
  220. opentrons/protocols/advanced_control/mix.py +1 -1
  221. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  222. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  223. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  224. opentrons/protocols/api_support/definitions.py +1 -1
  225. opentrons/protocols/api_support/instrument.py +1 -1
  226. opentrons/protocols/api_support/util.py +10 -0
  227. opentrons/protocols/labware.py +70 -8
  228. opentrons/protocols/models/json_protocol.py +5 -9
  229. opentrons/simulate.py +3 -1
  230. opentrons/types.py +162 -2
  231. opentrons/util/entrypoint_util.py +2 -5
  232. opentrons/util/logging_config.py +1 -1
  233. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/METADATA +16 -15
  234. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/RECORD +238 -208
  235. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/WHEEL +1 -1
  236. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/LICENSE +0 -0
  237. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0.dist-info}/top_level.txt +0 -0
@@ -96,7 +96,7 @@ class OpenLabwareLatch(
96
96
 
97
97
  commandType: OpenLabwareLatchCommandType = "heaterShaker/openLabwareLatch"
98
98
  params: OpenLabwareLatchParams
99
- result: Optional[OpenLabwareLatchResult]
99
+ result: Optional[OpenLabwareLatchResult] = None
100
100
 
101
101
  _ImplementationCls: Type[OpenLabwareLatchImpl] = OpenLabwareLatchImpl
102
102
 
@@ -109,7 +109,7 @@ class SetAndWaitForShakeSpeed(
109
109
  "heaterShaker/setAndWaitForShakeSpeed"
110
110
  )
111
111
  params: SetAndWaitForShakeSpeedParams
112
- result: Optional[SetAndWaitForShakeSpeedResult]
112
+ result: Optional[SetAndWaitForShakeSpeedResult] = None
113
113
 
114
114
  _ImplementationCls: Type[SetAndWaitForShakeSpeedImpl] = SetAndWaitForShakeSpeedImpl
115
115
 
@@ -76,7 +76,7 @@ class SetTargetTemperature(
76
76
 
77
77
  commandType: SetTargetTemperatureCommandType = "heaterShaker/setTargetTemperature"
78
78
  params: SetTargetTemperatureParams
79
- result: Optional[SetTargetTemperatureResult]
79
+ result: Optional[SetTargetTemperatureResult] = None
80
80
 
81
81
  _ImplementationCls: Type[SetTargetTemperatureImpl] = SetTargetTemperatureImpl
82
82
 
@@ -1,9 +1,10 @@
1
1
  """Command models to wait for a Heater-Shaker Module's target temperature."""
2
2
  from __future__ import annotations
3
- from typing import Optional, TYPE_CHECKING
4
- from typing_extensions import Literal, Type
3
+ from typing import Optional, TYPE_CHECKING, Any
5
4
 
5
+ from typing_extensions import Literal, Type
6
6
  from pydantic import BaseModel, Field
7
+ from pydantic.json_schema import SkipJsonSchema
7
8
 
8
9
  from ..command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
9
10
  from ...errors.error_occurrence import ErrorOccurrence
@@ -16,11 +17,15 @@ if TYPE_CHECKING:
16
17
  WaitForTemperatureCommandType = Literal["heaterShaker/waitForTemperature"]
17
18
 
18
19
 
20
+ def _remove_default(s: dict[str, Any]) -> None:
21
+ s.pop("default", None)
22
+
23
+
19
24
  class WaitForTemperatureParams(BaseModel):
20
25
  """Input parameters to wait for a Heater-Shaker's target temperature."""
21
26
 
22
27
  moduleId: str = Field(..., description="Unique ID of the Heater-Shaker Module.")
23
- celsius: Optional[float] = Field(
28
+ celsius: float | SkipJsonSchema[None] = Field(
24
29
  None,
25
30
  description="Target temperature in °C. If not specified, will "
26
31
  "default to the module's target temperature. "
@@ -28,6 +33,7 @@ class WaitForTemperatureParams(BaseModel):
28
33
  "could lead to unpredictable behavior and hence is not "
29
34
  "recommended for use. This parameter can be removed in a "
30
35
  "future version without prior notice.",
36
+ json_schema_extra=_remove_default,
31
37
  )
32
38
 
33
39
 
@@ -82,7 +88,7 @@ class WaitForTemperature(
82
88
 
83
89
  commandType: WaitForTemperatureCommandType = "heaterShaker/waitForTemperature"
84
90
  params: WaitForTemperatureParams
85
- result: Optional[WaitForTemperatureResult]
91
+ result: Optional[WaitForTemperatureResult] = None
86
92
 
87
93
  _ImplementationCls: Type[WaitForTemperatureImpl] = WaitForTemperatureImpl
88
94
 
@@ -1,7 +1,10 @@
1
1
  """Home command payload, result, and implementation models."""
2
2
  from __future__ import annotations
3
+ from typing import TYPE_CHECKING, Optional, List, Type, Any
4
+
3
5
  from pydantic import BaseModel, Field
4
- from typing import TYPE_CHECKING, Optional, List, Type
6
+ from pydantic.json_schema import SkipJsonSchema
7
+
5
8
  from typing_extensions import Literal
6
9
 
7
10
  from opentrons.types import MountType
@@ -17,24 +20,30 @@ if TYPE_CHECKING:
17
20
  HomeCommandType = Literal["home"]
18
21
 
19
22
 
23
+ def _remove_default(s: dict[str, Any]) -> None:
24
+ s.pop("default", None)
25
+
26
+
20
27
  class HomeParams(BaseModel):
21
28
  """Payload required for a Home command."""
22
29
 
23
- axes: Optional[List[MotorAxis]] = Field(
30
+ axes: List[MotorAxis] | SkipJsonSchema[None] = Field(
24
31
  None,
25
32
  description=(
26
33
  "Axes to return to their home positions. If omitted,"
27
34
  " will home all motors. Extra axes may be implicitly homed"
28
35
  " to ensure accurate homing of the explicitly specified axes."
29
36
  ),
37
+ json_schema_extra=_remove_default,
30
38
  )
31
- skipIfMountPositionOk: Optional[MountType] = Field(
39
+ skipIfMountPositionOk: MountType | SkipJsonSchema[None] = Field(
32
40
  None,
33
41
  description=(
34
42
  "If this parameter is provided, the gantry will only be homed if the"
35
43
  " specified mount has an invalid position. If omitted, the homing action"
36
44
  " will be executed unconditionally."
37
45
  ),
46
+ json_schema_extra=_remove_default,
38
47
  )
39
48
 
40
49
 
@@ -77,7 +86,7 @@ class Home(BaseCommand[HomeParams, HomeResult, ErrorOccurrence]):
77
86
 
78
87
  commandType: HomeCommandType = "home"
79
88
  params: HomeParams
80
- result: Optional[HomeResult]
89
+ result: Optional[HomeResult] = None
81
90
 
82
91
  _ImplementationCls: Type[HomeImplementation] = HomeImplementation
83
92
 
@@ -1,10 +1,11 @@
1
1
  """The liquidProbe and tryLiquidProbe commands."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import TYPE_CHECKING, NamedTuple, Optional, Type, Union
5
- from typing_extensions import Literal
4
+ from typing import TYPE_CHECKING, NamedTuple, Optional, Type, Union, Any
6
5
 
6
+ from typing_extensions import Literal
7
7
  from pydantic import Field
8
+ from pydantic.json_schema import SkipJsonSchema
8
9
 
9
10
  from opentrons.protocol_engine.state import update_types
10
11
  from opentrons.protocol_engine.errors.exceptions import (
@@ -12,18 +13,26 @@ from opentrons.protocol_engine.errors.exceptions import (
12
13
  PipetteNotReadyToAspirateError,
13
14
  TipNotEmptyError,
14
15
  IncompleteLabwareDefinitionError,
16
+ TipNotAttachedError,
15
17
  )
16
18
  from opentrons.types import MountType
17
19
  from opentrons_shared_data.errors.exceptions import (
18
20
  PipetteLiquidNotFoundError,
21
+ UnsupportedHardwareCommand,
22
+ PipetteOverpressureError,
19
23
  )
20
24
 
21
25
  from ..types import DeckPoint
22
26
  from .pipetting_common import (
23
27
  LiquidNotFoundError,
24
28
  PipetteIdMixin,
29
+ OverpressureError,
30
+ )
31
+ from .movement_common import (
25
32
  WellLocationMixin,
26
33
  DestinationPositionResult,
34
+ StallOrCollisionError,
35
+ move_to_well,
27
36
  )
28
37
  from .command import (
29
38
  AbstractCommandImpl,
@@ -36,11 +45,15 @@ from .command import (
36
45
  from ..errors.error_occurrence import ErrorOccurrence
37
46
 
38
47
  if TYPE_CHECKING:
39
- from ..execution import MovementHandler, PipettingHandler
48
+ from ..execution import MovementHandler, PipettingHandler, GantryMover
40
49
  from ..resources import ModelUtils
41
50
  from ..state.state import StateView
42
51
 
43
52
 
53
+ def _remove_default(s: dict[str, Any]) -> None:
54
+ s.pop("default", None)
55
+
56
+
44
57
  LiquidProbeCommandType = Literal["liquidProbe"]
45
58
  TryLiquidProbeCommandType = Literal["tryLiquidProbe"]
46
59
 
@@ -76,43 +89,64 @@ class LiquidProbeResult(DestinationPositionResult):
76
89
  class TryLiquidProbeResult(DestinationPositionResult):
77
90
  """Result data from the execution of a `tryLiquidProbe` command."""
78
91
 
79
- z_position: Optional[float] = Field(
92
+ z_position: float | SkipJsonSchema[None] = Field(
80
93
  ...,
81
94
  description=(
82
95
  "The Z coordinate, in mm, of the found liquid in deck space."
83
96
  " If no liquid was found, `null` or omitted."
84
97
  ),
98
+ json_schema_extra=_remove_default,
85
99
  )
86
100
 
87
101
 
88
102
  _LiquidProbeExecuteReturn = Union[
89
103
  SuccessData[LiquidProbeResult],
90
- DefinedErrorData[LiquidNotFoundError],
104
+ DefinedErrorData[LiquidNotFoundError]
105
+ | DefinedErrorData[StallOrCollisionError]
106
+ | DefinedErrorData[OverpressureError],
91
107
  ]
92
- _TryLiquidProbeExecuteReturn = SuccessData[TryLiquidProbeResult]
108
+ _TryLiquidProbeExecuteReturn = (
109
+ SuccessData[TryLiquidProbeResult]
110
+ | DefinedErrorData[StallOrCollisionError]
111
+ | DefinedErrorData[OverpressureError]
112
+ )
93
113
 
94
114
 
95
115
  class _ExecuteCommonResult(NamedTuple):
96
116
  # If the probe succeeded, the z_pos that it returned.
97
117
  # Or, if the probe found no liquid, the error representing that,
98
118
  # so calling code can propagate those details up.
99
- z_pos_or_error: float | PipetteLiquidNotFoundError
119
+ z_pos_or_error: float | PipetteLiquidNotFoundError | PipetteOverpressureError
100
120
 
101
121
  state_update: update_types.StateUpdate
102
122
  deck_point: DeckPoint
103
123
 
104
124
 
105
- async def _execute_common(
125
+ async def _execute_common( # noqa: C901
106
126
  state_view: StateView,
107
127
  movement: MovementHandler,
128
+ gantry_mover: GantryMover,
108
129
  pipetting: PipettingHandler,
130
+ model_utils: ModelUtils,
109
131
  params: _CommonParams,
110
- ) -> _ExecuteCommonResult:
132
+ ) -> _ExecuteCommonResult | DefinedErrorData[StallOrCollisionError] | DefinedErrorData[
133
+ OverpressureError
134
+ ]:
111
135
  pipette_id = params.pipetteId
112
136
  labware_id = params.labwareId
113
137
  well_name = params.wellName
138
+ if (
139
+ "pressure"
140
+ not in state_view.pipettes.get_config(pipette_id).available_sensors.sensors
141
+ ):
142
+ raise UnsupportedHardwareCommand(
143
+ "Pressure sensor not available for this pipette"
144
+ )
114
145
 
115
- state_update = update_types.StateUpdate()
146
+ if not state_view.pipettes.get_nozzle_configuration_supports_lld(pipette_id):
147
+ raise TipNotAttachedError(
148
+ "Either the front right or back left nozzle must have a tip attached to probe liquid height."
149
+ )
116
150
 
117
151
  # May raise TipNotAttachedError.
118
152
  aspirated_volume = state_view.pipettes.get_aspirated_volume(pipette_id)
@@ -137,21 +171,18 @@ async def _execute_common(
137
171
  )
138
172
 
139
173
  # liquid_probe process start position
140
- position = await movement.move_to_well(
174
+ move_result = await move_to_well(
175
+ movement=movement,
176
+ model_utils=model_utils,
141
177
  pipette_id=pipette_id,
142
178
  labware_id=labware_id,
143
179
  well_name=well_name,
144
180
  well_location=params.wellLocation,
145
181
  )
146
- deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
147
- state_update.set_pipette_location(
148
- pipette_id=pipette_id,
149
- new_labware_id=labware_id,
150
- new_well_name=well_name,
151
- new_deck_point=deck_point,
152
- )
153
-
182
+ if isinstance(move_result, DefinedErrorData):
183
+ return move_result
154
184
  try:
185
+ current_position = await gantry_mover.get_position(params.pipetteId)
155
186
  z_pos = await pipetting.liquid_probe_in_place(
156
187
  pipette_id=pipette_id,
157
188
  labware_id=labware_id,
@@ -160,11 +191,42 @@ async def _execute_common(
160
191
  )
161
192
  except PipetteLiquidNotFoundError as exception:
162
193
  return _ExecuteCommonResult(
163
- z_pos_or_error=exception, state_update=state_update, deck_point=deck_point
194
+ z_pos_or_error=exception,
195
+ state_update=move_result.state_update,
196
+ deck_point=move_result.public.position,
197
+ )
198
+ except PipetteOverpressureError as e:
199
+ return DefinedErrorData(
200
+ public=OverpressureError(
201
+ id=model_utils.generate_id(),
202
+ createdAt=model_utils.get_timestamp(),
203
+ wrappedErrors=[
204
+ ErrorOccurrence.from_failed(
205
+ id=model_utils.generate_id(),
206
+ createdAt=model_utils.get_timestamp(),
207
+ error=e,
208
+ )
209
+ ],
210
+ errorInfo=(
211
+ {
212
+ # This is here bc its not optional in the type but we are not using the retry location for this case
213
+ "retryLocation": (
214
+ current_position.x,
215
+ current_position.y,
216
+ current_position.z,
217
+ )
218
+ }
219
+ ),
220
+ ),
221
+ state_update=move_result.state_update.set_fluid_unknown(
222
+ pipette_id=pipette_id
223
+ ),
164
224
  )
165
225
  else:
166
226
  return _ExecuteCommonResult(
167
- z_pos_or_error=z_pos, state_update=state_update, deck_point=deck_point
227
+ z_pos_or_error=z_pos,
228
+ state_update=move_result.state_update,
229
+ deck_point=move_result.public.position,
168
230
  )
169
231
 
170
232
 
@@ -177,12 +239,14 @@ class LiquidProbeImplementation(
177
239
  self,
178
240
  state_view: StateView,
179
241
  movement: MovementHandler,
242
+ gantry_mover: GantryMover,
180
243
  pipetting: PipettingHandler,
181
244
  model_utils: ModelUtils,
182
245
  **kwargs: object,
183
246
  ) -> None:
184
247
  self._state_view = state_view
185
248
  self._movement = movement
249
+ self._gantry_mover = gantry_mover
186
250
  self._pipetting = pipetting
187
251
  self._model_utils = model_utils
188
252
 
@@ -202,10 +266,20 @@ class LiquidProbeImplementation(
202
266
  MustHomeError: as an undefined error, if the plunger is not in a valid
203
267
  position.
204
268
  """
205
- z_pos_or_error, state_update, deck_point = await _execute_common(
206
- self._state_view, self._movement, self._pipetting, params
269
+ result = await _execute_common(
270
+ state_view=self._state_view,
271
+ movement=self._movement,
272
+ gantry_mover=self._gantry_mover,
273
+ pipetting=self._pipetting,
274
+ model_utils=self._model_utils,
275
+ params=params,
207
276
  )
208
- if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
277
+ if isinstance(result, DefinedErrorData):
278
+ return result
279
+ z_pos_or_error, state_update, deck_point = result
280
+ if isinstance(
281
+ z_pos_or_error, (PipetteLiquidNotFoundError, PipetteOverpressureError)
282
+ ):
209
283
  state_update.set_liquid_probed(
210
284
  labware_id=params.labwareId,
211
285
  well_name=params.wellName,
@@ -262,12 +336,14 @@ class TryLiquidProbeImplementation(
262
336
  self,
263
337
  state_view: StateView,
264
338
  movement: MovementHandler,
339
+ gantry_mover: GantryMover,
265
340
  pipetting: PipettingHandler,
266
341
  model_utils: ModelUtils,
267
342
  **kwargs: object,
268
343
  ) -> None:
269
344
  self._state_view = state_view
270
345
  self._movement = movement
346
+ self._gantry_mover = gantry_mover
271
347
  self._pipetting = pipetting
272
348
  self._model_utils = model_utils
273
349
 
@@ -278,11 +354,21 @@ class TryLiquidProbeImplementation(
278
354
  found, `tryLiquidProbe` returns a success result with `z_position=null` instead
279
355
  of a defined error.
280
356
  """
281
- z_pos_or_error, state_update, deck_point = await _execute_common(
282
- self._state_view, self._movement, self._pipetting, params
357
+ result = await _execute_common(
358
+ state_view=self._state_view,
359
+ movement=self._movement,
360
+ gantry_mover=self._gantry_mover,
361
+ pipetting=self._pipetting,
362
+ model_utils=self._model_utils,
363
+ params=params,
283
364
  )
365
+ if isinstance(result, DefinedErrorData):
366
+ return result
367
+ z_pos_or_error, state_update, deck_point = result
284
368
 
285
- if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
369
+ if isinstance(
370
+ z_pos_or_error, (PipetteLiquidNotFoundError, PipetteOverpressureError)
371
+ ):
286
372
  z_pos = None
287
373
  well_volume: float | update_types.ClearType = update_types.CLEAR
288
374
  else:
@@ -312,25 +398,33 @@ class TryLiquidProbeImplementation(
312
398
 
313
399
 
314
400
  class LiquidProbe(
315
- BaseCommand[LiquidProbeParams, LiquidProbeResult, LiquidNotFoundError]
401
+ BaseCommand[
402
+ LiquidProbeParams,
403
+ LiquidProbeResult,
404
+ LiquidNotFoundError | StallOrCollisionError | OverpressureError,
405
+ ]
316
406
  ):
317
407
  """The model for a full `liquidProbe` command."""
318
408
 
319
409
  commandType: LiquidProbeCommandType = "liquidProbe"
320
410
  params: LiquidProbeParams
321
- result: Optional[LiquidProbeResult]
411
+ result: Optional[LiquidProbeResult] = None
322
412
 
323
413
  _ImplementationCls: Type[LiquidProbeImplementation] = LiquidProbeImplementation
324
414
 
325
415
 
326
416
  class TryLiquidProbe(
327
- BaseCommand[TryLiquidProbeParams, TryLiquidProbeResult, ErrorOccurrence]
417
+ BaseCommand[
418
+ TryLiquidProbeParams,
419
+ TryLiquidProbeResult,
420
+ StallOrCollisionError | OverpressureError,
421
+ ]
328
422
  ):
329
423
  """The model for a full `tryLiquidProbe` command."""
330
424
 
331
425
  commandType: TryLiquidProbeCommandType = "tryLiquidProbe"
332
426
  params: TryLiquidProbeParams
333
- result: Optional[TryLiquidProbeResult]
427
+ result: Optional[TryLiquidProbeResult] = None
334
428
 
335
429
  _ImplementationCls: Type[
336
430
  TryLiquidProbeImplementation
@@ -1,7 +1,9 @@
1
1
  """Load labware command request, result, and implementation models."""
2
2
  from __future__ import annotations
3
+ from typing import TYPE_CHECKING, Optional, Type, Any
4
+
3
5
  from pydantic import BaseModel, Field
4
- from typing import TYPE_CHECKING, Optional, Type
6
+ from pydantic.json_schema import SkipJsonSchema
5
7
  from typing_extensions import Literal
6
8
 
7
9
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
@@ -29,6 +31,10 @@ if TYPE_CHECKING:
29
31
  LoadLabwareCommandType = Literal["loadLabware"]
30
32
 
31
33
 
34
+ def _remove_default(s: dict[str, Any]) -> None:
35
+ s.pop("default", None)
36
+
37
+
32
38
  class LoadLabwareParams(BaseModel):
33
39
  """Payload required to load a labware into a slot."""
34
40
 
@@ -48,18 +54,20 @@ class LoadLabwareParams(BaseModel):
48
54
  ...,
49
55
  description="The labware definition version.",
50
56
  )
51
- labwareId: Optional[str] = Field(
57
+ labwareId: str | SkipJsonSchema[None] = Field(
52
58
  None,
53
59
  description="An optional ID to assign to this labware. If None, an ID "
54
60
  "will be generated.",
61
+ json_schema_extra=_remove_default,
55
62
  )
56
- displayName: Optional[str] = Field(
63
+ displayName: str | SkipJsonSchema[None] = Field(
57
64
  None,
58
65
  description="An optional user-specified display name "
59
66
  "or label for this labware.",
60
67
  # NOTE: v4/5 JSON protocols will always have a displayName which will be the
61
68
  # user-specified label OR the displayName property of the labware's definition.
62
69
  # TODO: Make sure v6 JSON protocols don't do that.
70
+ json_schema_extra=_remove_default,
63
71
  )
64
72
 
65
73
 
@@ -104,6 +112,8 @@ class LoadLabwareImplementation(
104
112
  self, params: LoadLabwareParams
105
113
  ) -> SuccessData[LoadLabwareResult]:
106
114
  """Load definition and calibration data necessary for a labware."""
115
+ state_update = StateUpdate()
116
+
107
117
  # TODO (tz, 8-15-2023): extend column validation to column 1 when working
108
118
  # on https://opentrons.atlassian.net/browse/RSS-258 and completing
109
119
  # https://opentrons.atlassian.net/browse/RSS-255
@@ -128,10 +138,12 @@ class LoadLabwareImplementation(
128
138
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
129
139
  area_name
130
140
  )
141
+ state_update.set_addressable_area_used(area_name)
131
142
  elif isinstance(params.location, DeckSlotLocation):
132
143
  self._state_view.addressable_areas.raise_if_area_not_in_deck_configuration(
133
144
  params.location.slotName.id
134
145
  )
146
+ state_update.set_addressable_area_used(params.location.slotName.id)
135
147
 
136
148
  verified_location = self._state_view.geometry.ensure_location_not_occupied(
137
149
  params.location
@@ -144,8 +156,6 @@ class LoadLabwareImplementation(
144
156
  labware_id=params.labwareId,
145
157
  )
146
158
 
147
- state_update = StateUpdate()
148
-
149
159
  state_update.set_loaded_labware(
150
160
  labware_id=loaded_labware.labware_id,
151
161
  offset_id=loaded_labware.offsetId,
@@ -162,6 +172,19 @@ class LoadLabwareImplementation(
162
172
  top_labware_definition=loaded_labware.definition,
163
173
  bottom_labware_id=verified_location.labwareId,
164
174
  )
175
+ # Validate load location is valid for lids
176
+ if (
177
+ labware_validation.validate_definition_is_lid(
178
+ definition=loaded_labware.definition
179
+ )
180
+ and loaded_labware.definition.compatibleParentLabware is not None
181
+ and self._state_view.labware.get_load_name(verified_location.labwareId)
182
+ not in loaded_labware.definition.compatibleParentLabware
183
+ ):
184
+ raise ValueError(
185
+ f"Labware Lid {params.loadName} may not be loaded on parent labware {self._state_view.labware.get_display_name(verified_location.labwareId)}."
186
+ )
187
+
165
188
  # Validate labware for the absorbance reader
166
189
  elif isinstance(params.location, ModuleLocation):
167
190
  module = self._state_view.modules.get(params.location.moduleId)
@@ -170,6 +193,10 @@ class LoadLabwareImplementation(
170
193
  loaded_labware.definition
171
194
  )
172
195
 
196
+ self._state_view.labware.raise_if_labware_cannot_be_ondeck(
197
+ location=params.location, labware_definition=loaded_labware.definition
198
+ )
199
+
173
200
  return SuccessData(
174
201
  public=LoadLabwareResult(
175
202
  labwareId=loaded_labware.labware_id,
@@ -185,7 +212,7 @@ class LoadLabware(BaseCommand[LoadLabwareParams, LoadLabwareResult, ErrorOccurre
185
212
 
186
213
  commandType: LoadLabwareCommandType = "loadLabware"
187
214
  params: LoadLabwareParams
188
- result: Optional[LoadLabwareResult]
215
+ result: Optional[LoadLabwareResult] = None
189
216
 
190
217
  _ImplementationCls: Type[LoadLabwareImplementation] = LoadLabwareImplementation
191
218