opentrons 8.3.0a0__py2.py3-none-any.whl → 8.3.0a2__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 (229) 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 +70 -8
  219. opentrons/protocols/models/json_protocol.py +5 -9
  220. opentrons/simulate.py +3 -1
  221. opentrons/types.py +162 -2
  222. opentrons/util/entrypoint_util.py +2 -5
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/METADATA +16 -15
  225. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/RECORD +229 -202
  226. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/WHEEL +1 -1
  227. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/LICENSE +0 -0
  228. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.3.0a0.dist-info → opentrons-8.3.0a2.dist-info}/top_level.txt +0 -0
@@ -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,6 +13,7 @@ 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 (
@@ -23,8 +25,12 @@ from ..types import DeckPoint
23
25
  from .pipetting_common import (
24
26
  LiquidNotFoundError,
25
27
  PipetteIdMixin,
28
+ )
29
+ from .movement_common import (
26
30
  WellLocationMixin,
27
31
  DestinationPositionResult,
32
+ StallOrCollisionError,
33
+ move_to_well,
28
34
  )
29
35
  from .command import (
30
36
  AbstractCommandImpl,
@@ -42,6 +48,10 @@ if TYPE_CHECKING:
42
48
  from ..state.state import StateView
43
49
 
44
50
 
51
+ def _remove_default(s: dict[str, Any]) -> None:
52
+ s.pop("default", None)
53
+
54
+
45
55
  LiquidProbeCommandType = Literal["liquidProbe"]
46
56
  TryLiquidProbeCommandType = Literal["tryLiquidProbe"]
47
57
 
@@ -77,20 +87,23 @@ class LiquidProbeResult(DestinationPositionResult):
77
87
  class TryLiquidProbeResult(DestinationPositionResult):
78
88
  """Result data from the execution of a `tryLiquidProbe` command."""
79
89
 
80
- z_position: Optional[float] = Field(
90
+ z_position: float | SkipJsonSchema[None] = Field(
81
91
  ...,
82
92
  description=(
83
93
  "The Z coordinate, in mm, of the found liquid in deck space."
84
94
  " If no liquid was found, `null` or omitted."
85
95
  ),
96
+ json_schema_extra=_remove_default,
86
97
  )
87
98
 
88
99
 
89
100
  _LiquidProbeExecuteReturn = Union[
90
101
  SuccessData[LiquidProbeResult],
91
- DefinedErrorData[LiquidNotFoundError],
102
+ DefinedErrorData[LiquidNotFoundError] | DefinedErrorData[StallOrCollisionError],
92
103
  ]
93
- _TryLiquidProbeExecuteReturn = SuccessData[TryLiquidProbeResult]
104
+ _TryLiquidProbeExecuteReturn = (
105
+ SuccessData[TryLiquidProbeResult] | DefinedErrorData[StallOrCollisionError]
106
+ )
94
107
 
95
108
 
96
109
  class _ExecuteCommonResult(NamedTuple):
@@ -107,13 +120,12 @@ async def _execute_common(
107
120
  state_view: StateView,
108
121
  movement: MovementHandler,
109
122
  pipetting: PipettingHandler,
123
+ model_utils: ModelUtils,
110
124
  params: _CommonParams,
111
- ) -> _ExecuteCommonResult:
125
+ ) -> _ExecuteCommonResult | DefinedErrorData[StallOrCollisionError]:
112
126
  pipette_id = params.pipetteId
113
127
  labware_id = params.labwareId
114
128
  well_name = params.wellName
115
-
116
- state_update = update_types.StateUpdate()
117
129
  if (
118
130
  "pressure"
119
131
  not in state_view.pipettes.get_config(pipette_id).available_sensors.sensors
@@ -122,6 +134,11 @@ async def _execute_common(
122
134
  "Pressure sensor not available for this pipette"
123
135
  )
124
136
 
137
+ if not state_view.pipettes.get_nozzle_configuration_supports_lld(pipette_id):
138
+ raise TipNotAttachedError(
139
+ "Either the front right or back left nozzle must have a tip attached to probe liquid height."
140
+ )
141
+
125
142
  # May raise TipNotAttachedError.
126
143
  aspirated_volume = state_view.pipettes.get_aspirated_volume(pipette_id)
127
144
 
@@ -145,20 +162,16 @@ async def _execute_common(
145
162
  )
146
163
 
147
164
  # liquid_probe process start position
148
- position = await movement.move_to_well(
165
+ move_result = await move_to_well(
166
+ movement=movement,
167
+ model_utils=model_utils,
149
168
  pipette_id=pipette_id,
150
169
  labware_id=labware_id,
151
170
  well_name=well_name,
152
171
  well_location=params.wellLocation,
153
172
  )
154
- deck_point = DeckPoint.construct(x=position.x, y=position.y, z=position.z)
155
- state_update.set_pipette_location(
156
- pipette_id=pipette_id,
157
- new_labware_id=labware_id,
158
- new_well_name=well_name,
159
- new_deck_point=deck_point,
160
- )
161
-
173
+ if isinstance(move_result, DefinedErrorData):
174
+ return move_result
162
175
  try:
163
176
  z_pos = await pipetting.liquid_probe_in_place(
164
177
  pipette_id=pipette_id,
@@ -168,11 +181,15 @@ async def _execute_common(
168
181
  )
169
182
  except PipetteLiquidNotFoundError as exception:
170
183
  return _ExecuteCommonResult(
171
- z_pos_or_error=exception, state_update=state_update, deck_point=deck_point
184
+ z_pos_or_error=exception,
185
+ state_update=move_result.state_update,
186
+ deck_point=move_result.public.position,
172
187
  )
173
188
  else:
174
189
  return _ExecuteCommonResult(
175
- z_pos_or_error=z_pos, state_update=state_update, deck_point=deck_point
190
+ z_pos_or_error=z_pos,
191
+ state_update=move_result.state_update,
192
+ deck_point=move_result.public.position,
176
193
  )
177
194
 
178
195
 
@@ -210,9 +227,16 @@ class LiquidProbeImplementation(
210
227
  MustHomeError: as an undefined error, if the plunger is not in a valid
211
228
  position.
212
229
  """
213
- z_pos_or_error, state_update, deck_point = await _execute_common(
214
- self._state_view, self._movement, self._pipetting, params
230
+ result = await _execute_common(
231
+ state_view=self._state_view,
232
+ movement=self._movement,
233
+ pipetting=self._pipetting,
234
+ model_utils=self._model_utils,
235
+ params=params,
215
236
  )
237
+ if isinstance(result, DefinedErrorData):
238
+ return result
239
+ z_pos_or_error, state_update, deck_point = result
216
240
  if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
217
241
  state_update.set_liquid_probed(
218
242
  labware_id=params.labwareId,
@@ -286,9 +310,16 @@ class TryLiquidProbeImplementation(
286
310
  found, `tryLiquidProbe` returns a success result with `z_position=null` instead
287
311
  of a defined error.
288
312
  """
289
- z_pos_or_error, state_update, deck_point = await _execute_common(
290
- self._state_view, self._movement, self._pipetting, params
313
+ result = await _execute_common(
314
+ state_view=self._state_view,
315
+ movement=self._movement,
316
+ pipetting=self._pipetting,
317
+ model_utils=self._model_utils,
318
+ params=params,
291
319
  )
320
+ if isinstance(result, DefinedErrorData):
321
+ return result
322
+ z_pos_or_error, state_update, deck_point = result
292
323
 
293
324
  if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
294
325
  z_pos = None
@@ -320,7 +351,11 @@ class TryLiquidProbeImplementation(
320
351
 
321
352
 
322
353
  class LiquidProbe(
323
- BaseCommand[LiquidProbeParams, LiquidProbeResult, LiquidNotFoundError]
354
+ BaseCommand[
355
+ LiquidProbeParams,
356
+ LiquidProbeResult,
357
+ LiquidNotFoundError | StallOrCollisionError,
358
+ ]
324
359
  ):
325
360
  """The model for a full `liquidProbe` command."""
326
361
 
@@ -332,7 +367,7 @@ class LiquidProbe(
332
367
 
333
368
 
334
369
  class TryLiquidProbe(
335
- BaseCommand[TryLiquidProbeParams, TryLiquidProbeResult, ErrorOccurrence]
370
+ BaseCommand[TryLiquidProbeParams, TryLiquidProbeResult, StallOrCollisionError]
336
371
  ):
337
372
  """The model for a full `tryLiquidProbe` command."""
338
373
 
@@ -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)
@@ -169,7 +192,6 @@ class LoadLabwareImplementation(
169
192
  self._state_view.labware.raise_if_labware_incompatible_with_plate_reader(
170
193
  loaded_labware.definition
171
194
  )
172
-
173
195
  return SuccessData(
174
196
  public=LoadLabwareResult(
175
197
  labwareId=loaded_labware.labware_id,
@@ -185,7 +207,7 @@ class LoadLabware(BaseCommand[LoadLabwareParams, LoadLabwareResult, ErrorOccurre
185
207
 
186
208
  commandType: LoadLabwareCommandType = "loadLabware"
187
209
  params: LoadLabwareParams
188
- result: Optional[LoadLabwareResult]
210
+ result: Optional[LoadLabwareResult] = None
189
211
 
190
212
  _ImplementationCls: Type[LoadLabwareImplementation] = LoadLabwareImplementation
191
213
 
@@ -0,0 +1,146 @@
1
+ """Load lid command request, result, and implementation models."""
2
+ from __future__ import annotations
3
+ from pydantic import BaseModel, Field
4
+ from typing import TYPE_CHECKING, Optional, Type
5
+ from typing_extensions import Literal
6
+
7
+ from opentrons_shared_data.labware.labware_definition import LabwareDefinition
8
+
9
+ from ..errors import LabwareCannotBeStackedError, LabwareIsNotAllowedInLocationError
10
+ from ..resources import labware_validation
11
+ from ..types import (
12
+ LabwareLocation,
13
+ OnLabwareLocation,
14
+ )
15
+
16
+ from .command import AbstractCommandImpl, BaseCommand, BaseCommandCreate, SuccessData
17
+ from ..errors.error_occurrence import ErrorOccurrence
18
+ from ..state.update_types import StateUpdate
19
+
20
+ if TYPE_CHECKING:
21
+ from ..state.state import StateView
22
+ from ..execution import EquipmentHandler
23
+
24
+
25
+ LoadLidCommandType = Literal["loadLid"]
26
+
27
+
28
+ class LoadLidParams(BaseModel):
29
+ """Payload required to load a lid onto a labware."""
30
+
31
+ location: LabwareLocation = Field(
32
+ ...,
33
+ description="Labware the lid should be loaded onto.",
34
+ )
35
+ loadName: str = Field(
36
+ ...,
37
+ description="Name used to reference a lid labware definition.",
38
+ )
39
+ namespace: str = Field(
40
+ ...,
41
+ description="The namespace the lid labware definition belongs to.",
42
+ )
43
+ version: int = Field(
44
+ ...,
45
+ description="The lid labware definition version.",
46
+ )
47
+
48
+
49
+ class LoadLidResult(BaseModel):
50
+ """Result data from the execution of a LoadLabware command."""
51
+
52
+ labwareId: str = Field(
53
+ ...,
54
+ description="An ID to reference this lid labware in subsequent commands.",
55
+ )
56
+ definition: LabwareDefinition = Field(
57
+ ...,
58
+ description="The full definition data for this lid labware.",
59
+ )
60
+
61
+
62
+ class LoadLidImplementation(
63
+ AbstractCommandImpl[LoadLidParams, SuccessData[LoadLidResult]]
64
+ ):
65
+ """Load lid command implementation."""
66
+
67
+ def __init__(
68
+ self, equipment: EquipmentHandler, state_view: StateView, **kwargs: object
69
+ ) -> None:
70
+ self._equipment = equipment
71
+ self._state_view = state_view
72
+
73
+ async def execute(self, params: LoadLidParams) -> SuccessData[LoadLidResult]:
74
+ """Load definition and calibration data necessary for a lid."""
75
+ if not isinstance(params.location, OnLabwareLocation):
76
+ raise LabwareIsNotAllowedInLocationError(
77
+ "Lid Labware is only allowed to be loaded on top of a labware. Try `load_lid_stack(...)` to load lids without parent labware."
78
+ )
79
+
80
+ verified_location = self._state_view.geometry.ensure_location_not_occupied(
81
+ params.location
82
+ )
83
+ loaded_labware = await self._equipment.load_labware(
84
+ load_name=params.loadName,
85
+ namespace=params.namespace,
86
+ version=params.version,
87
+ location=verified_location,
88
+ labware_id=None,
89
+ )
90
+
91
+ # TODO(chb 2024-12-12) these validation checks happen after the labware is loaded, because they rely on
92
+ # on the definition. In practice this will not cause any issues since they will raise protocol ending
93
+ # exception, but for correctness should be refactored to do this check beforehand.
94
+ if not labware_validation.validate_definition_is_lid(loaded_labware.definition):
95
+ raise LabwareCannotBeStackedError(
96
+ f"Labware {params.loadName} is not a Lid and cannot be loaded onto {self._state_view.labware.get_display_name(params.location.labwareId)}."
97
+ )
98
+
99
+ state_update = StateUpdate()
100
+
101
+ # In the case of lids being loaded on top of other labware, set the parent labware's lid
102
+ state_update.set_lid(
103
+ parent_labware_id=params.location.labwareId,
104
+ lid_id=loaded_labware.labware_id,
105
+ )
106
+
107
+ state_update.set_loaded_labware(
108
+ labware_id=loaded_labware.labware_id,
109
+ offset_id=loaded_labware.offsetId,
110
+ definition=loaded_labware.definition,
111
+ location=verified_location,
112
+ display_name=None,
113
+ )
114
+
115
+ if isinstance(verified_location, OnLabwareLocation):
116
+ self._state_view.labware.raise_if_labware_cannot_be_stacked(
117
+ top_labware_definition=loaded_labware.definition,
118
+ bottom_labware_id=verified_location.labwareId,
119
+ )
120
+
121
+ return SuccessData(
122
+ public=LoadLidResult(
123
+ labwareId=loaded_labware.labware_id,
124
+ definition=loaded_labware.definition,
125
+ ),
126
+ state_update=state_update,
127
+ )
128
+
129
+
130
+ class LoadLid(BaseCommand[LoadLidParams, LoadLidResult, ErrorOccurrence]):
131
+ """Load lid command resource model."""
132
+
133
+ commandType: LoadLidCommandType = "loadLid"
134
+ params: LoadLidParams
135
+ result: Optional[LoadLidResult]
136
+
137
+ _ImplementationCls: Type[LoadLidImplementation] = LoadLidImplementation
138
+
139
+
140
+ class LoadLidCreate(BaseCommandCreate[LoadLidParams]):
141
+ """Load lid command creation request."""
142
+
143
+ commandType: LoadLidCommandType = "loadLid"
144
+ params: LoadLidParams
145
+
146
+ _CommandCls: Type[LoadLid] = LoadLid