opentrons 8.2.0a4__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 (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/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 +28 -20
  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 +60 -23
  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 +18 -0
  47. opentrons/hardware_control/protocols/motion_controller.py +6 -0
  48. opentrons/hardware_control/robot_calibration.py +1 -1
  49. opentrons/hardware_control/types.py +61 -0
  50. opentrons/protocol_api/__init__.py +20 -1
  51. opentrons/protocol_api/_liquid.py +24 -49
  52. opentrons/protocol_api/_liquid_properties.py +754 -0
  53. opentrons/protocol_api/_types.py +24 -0
  54. opentrons/protocol_api/core/common.py +2 -0
  55. opentrons/protocol_api/core/engine/instrument.py +82 -10
  56. opentrons/protocol_api/core/engine/labware.py +29 -7
  57. opentrons/protocol_api/core/engine/protocol.py +130 -5
  58. opentrons/protocol_api/core/engine/robot.py +139 -0
  59. opentrons/protocol_api/core/engine/well.py +4 -1
  60. opentrons/protocol_api/core/instrument.py +46 -4
  61. opentrons/protocol_api/core/labware.py +13 -4
  62. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +37 -3
  63. opentrons/protocol_api/core/legacy/legacy_labware_core.py +13 -4
  64. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +32 -1
  65. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +37 -3
  67. opentrons/protocol_api/core/protocol.py +34 -1
  68. opentrons/protocol_api/core/robot.py +51 -0
  69. opentrons/protocol_api/instrument_context.py +158 -44
  70. opentrons/protocol_api/labware.py +231 -7
  71. opentrons/protocol_api/module_contexts.py +21 -17
  72. opentrons/protocol_api/protocol_context.py +125 -4
  73. opentrons/protocol_api/robot_context.py +204 -32
  74. opentrons/protocol_api/validation.py +262 -3
  75. opentrons/protocol_engine/__init__.py +4 -0
  76. opentrons/protocol_engine/actions/actions.py +2 -3
  77. opentrons/protocol_engine/clients/sync_client.py +18 -0
  78. opentrons/protocol_engine/commands/__init__.py +81 -0
  79. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +0 -2
  80. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +19 -5
  81. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +0 -1
  82. opentrons/protocol_engine/commands/absorbance_reader/read.py +32 -9
  83. opentrons/protocol_engine/commands/air_gap_in_place.py +160 -0
  84. opentrons/protocol_engine/commands/aspirate.py +103 -53
  85. opentrons/protocol_engine/commands/aspirate_in_place.py +55 -51
  86. opentrons/protocol_engine/commands/blow_out.py +44 -39
  87. opentrons/protocol_engine/commands/blow_out_in_place.py +21 -32
  88. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +13 -6
  89. opentrons/protocol_engine/commands/calibration/calibrate_module.py +1 -1
  90. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +3 -3
  91. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +1 -1
  92. opentrons/protocol_engine/commands/command.py +73 -66
  93. opentrons/protocol_engine/commands/command_unions.py +101 -1
  94. opentrons/protocol_engine/commands/comment.py +1 -1
  95. opentrons/protocol_engine/commands/configure_for_volume.py +10 -3
  96. opentrons/protocol_engine/commands/configure_nozzle_layout.py +6 -4
  97. opentrons/protocol_engine/commands/custom.py +6 -12
  98. opentrons/protocol_engine/commands/dispense.py +82 -48
  99. opentrons/protocol_engine/commands/dispense_in_place.py +71 -51
  100. opentrons/protocol_engine/commands/drop_tip.py +52 -31
  101. opentrons/protocol_engine/commands/drop_tip_in_place.py +13 -3
  102. opentrons/protocol_engine/commands/generate_command_schema.py +4 -11
  103. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  104. opentrons/protocol_engine/commands/get_tip_presence.py +1 -1
  105. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +1 -1
  106. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +1 -1
  107. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +1 -1
  108. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +1 -1
  109. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +1 -1
  110. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +1 -1
  111. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +10 -4
  112. opentrons/protocol_engine/commands/home.py +13 -4
  113. opentrons/protocol_engine/commands/liquid_probe.py +67 -24
  114. opentrons/protocol_engine/commands/load_labware.py +29 -7
  115. opentrons/protocol_engine/commands/load_lid.py +146 -0
  116. opentrons/protocol_engine/commands/load_lid_stack.py +189 -0
  117. opentrons/protocol_engine/commands/load_liquid.py +12 -4
  118. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  119. opentrons/protocol_engine/commands/load_module.py +31 -10
  120. opentrons/protocol_engine/commands/load_pipette.py +19 -8
  121. opentrons/protocol_engine/commands/magnetic_module/disengage.py +1 -1
  122. opentrons/protocol_engine/commands/magnetic_module/engage.py +1 -1
  123. opentrons/protocol_engine/commands/move_labware.py +19 -6
  124. opentrons/protocol_engine/commands/move_relative.py +35 -25
  125. opentrons/protocol_engine/commands/move_to_addressable_area.py +40 -27
  126. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +53 -32
  127. opentrons/protocol_engine/commands/move_to_coordinates.py +36 -22
  128. opentrons/protocol_engine/commands/move_to_well.py +40 -24
  129. opentrons/protocol_engine/commands/movement_common.py +338 -0
  130. opentrons/protocol_engine/commands/pick_up_tip.py +49 -27
  131. opentrons/protocol_engine/commands/pipetting_common.py +169 -87
  132. opentrons/protocol_engine/commands/prepare_to_aspirate.py +24 -33
  133. opentrons/protocol_engine/commands/reload_labware.py +1 -1
  134. opentrons/protocol_engine/commands/retract_axis.py +1 -1
  135. opentrons/protocol_engine/commands/robot/__init__.py +69 -0
  136. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +86 -0
  137. opentrons/protocol_engine/commands/robot/common.py +18 -0
  138. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  139. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  140. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  141. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +77 -0
  142. opentrons/protocol_engine/commands/save_position.py +14 -5
  143. opentrons/protocol_engine/commands/set_rail_lights.py +1 -1
  144. opentrons/protocol_engine/commands/set_status_bar.py +1 -1
  145. opentrons/protocol_engine/commands/temperature_module/deactivate.py +1 -1
  146. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +1 -1
  147. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +10 -4
  148. opentrons/protocol_engine/commands/thermocycler/close_lid.py +1 -1
  149. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +1 -1
  150. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +1 -1
  151. opentrons/protocol_engine/commands/thermocycler/open_lid.py +1 -1
  152. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +8 -2
  153. opentrons/protocol_engine/commands/thermocycler/run_profile.py +9 -3
  154. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +11 -4
  155. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +1 -1
  156. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +1 -1
  157. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +1 -1
  158. opentrons/protocol_engine/commands/touch_tip.py +65 -16
  159. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +4 -1
  160. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -3
  161. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +1 -4
  162. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +1 -4
  163. opentrons/protocol_engine/commands/verify_tip_presence.py +11 -4
  164. opentrons/protocol_engine/commands/wait_for_duration.py +10 -3
  165. opentrons/protocol_engine/commands/wait_for_resume.py +10 -3
  166. opentrons/protocol_engine/errors/__init__.py +8 -0
  167. opentrons/protocol_engine/errors/error_occurrence.py +19 -20
  168. opentrons/protocol_engine/errors/exceptions.py +50 -0
  169. opentrons/protocol_engine/execution/command_executor.py +1 -1
  170. opentrons/protocol_engine/execution/equipment.py +73 -5
  171. opentrons/protocol_engine/execution/gantry_mover.py +364 -8
  172. opentrons/protocol_engine/execution/movement.py +27 -0
  173. opentrons/protocol_engine/execution/pipetting.py +5 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +4 -6
  175. opentrons/protocol_engine/notes/notes.py +1 -1
  176. opentrons/protocol_engine/protocol_engine.py +7 -6
  177. opentrons/protocol_engine/resources/labware_data_provider.py +1 -1
  178. opentrons/protocol_engine/resources/labware_validation.py +5 -0
  179. opentrons/protocol_engine/resources/module_data_provider.py +1 -1
  180. opentrons/protocol_engine/resources/pipette_data_provider.py +26 -0
  181. opentrons/protocol_engine/slot_standardization.py +9 -9
  182. opentrons/protocol_engine/state/_move_types.py +9 -5
  183. opentrons/protocol_engine/state/_well_math.py +193 -0
  184. opentrons/protocol_engine/state/addressable_areas.py +25 -61
  185. opentrons/protocol_engine/state/command_history.py +12 -0
  186. opentrons/protocol_engine/state/commands.py +17 -13
  187. opentrons/protocol_engine/state/files.py +10 -12
  188. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +57 -32
  190. opentrons/protocol_engine/state/geometry.py +47 -1
  191. opentrons/protocol_engine/state/labware.py +79 -25
  192. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  193. opentrons/protocol_engine/state/liquids.py +16 -4
  194. opentrons/protocol_engine/state/modules.py +52 -70
  195. opentrons/protocol_engine/state/motion.py +6 -1
  196. opentrons/protocol_engine/state/pipettes.py +144 -58
  197. opentrons/protocol_engine/state/state.py +21 -2
  198. opentrons/protocol_engine/state/state_summary.py +4 -2
  199. opentrons/protocol_engine/state/tips.py +11 -44
  200. opentrons/protocol_engine/state/update_types.py +343 -48
  201. opentrons/protocol_engine/state/wells.py +19 -11
  202. opentrons/protocol_engine/types.py +176 -28
  203. opentrons/protocol_reader/extract_labware_definitions.py +5 -2
  204. opentrons/protocol_reader/file_format_validator.py +5 -5
  205. opentrons/protocol_runner/json_file_reader.py +9 -3
  206. opentrons/protocol_runner/json_translator.py +51 -25
  207. opentrons/protocol_runner/legacy_command_mapper.py +66 -64
  208. opentrons/protocol_runner/protocol_runner.py +35 -4
  209. opentrons/protocol_runner/python_protocol_wrappers.py +1 -1
  210. opentrons/protocol_runner/run_orchestrator.py +13 -3
  211. opentrons/protocols/advanced_control/common.py +38 -0
  212. opentrons/protocols/advanced_control/mix.py +1 -1
  213. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  214. opentrons/protocols/advanced_control/transfers/common.py +56 -0
  215. opentrons/protocols/advanced_control/{transfers.py → transfers/transfer.py} +10 -85
  216. opentrons/protocols/api_support/definitions.py +1 -1
  217. opentrons/protocols/api_support/instrument.py +1 -1
  218. opentrons/protocols/api_support/util.py +10 -0
  219. opentrons/protocols/labware.py +39 -6
  220. opentrons/protocols/models/json_protocol.py +5 -9
  221. opentrons/simulate.py +3 -1
  222. opentrons/types.py +162 -2
  223. opentrons/util/logging_config.py +1 -1
  224. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/METADATA +16 -15
  225. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/RECORD +229 -202
  226. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/WHEEL +1 -1
  227. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/LICENSE +0 -0
  228. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/entry_points.txt +0 -0
  229. {opentrons-8.2.0a4.dist-info → opentrons-8.3.0a1.dist-info}/top_level.txt +0 -0
@@ -1,24 +1,17 @@
1
1
  """Generate a JSON schema against which all create commands statically validate."""
2
+
2
3
  import json
3
- import pydantic
4
4
  import argparse
5
5
  import sys
6
- from opentrons.protocol_engine.commands.command_unions import CommandCreate
7
-
8
-
9
- class CreateCommandUnion(pydantic.BaseModel):
10
- """Model that validates a union of all CommandCreate models."""
11
-
12
- __root__: CommandCreate
6
+ from opentrons.protocol_engine.commands.command_unions import CommandCreateAdapter
13
7
 
14
8
 
15
9
  def generate_command_schema(version: str) -> str:
16
10
  """Generate a JSON Schema that all valid create commands can validate against."""
17
- raw_json_schema = CreateCommandUnion.schema_json()
18
- schema_as_dict = json.loads(raw_json_schema)
11
+ schema_as_dict = CommandCreateAdapter.json_schema(mode="validation")
19
12
  schema_as_dict["$id"] = f"opentronsCommandSchemaV{version}"
20
13
  schema_as_dict["$schema"] = "http://json-schema.org/draft-07/schema#"
21
- return json.dumps(schema_as_dict, indent=2)
14
+ return json.dumps(schema_as_dict, indent=2, sort_keys=True)
22
15
 
23
16
 
24
17
  if __name__ == "__main__":
@@ -0,0 +1,134 @@
1
+ """Get next tip command request, result, and implementation models."""
2
+
3
+ from __future__ import annotations
4
+ from pydantic import BaseModel, Field
5
+ from typing import TYPE_CHECKING, Any, Optional, Type, List, Literal, Union
6
+
7
+ from pydantic.json_schema import SkipJsonSchema
8
+
9
+ from opentrons.types import NozzleConfigurationType
10
+
11
+ from ..errors import ErrorOccurrence
12
+ from ..types import NextTipInfo, NoTipAvailable, NoTipReason
13
+ from .pipetting_common import PipetteIdMixin
14
+
15
+ from .command import (
16
+ AbstractCommandImpl,
17
+ BaseCommand,
18
+ BaseCommandCreate,
19
+ SuccessData,
20
+ )
21
+
22
+ if TYPE_CHECKING:
23
+ from ..state.state import StateView
24
+
25
+
26
+ def _remove_default(s: dict[str, Any]) -> None:
27
+ s.pop("default", None)
28
+
29
+
30
+ GetNextTipCommandType = Literal["getNextTip"]
31
+
32
+
33
+ class GetNextTipParams(PipetteIdMixin):
34
+ """Payload needed to resolve the next available tip."""
35
+
36
+ labwareIds: List[str] = Field(
37
+ ...,
38
+ description="Labware ID(s) of tip racks to resolve next available tip(s) from"
39
+ " Labware IDs will be resolved sequentially",
40
+ )
41
+ startingTipWell: str | SkipJsonSchema[None] = Field(
42
+ None,
43
+ description="Name of starting tip rack 'well'."
44
+ " This only applies to the first tip rack in the list provided in labwareIDs",
45
+ json_schema_extra=_remove_default,
46
+ )
47
+
48
+
49
+ class GetNextTipResult(BaseModel):
50
+ """Result data from the execution of a GetNextTip."""
51
+
52
+ nextTipInfo: Union[NextTipInfo, NoTipAvailable] = Field(
53
+ ...,
54
+ description="Labware ID and well name of next available tip for a pipette,"
55
+ " or information why no tip could be resolved.",
56
+ )
57
+
58
+
59
+ class GetNextTipImplementation(
60
+ AbstractCommandImpl[GetNextTipParams, SuccessData[GetNextTipResult]]
61
+ ):
62
+ """Get next tip command implementation."""
63
+
64
+ def __init__(
65
+ self,
66
+ state_view: StateView,
67
+ **kwargs: object,
68
+ ) -> None:
69
+ self._state_view = state_view
70
+
71
+ async def execute(self, params: GetNextTipParams) -> SuccessData[GetNextTipResult]:
72
+ """Get the next available tip for the requested pipette."""
73
+ pipette_id = params.pipetteId
74
+ starting_tip_name = params.startingTipWell
75
+
76
+ num_tips = self._state_view.tips.get_pipette_active_channels(pipette_id)
77
+ nozzle_map = self._state_view.tips.get_pipette_nozzle_map(pipette_id)
78
+
79
+ if (
80
+ starting_tip_name is not None
81
+ and nozzle_map.configuration != NozzleConfigurationType.FULL
82
+ ):
83
+ # This is to match the behavior found in PAPI, but also because we don't have logic to automatically find
84
+ # the next tip with partial configuration and a starting tip. This will never work for a 96-channel due to
85
+ # x-axis overlap, but could eventually work with 8-channel if we better define starting tip USED or CLEAN
86
+ # state when starting a protocol to prevent accidental tip pick-up with starting non-full tip racks.
87
+ return SuccessData(
88
+ public=GetNextTipResult(
89
+ nextTipInfo=NoTipAvailable(
90
+ noTipReason=NoTipReason.STARTING_TIP_WITH_PARTIAL,
91
+ message="Cannot automatically resolve next tip with starting tip and partial tip configuration.",
92
+ )
93
+ )
94
+ )
95
+
96
+ next_tip: Union[NextTipInfo, NoTipAvailable]
97
+ for labware_id in params.labwareIds:
98
+ well_name = self._state_view.tips.get_next_tip(
99
+ labware_id=labware_id,
100
+ num_tips=num_tips,
101
+ starting_tip_name=starting_tip_name,
102
+ nozzle_map=nozzle_map,
103
+ )
104
+ if well_name is not None:
105
+ next_tip = NextTipInfo(labwareId=labware_id, tipStartingWell=well_name)
106
+ break
107
+ # After the first tip rack is exhausted, starting tip no longer applies
108
+ starting_tip_name = None
109
+ else:
110
+ next_tip = NoTipAvailable(
111
+ noTipReason=NoTipReason.NO_AVAILABLE_TIPS,
112
+ message="No available tips for given pipette, nozzle configuration and provided tip racks.",
113
+ )
114
+
115
+ return SuccessData(public=GetNextTipResult(nextTipInfo=next_tip))
116
+
117
+
118
+ class GetNextTip(BaseCommand[GetNextTipParams, GetNextTipResult, ErrorOccurrence]):
119
+ """Get next tip command model."""
120
+
121
+ commandType: GetNextTipCommandType = "getNextTip"
122
+ params: GetNextTipParams
123
+ result: Optional[GetNextTipResult]
124
+
125
+ _ImplementationCls: Type[GetNextTipImplementation] = GetNextTipImplementation
126
+
127
+
128
+ class GetNextTipCreate(BaseCommandCreate[GetNextTipParams]):
129
+ """Get next tip command creation request model."""
130
+
131
+ commandType: GetNextTipCommandType = "getNextTip"
132
+ params: GetNextTipParams
133
+
134
+ _CommandCls: Type[GetNextTip] = GetNextTip
@@ -71,7 +71,7 @@ class GetTipPresence(
71
71
 
72
72
  commandType: GetTipPresenceCommandType = "getTipPresence"
73
73
  params: GetTipPresenceParams
74
- result: Optional[GetTipPresenceResult]
74
+ result: Optional[GetTipPresenceResult] = None
75
75
 
76
76
  _ImplementationCls: Type[
77
77
  GetTipPresenceImplementation
@@ -69,7 +69,7 @@ class CloseLabwareLatch(
69
69
 
70
70
  commandType: CloseLabwareLatchCommandType = "heaterShaker/closeLabwareLatch"
71
71
  params: CloseLabwareLatchParams
72
- result: Optional[CloseLabwareLatchResult]
72
+ result: Optional[CloseLabwareLatchResult] = None
73
73
 
74
74
  _ImplementationCls: Type[CloseLabwareLatchImpl] = CloseLabwareLatchImpl
75
75
 
@@ -68,7 +68,7 @@ class DeactivateHeater(
68
68
 
69
69
  commandType: DeactivateHeaterCommandType = "heaterShaker/deactivateHeater"
70
70
  params: DeactivateHeaterParams
71
- result: Optional[DeactivateHeaterResult]
71
+ result: Optional[DeactivateHeaterResult] = None
72
72
 
73
73
  _ImplementationCls: Type[DeactivateHeaterImpl] = DeactivateHeaterImpl
74
74
 
@@ -70,7 +70,7 @@ class DeactivateShaker(
70
70
 
71
71
  commandType: DeactivateShakerCommandType = "heaterShaker/deactivateShaker"
72
72
  params: DeactivateShakerParams
73
- result: Optional[DeactivateShakerResult]
73
+ result: Optional[DeactivateShakerResult] = None
74
74
 
75
75
  _ImplementationCls: Type[DeactivateShakerImpl] = DeactivateShakerImpl
76
76
 
@@ -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,24 @@ 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,
19
22
  )
20
23
 
21
24
  from ..types import DeckPoint
22
25
  from .pipetting_common import (
23
26
  LiquidNotFoundError,
24
27
  PipetteIdMixin,
28
+ )
29
+ from .movement_common import (
25
30
  WellLocationMixin,
26
31
  DestinationPositionResult,
32
+ StallOrCollisionError,
33
+ move_to_well,
27
34
  )
28
35
  from .command import (
29
36
  AbstractCommandImpl,
@@ -41,6 +48,10 @@ if TYPE_CHECKING:
41
48
  from ..state.state import StateView
42
49
 
43
50
 
51
+ def _remove_default(s: dict[str, Any]) -> None:
52
+ s.pop("default", None)
53
+
54
+
44
55
  LiquidProbeCommandType = Literal["liquidProbe"]
45
56
  TryLiquidProbeCommandType = Literal["tryLiquidProbe"]
46
57
 
@@ -76,20 +87,23 @@ class LiquidProbeResult(DestinationPositionResult):
76
87
  class TryLiquidProbeResult(DestinationPositionResult):
77
88
  """Result data from the execution of a `tryLiquidProbe` command."""
78
89
 
79
- z_position: Optional[float] = Field(
90
+ z_position: float | SkipJsonSchema[None] = Field(
80
91
  ...,
81
92
  description=(
82
93
  "The Z coordinate, in mm, of the found liquid in deck space."
83
94
  " If no liquid was found, `null` or omitted."
84
95
  ),
96
+ json_schema_extra=_remove_default,
85
97
  )
86
98
 
87
99
 
88
100
  _LiquidProbeExecuteReturn = Union[
89
101
  SuccessData[LiquidProbeResult],
90
- DefinedErrorData[LiquidNotFoundError],
102
+ DefinedErrorData[LiquidNotFoundError] | DefinedErrorData[StallOrCollisionError],
91
103
  ]
92
- _TryLiquidProbeExecuteReturn = SuccessData[TryLiquidProbeResult]
104
+ _TryLiquidProbeExecuteReturn = (
105
+ SuccessData[TryLiquidProbeResult] | DefinedErrorData[StallOrCollisionError]
106
+ )
93
107
 
94
108
 
95
109
  class _ExecuteCommonResult(NamedTuple):
@@ -106,13 +120,24 @@ async def _execute_common(
106
120
  state_view: StateView,
107
121
  movement: MovementHandler,
108
122
  pipetting: PipettingHandler,
123
+ model_utils: ModelUtils,
109
124
  params: _CommonParams,
110
- ) -> _ExecuteCommonResult:
125
+ ) -> _ExecuteCommonResult | DefinedErrorData[StallOrCollisionError]:
111
126
  pipette_id = params.pipetteId
112
127
  labware_id = params.labwareId
113
128
  well_name = params.wellName
129
+ if (
130
+ "pressure"
131
+ not in state_view.pipettes.get_config(pipette_id).available_sensors.sensors
132
+ ):
133
+ raise UnsupportedHardwareCommand(
134
+ "Pressure sensor not available for this pipette"
135
+ )
114
136
 
115
- state_update = update_types.StateUpdate()
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
+ )
116
141
 
117
142
  # May raise TipNotAttachedError.
118
143
  aspirated_volume = state_view.pipettes.get_aspirated_volume(pipette_id)
@@ -137,20 +162,16 @@ async def _execute_common(
137
162
  )
138
163
 
139
164
  # liquid_probe process start position
140
- position = await movement.move_to_well(
165
+ move_result = await move_to_well(
166
+ movement=movement,
167
+ model_utils=model_utils,
141
168
  pipette_id=pipette_id,
142
169
  labware_id=labware_id,
143
170
  well_name=well_name,
144
171
  well_location=params.wellLocation,
145
172
  )
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
-
173
+ if isinstance(move_result, DefinedErrorData):
174
+ return move_result
154
175
  try:
155
176
  z_pos = await pipetting.liquid_probe_in_place(
156
177
  pipette_id=pipette_id,
@@ -160,11 +181,15 @@ async def _execute_common(
160
181
  )
161
182
  except PipetteLiquidNotFoundError as exception:
162
183
  return _ExecuteCommonResult(
163
- 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,
164
187
  )
165
188
  else:
166
189
  return _ExecuteCommonResult(
167
- 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,
168
193
  )
169
194
 
170
195
 
@@ -202,9 +227,16 @@ class LiquidProbeImplementation(
202
227
  MustHomeError: as an undefined error, if the plunger is not in a valid
203
228
  position.
204
229
  """
205
- z_pos_or_error, state_update, deck_point = await _execute_common(
206
- 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,
207
236
  )
237
+ if isinstance(result, DefinedErrorData):
238
+ return result
239
+ z_pos_or_error, state_update, deck_point = result
208
240
  if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
209
241
  state_update.set_liquid_probed(
210
242
  labware_id=params.labwareId,
@@ -278,9 +310,16 @@ class TryLiquidProbeImplementation(
278
310
  found, `tryLiquidProbe` returns a success result with `z_position=null` instead
279
311
  of a defined error.
280
312
  """
281
- z_pos_or_error, state_update, deck_point = await _execute_common(
282
- 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,
283
319
  )
320
+ if isinstance(result, DefinedErrorData):
321
+ return result
322
+ z_pos_or_error, state_update, deck_point = result
284
323
 
285
324
  if isinstance(z_pos_or_error, PipetteLiquidNotFoundError):
286
325
  z_pos = None
@@ -312,7 +351,11 @@ class TryLiquidProbeImplementation(
312
351
 
313
352
 
314
353
  class LiquidProbe(
315
- BaseCommand[LiquidProbeParams, LiquidProbeResult, LiquidNotFoundError]
354
+ BaseCommand[
355
+ LiquidProbeParams,
356
+ LiquidProbeResult,
357
+ LiquidNotFoundError | StallOrCollisionError,
358
+ ]
316
359
  ):
317
360
  """The model for a full `liquidProbe` command."""
318
361
 
@@ -324,7 +367,7 @@ class LiquidProbe(
324
367
 
325
368
 
326
369
  class TryLiquidProbe(
327
- BaseCommand[TryLiquidProbeParams, TryLiquidProbeResult, ErrorOccurrence]
370
+ BaseCommand[TryLiquidProbeParams, TryLiquidProbeResult, StallOrCollisionError]
328
371
  ):
329
372
  """The model for a full `tryLiquidProbe` command."""
330
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