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
@@ -177,11 +177,13 @@ class LegacyCommandMapper:
177
177
  completed_command: pe_commands.Command
178
178
  if command_error is None:
179
179
  if isinstance(running_command, pe_commands.PickUpTip):
180
- completed_command = running_command.copy(
180
+ completed_command = running_command.model_copy(
181
181
  update={
182
- "result": pe_commands.PickUpTipResult.construct(
182
+ "result": pe_commands.PickUpTipResult.model_construct(
183
183
  tipVolume=command["payload"]["location"].max_volume, # type: ignore[typeddict-item]
184
- tipLength=command["payload"]["instrument"].hw_pipette["tip_length"], # type: ignore[typeddict-item]
184
+ tipLength=command["payload"]["instrument"].hw_pipette[ # type: ignore[typeddict-item]
185
+ "tip_length"
186
+ ],
185
187
  position=pe_types.DeckPoint(x=0, y=0, z=0),
186
188
  ),
187
189
  "status": pe_commands.CommandStatus.SUCCEEDED,
@@ -190,9 +192,9 @@ class LegacyCommandMapper:
190
192
  }
191
193
  )
192
194
  elif isinstance(running_command, pe_commands.DropTip):
193
- completed_command = running_command.copy(
195
+ completed_command = running_command.model_copy(
194
196
  update={
195
- "result": pe_commands.DropTipResult.construct(
197
+ "result": pe_commands.DropTipResult.model_construct(
196
198
  position=pe_types.DeckPoint(x=0, y=0, z=0)
197
199
  ),
198
200
  "status": pe_commands.CommandStatus.SUCCEEDED,
@@ -201,9 +203,9 @@ class LegacyCommandMapper:
201
203
  }
202
204
  )
203
205
  elif isinstance(running_command, pe_commands.Aspirate):
204
- completed_command = running_command.copy(
206
+ completed_command = running_command.model_copy(
205
207
  update={
206
- # Don't .construct() result, because we want to validate
208
+ # Don't .model_construct() result, because we want to validate
207
209
  # volume.
208
210
  "result": pe_commands.AspirateResult(
209
211
  volume=running_command.params.volume,
@@ -215,9 +217,9 @@ class LegacyCommandMapper:
215
217
  }
216
218
  )
217
219
  elif isinstance(running_command, pe_commands.Dispense):
218
- completed_command = running_command.copy(
220
+ completed_command = running_command.model_copy(
219
221
  update={
220
- # Don't .construct() result, because we want to validate
222
+ # Don't .model_construct() result, because we want to validate
221
223
  # volume.
222
224
  "result": pe_commands.DispenseResult(
223
225
  volume=running_command.params.volume,
@@ -229,9 +231,9 @@ class LegacyCommandMapper:
229
231
  }
230
232
  )
231
233
  elif isinstance(running_command, pe_commands.BlowOut):
232
- completed_command = running_command.copy(
234
+ completed_command = running_command.model_copy(
233
235
  update={
234
- "result": pe_commands.BlowOutResult.construct(
236
+ "result": pe_commands.BlowOutResult.model_construct(
235
237
  position=pe_types.DeckPoint(x=0, y=0, z=0)
236
238
  ),
237
239
  "status": pe_commands.CommandStatus.SUCCEEDED,
@@ -240,18 +242,18 @@ class LegacyCommandMapper:
240
242
  }
241
243
  )
242
244
  elif isinstance(running_command, pe_commands.Comment):
243
- completed_command = running_command.copy(
245
+ completed_command = running_command.model_copy(
244
246
  update={
245
- "result": pe_commands.CommentResult.construct(),
247
+ "result": pe_commands.CommentResult.model_construct(),
246
248
  "status": pe_commands.CommandStatus.SUCCEEDED,
247
249
  "completedAt": now,
248
250
  "notes": [],
249
251
  }
250
252
  )
251
253
  elif isinstance(running_command, pe_commands.Custom):
252
- completed_command = running_command.copy(
254
+ completed_command = running_command.model_copy(
253
255
  update={
254
- "result": pe_commands.CustomResult.construct(),
256
+ "result": pe_commands.CustomResult.model_construct(),
255
257
  "status": pe_commands.CommandStatus.SUCCEEDED,
256
258
  "completedAt": now,
257
259
  "notes": [],
@@ -261,7 +263,7 @@ class LegacyCommandMapper:
261
263
  # TODO(mm, 2024-06-13): This looks potentially wrong.
262
264
  # We're creating a `SUCCEEDED` command that does not have a `result`,
263
265
  # which is not normally possible.
264
- completed_command = running_command.copy(
266
+ completed_command = running_command.model_copy(
265
267
  update={
266
268
  "status": pe_commands.CommandStatus.SUCCEEDED,
267
269
  "completedAt": now,
@@ -331,51 +333,51 @@ class LegacyCommandMapper:
331
333
  elif command["name"] == legacy_command_types.BLOW_OUT:
332
334
  return self._build_blow_out(command=command, command_id=command_id, now=now)
333
335
  elif command["name"] == legacy_command_types.PAUSE:
334
- wait_for_resume_running = pe_commands.WaitForResume.construct(
336
+ wait_for_resume_running = pe_commands.WaitForResume.model_construct(
335
337
  id=command_id,
336
338
  key=command_id,
337
339
  status=pe_commands.CommandStatus.RUNNING,
338
340
  createdAt=now,
339
341
  startedAt=now,
340
- params=pe_commands.WaitForResumeParams.construct(
342
+ params=pe_commands.WaitForResumeParams.model_construct(
341
343
  message=command["payload"]["userMessage"],
342
344
  ),
343
345
  )
344
346
  wait_for_resume_create: pe_commands.CommandCreate = (
345
- pe_commands.WaitForResumeCreate.construct(
347
+ pe_commands.WaitForResumeCreate.model_construct(
346
348
  key=wait_for_resume_running.key,
347
349
  params=wait_for_resume_running.params,
348
350
  )
349
351
  )
350
352
  return wait_for_resume_create, wait_for_resume_running
351
353
  elif command["name"] == legacy_command_types.COMMENT:
352
- comment_running = pe_commands.Comment.construct(
354
+ comment_running = pe_commands.Comment.model_construct(
353
355
  id=command_id,
354
356
  key=command_id,
355
357
  status=pe_commands.CommandStatus.RUNNING,
356
358
  createdAt=now,
357
359
  startedAt=now,
358
- params=pe_commands.CommentParams.construct(
360
+ params=pe_commands.CommentParams.model_construct(
359
361
  message=command["payload"]["text"],
360
362
  ),
361
363
  )
362
- comment_create = pe_commands.CommentCreate.construct(
364
+ comment_create = pe_commands.CommentCreate.model_construct(
363
365
  key=comment_running.key, params=comment_running.params
364
366
  )
365
367
  return comment_create, comment_running
366
368
  else:
367
- custom_running = pe_commands.Custom.construct(
369
+ custom_running = pe_commands.Custom.model_construct(
368
370
  id=command_id,
369
371
  key=command_id,
370
372
  status=pe_commands.CommandStatus.RUNNING,
371
373
  createdAt=now,
372
374
  startedAt=now,
373
- params=LegacyCommandParams.construct(
375
+ params=LegacyCommandParams.model_construct(
374
376
  legacyCommandType=command["name"],
375
377
  legacyCommandText=command["payload"]["text"],
376
378
  ),
377
379
  )
378
- custom_create = pe_commands.CustomCreate.construct(
380
+ custom_create = pe_commands.CustomCreate.model_construct(
379
381
  key=custom_running.key,
380
382
  params=custom_running.params,
381
383
  )
@@ -396,19 +398,19 @@ class LegacyCommandMapper:
396
398
  labware_id = self._labware_id_by_slot[slot]
397
399
  pipette_id = self._pipette_id_by_mount[mount]
398
400
 
399
- running = pe_commands.DropTip.construct(
401
+ running = pe_commands.DropTip.model_construct(
400
402
  id=command_id,
401
403
  key=command_id,
402
404
  status=pe_commands.CommandStatus.RUNNING,
403
405
  createdAt=now,
404
406
  startedAt=now,
405
- params=pe_commands.DropTipParams.construct(
407
+ params=pe_commands.DropTipParams.model_construct(
406
408
  pipetteId=pipette_id,
407
409
  labwareId=labware_id,
408
410
  wellName=well_name,
409
411
  ),
410
412
  )
411
- create = pe_commands.DropTipCreate.construct(
413
+ create = pe_commands.DropTipCreate.model_construct(
412
414
  key=running.key,
413
415
  params=running.params,
414
416
  )
@@ -430,19 +432,19 @@ class LegacyCommandMapper:
430
432
  labware_id = self._labware_id_by_slot[slot]
431
433
  pipette_id = self._pipette_id_by_mount[mount]
432
434
 
433
- running = pe_commands.PickUpTip.construct(
435
+ running = pe_commands.PickUpTip.model_construct(
434
436
  id=command_id,
435
437
  key=command_id,
436
438
  status=pe_commands.CommandStatus.RUNNING,
437
439
  createdAt=now,
438
440
  startedAt=now,
439
- params=pe_commands.PickUpTipParams.construct(
441
+ params=pe_commands.PickUpTipParams.model_construct(
440
442
  pipetteId=pipette_id,
441
443
  labwareId=labware_id,
442
444
  wellName=well_name,
443
445
  ),
444
446
  )
445
- create = pe_commands.PickUpTipCreate.construct(
447
+ create = pe_commands.PickUpTipCreate.model_construct(
446
448
  key=running.key, params=running.params
447
449
  )
448
450
  return create, running
@@ -482,31 +484,31 @@ class LegacyCommandMapper:
482
484
  # TODO(mm, 2024-03-22): I don't think this has been true since
483
485
  # https://github.com/Opentrons/opentrons/pull/14211. Can we just use
484
486
  # aspirate and dispense commands now?
485
- move_to_well_running = pe_commands.MoveToWell.construct(
487
+ move_to_well_running = pe_commands.MoveToWell.model_construct(
486
488
  id=command_id,
487
489
  key=command_id,
488
490
  status=pe_commands.CommandStatus.RUNNING,
489
491
  createdAt=now,
490
492
  startedAt=now,
491
- params=pe_commands.MoveToWellParams.construct(
493
+ params=pe_commands.MoveToWellParams.model_construct(
492
494
  pipetteId=pipette_id,
493
495
  labwareId=labware_id,
494
496
  wellName=well_name,
495
497
  ),
496
498
  )
497
- move_to_well_create = pe_commands.MoveToWellCreate.construct(
499
+ move_to_well_create = pe_commands.MoveToWellCreate.model_construct(
498
500
  key=move_to_well_running.key, params=move_to_well_running.params
499
501
  )
500
502
  return move_to_well_create, move_to_well_running
501
503
  elif command["name"] == legacy_command_types.ASPIRATE:
502
504
  flow_rate = command["payload"]["rate"] * pipette.flow_rate.aspirate
503
- aspirate_running = pe_commands.Aspirate.construct(
505
+ aspirate_running = pe_commands.Aspirate.model_construct(
504
506
  id=command_id,
505
507
  key=command_id,
506
508
  status=pe_commands.CommandStatus.RUNNING,
507
509
  createdAt=now,
508
510
  startedAt=now,
509
- # Don't .construct() params, because we want to validate
511
+ # Don't .model_construct() params, because we want to validate
510
512
  # volume and flowRate.
511
513
  params=pe_commands.AspirateParams(
512
514
  pipetteId=pipette_id,
@@ -516,19 +518,19 @@ class LegacyCommandMapper:
516
518
  flowRate=flow_rate,
517
519
  ),
518
520
  )
519
- aspirate_create = pe_commands.AspirateCreate.construct(
521
+ aspirate_create = pe_commands.AspirateCreate.model_construct(
520
522
  key=aspirate_running.key, params=aspirate_running.params
521
523
  )
522
524
  return aspirate_create, aspirate_running
523
525
  else:
524
526
  flow_rate = command["payload"]["rate"] * pipette.flow_rate.dispense
525
- dispense_running = pe_commands.Dispense.construct(
527
+ dispense_running = pe_commands.Dispense.model_construct(
526
528
  id=command_id,
527
529
  key=command_id,
528
530
  status=pe_commands.CommandStatus.RUNNING,
529
531
  createdAt=now,
530
532
  startedAt=now,
531
- # Don't .construct params, because we want to validate
533
+ # Don't .model_construct params, because we want to validate
532
534
  # volume and flowRate.
533
535
  params=pe_commands.DispenseParams(
534
536
  pipetteId=pipette_id,
@@ -538,24 +540,24 @@ class LegacyCommandMapper:
538
540
  flowRate=flow_rate,
539
541
  ),
540
542
  )
541
- dispense_create = pe_commands.DispenseCreate.construct(
543
+ dispense_create = pe_commands.DispenseCreate.model_construct(
542
544
  key=dispense_running.key, params=dispense_running.params
543
545
  )
544
546
  return dispense_create, dispense_running
545
547
 
546
548
  else:
547
- running = pe_commands.Custom.construct(
549
+ running = pe_commands.Custom.model_construct(
548
550
  id=command_id,
549
551
  key=command_id,
550
552
  status=pe_commands.CommandStatus.RUNNING,
551
553
  createdAt=now,
552
554
  startedAt=now,
553
- params=LegacyCommandParams.construct(
555
+ params=LegacyCommandParams.model_construct(
554
556
  legacyCommandType=command["name"],
555
557
  legacyCommandText=command["payload"]["text"],
556
558
  ),
557
559
  )
558
- create = pe_commands.CustomCreate.construct(
560
+ create = pe_commands.CustomCreate.model_construct(
559
561
  key=running.key, params=running.params
560
562
  )
561
563
  return create, running
@@ -584,13 +586,13 @@ class LegacyCommandMapper:
584
586
  well_name = well.well_name
585
587
  pipette_id = self._pipette_id_by_mount[mount]
586
588
 
587
- blow_out_running = pe_commands.BlowOut.construct(
589
+ blow_out_running = pe_commands.BlowOut.model_construct(
588
590
  id=command_id,
589
591
  key=command_id,
590
592
  status=pe_commands.CommandStatus.RUNNING,
591
593
  createdAt=now,
592
594
  startedAt=now,
593
- # Don't .construct() params, because we want to validate flowRate.
595
+ # Don't .model_construct() params, because we want to validate flowRate.
594
596
  params=pe_commands.BlowOutParams(
595
597
  pipetteId=pipette_id,
596
598
  labwareId=labware_id,
@@ -598,7 +600,7 @@ class LegacyCommandMapper:
598
600
  flowRate=flow_rate,
599
601
  ),
600
602
  )
601
- blow_out_create = pe_commands.BlowOutCreate.construct(
603
+ blow_out_create = pe_commands.BlowOutCreate.model_construct(
602
604
  key=blow_out_running.key, params=blow_out_running.params
603
605
  )
604
606
  return blow_out_create, blow_out_running
@@ -606,18 +608,18 @@ class LegacyCommandMapper:
606
608
  # TODO:(jr, 15.08.2022): blow_out commands with no specified labware get filtered
607
609
  # into custom. Refactor this in followup legacy command mapping
608
610
  else:
609
- custom_running = pe_commands.Custom.construct(
611
+ custom_running = pe_commands.Custom.model_construct(
610
612
  id=command_id,
611
613
  key=command_id,
612
614
  status=pe_commands.CommandStatus.RUNNING,
613
615
  createdAt=now,
614
616
  startedAt=now,
615
- params=LegacyCommandParams.construct(
617
+ params=LegacyCommandParams.model_construct(
616
618
  legacyCommandType=command["name"],
617
619
  legacyCommandText=command["payload"]["text"],
618
620
  ),
619
621
  )
620
- custom_create = pe_commands.CustomCreate.construct(
622
+ custom_create = pe_commands.CustomCreate.model_construct(
621
623
  key=custom_running.key, params=custom_running.params
622
624
  )
623
625
  return custom_create, custom_running
@@ -631,23 +633,23 @@ class LegacyCommandMapper:
631
633
  slot = labware_load_info.deck_slot
632
634
  location: pe_types.LabwareLocation
633
635
  if labware_load_info.on_module:
634
- location = pe_types.ModuleLocation.construct(
636
+ location = pe_types.ModuleLocation.model_construct(
635
637
  moduleId=self._module_id_by_slot[slot]
636
638
  )
637
639
  else:
638
- location = pe_types.DeckSlotLocation.construct(slotName=slot)
640
+ location = pe_types.DeckSlotLocation.model_construct(slotName=slot)
639
641
 
640
642
  command_id = f"commands.LOAD_LABWARE-{count}"
641
643
  labware_id = f"labware-{count}"
642
644
 
643
- succeeded_command = pe_commands.LoadLabware.construct(
645
+ succeeded_command = pe_commands.LoadLabware.model_construct(
644
646
  id=command_id,
645
647
  key=command_id,
646
648
  status=pe_commands.CommandStatus.SUCCEEDED,
647
649
  createdAt=now,
648
650
  startedAt=now,
649
651
  completedAt=now,
650
- params=pe_commands.LoadLabwareParams.construct(
652
+ params=pe_commands.LoadLabwareParams.model_construct(
651
653
  location=location,
652
654
  loadName=labware_load_info.labware_load_name,
653
655
  namespace=labware_load_info.labware_namespace,
@@ -655,9 +657,9 @@ class LegacyCommandMapper:
655
657
  displayName=labware_load_info.labware_display_name,
656
658
  ),
657
659
  notes=[],
658
- result=pe_commands.LoadLabwareResult.construct(
660
+ result=pe_commands.LoadLabwareResult.model_construct(
659
661
  labwareId=labware_id,
660
- definition=LabwareDefinition.parse_obj(
662
+ definition=LabwareDefinition.model_validate(
661
663
  labware_load_info.labware_definition
662
664
  ),
663
665
  offsetId=labware_load_info.offset_id,
@@ -666,7 +668,7 @@ class LegacyCommandMapper:
666
668
  queue_action = pe_actions.QueueCommandAction(
667
669
  command_id=succeeded_command.id,
668
670
  created_at=succeeded_command.createdAt,
669
- request=pe_commands.LoadLabwareCreate.construct(
671
+ request=pe_commands.LoadLabwareCreate.model_construct(
670
672
  key=succeeded_command.key, params=succeeded_command.params
671
673
  ),
672
674
  request_hash=None,
@@ -714,19 +716,19 @@ class LegacyCommandMapper:
714
716
  pipette_id = f"pipette-{count}"
715
717
  mount = MountType(str(instrument_load_info.mount).lower())
716
718
 
717
- succeeded_command = pe_commands.LoadPipette.construct(
719
+ succeeded_command = pe_commands.LoadPipette.model_construct(
718
720
  id=command_id,
719
721
  key=command_id,
720
722
  status=pe_commands.CommandStatus.SUCCEEDED,
721
723
  createdAt=now,
722
724
  startedAt=now,
723
725
  completedAt=now,
724
- params=pe_commands.LoadPipetteParams.construct(
726
+ params=pe_commands.LoadPipetteParams.model_construct(
725
727
  pipetteName=PipetteNameType(instrument_load_info.instrument_load_name),
726
728
  mount=mount,
727
729
  ),
728
730
  notes=[],
729
- result=pe_commands.LoadPipetteResult.construct(pipetteId=pipette_id),
731
+ result=pe_commands.LoadPipetteResult.model_construct(pipetteId=pipette_id),
730
732
  )
731
733
  serial = instrument_load_info.pipette_dict.get("pipette_id", None) or ""
732
734
  state_update = StateUpdate()
@@ -749,7 +751,7 @@ class LegacyCommandMapper:
749
751
  queue_action = pe_actions.QueueCommandAction(
750
752
  command_id=succeeded_command.id,
751
753
  created_at=succeeded_command.createdAt,
752
- request=pe_commands.LoadPipetteCreate.construct(
754
+ request=pe_commands.LoadPipetteCreate.model_construct(
753
755
  key=succeeded_command.key, params=succeeded_command.params
754
756
  ),
755
757
  request_hash=None,
@@ -791,14 +793,14 @@ class LegacyCommandMapper:
791
793
  loaded_model
792
794
  ) or self._module_data_provider.get_definition(loaded_model)
793
795
 
794
- succeeded_command = pe_commands.LoadModule.construct(
796
+ succeeded_command = pe_commands.LoadModule.model_construct(
795
797
  id=command_id,
796
798
  key=command_id,
797
799
  status=pe_commands.CommandStatus.SUCCEEDED,
798
800
  createdAt=now,
799
801
  startedAt=now,
800
802
  completedAt=now,
801
- params=pe_commands.LoadModuleParams.construct(
803
+ params=pe_commands.LoadModuleParams.model_construct(
802
804
  model=requested_model,
803
805
  location=pe_types.DeckSlotLocation(
804
806
  slotName=module_load_info.deck_slot,
@@ -806,7 +808,7 @@ class LegacyCommandMapper:
806
808
  moduleId=module_id,
807
809
  ),
808
810
  notes=[],
809
- result=pe_commands.LoadModuleResult.construct(
811
+ result=pe_commands.LoadModuleResult.model_construct(
810
812
  moduleId=module_id,
811
813
  serialNumber=module_load_info.module_serial,
812
814
  definition=loaded_definition,
@@ -816,7 +818,7 @@ class LegacyCommandMapper:
816
818
  queue_action = pe_actions.QueueCommandAction(
817
819
  command_id=succeeded_command.id,
818
820
  created_at=succeeded_command.createdAt,
819
- request=pe_commands.LoadModuleCreate.construct(
821
+ request=pe_commands.LoadModuleCreate.model_construct(
820
822
  key=succeeded_command.key, params=succeeded_command.params
821
823
  ),
822
824
  request_hash=None,
@@ -24,6 +24,7 @@ from opentrons.protocol_engine import (
24
24
  Command,
25
25
  commands as pe_commands,
26
26
  )
27
+ from opentrons.protocol_engine.types import CommandAnnotation
27
28
  from opentrons.protocols.parse import PythonParseMode
28
29
  from opentrons.util.async_helpers import asyncio_yield
29
30
  from opentrons.util.broker import Broker
@@ -56,6 +57,7 @@ class RunResult(NamedTuple):
56
57
  commands: List[Command]
57
58
  state_summary: StateSummary
58
59
  parameters: List[RunTimeParameter]
60
+ command_annotations: List[CommandAnnotation]
59
61
 
60
62
 
61
63
  class AbstractRunner(ABC):
@@ -93,6 +95,11 @@ class AbstractRunner(ABC):
93
95
  """Parameter definitions defined by protocol, if any. Currently only for python protocols."""
94
96
  return []
95
97
 
98
+ @property
99
+ def command_annotations(self) -> List[CommandAnnotation]:
100
+ """Command annotations defined by protocol, if any. Currently only for json protocols."""
101
+ return []
102
+
96
103
  def was_started(self) -> bool:
97
104
  """Whether the run has been started.
98
105
 
@@ -177,7 +184,7 @@ class PythonAndLegacyRunner(AbstractRunner):
177
184
 
178
185
  @property
179
186
  def run_time_parameters(self) -> List[RunTimeParameter]:
180
- """Parameter definitions defined by protocol, if any. Will always be empty before execution."""
187
+ """Parameter definitions defined by protocol, if any."""
181
188
  if self._parameter_context is not None:
182
189
  return self._parameter_context.export_parameters_for_analysis()
183
190
  return []
@@ -278,7 +285,10 @@ class PythonAndLegacyRunner(AbstractRunner):
278
285
  commands = self._protocol_engine.state_view.commands.get_all()
279
286
  parameters = self.run_time_parameters
280
287
  return RunResult(
281
- commands=commands, state_summary=run_data, parameters=parameters
288
+ commands=commands,
289
+ state_summary=run_data,
290
+ parameters=parameters,
291
+ command_annotations=[],
282
292
  )
283
293
 
284
294
 
@@ -313,6 +323,12 @@ class JsonRunner(AbstractRunner):
313
323
 
314
324
  hardware_api.should_taskify_movement_execution(taskify=False)
315
325
  self._queued_commands: List[pe_commands.CommandCreate] = []
326
+ self._command_annotations: List[CommandAnnotation] = []
327
+
328
+ @property
329
+ def command_annotations(self) -> List[CommandAnnotation]:
330
+ """Command annotations defined by protocol, if any."""
331
+ return self._command_annotations
316
332
 
317
333
  async def load(self, protocol_source: ProtocolSource) -> None:
318
334
  """Load a JSONv6+ ProtocolSource into managed ProtocolEngine."""
@@ -355,6 +371,11 @@ class JsonRunner(AbstractRunner):
355
371
  )
356
372
  await asyncio_yield()
357
373
 
374
+ self._command_annotations = await anyio.to_thread.run_sync(
375
+ self._json_translator.translate_command_annotations,
376
+ protocol,
377
+ )
378
+
358
379
  initial_home_command = pe_commands.HomeCreate(
359
380
  params=pe_commands.HomeParams(axes=None)
360
381
  )
@@ -382,7 +403,12 @@ class JsonRunner(AbstractRunner):
382
403
 
383
404
  run_data = self._protocol_engine.state_view.get_summary()
384
405
  commands = self._protocol_engine.state_view.commands.get_all()
385
- return RunResult(commands=commands, state_summary=run_data, parameters=[])
406
+ return RunResult(
407
+ commands=commands,
408
+ state_summary=run_data,
409
+ parameters=[],
410
+ command_annotations=self._command_annotations,
411
+ )
386
412
 
387
413
  async def _add_and_execute_commands(self) -> None:
388
414
  for command_request in self._queued_commands:
@@ -453,7 +479,12 @@ class LiveRunner(AbstractRunner):
453
479
 
454
480
  run_data = self._protocol_engine.state_view.get_summary()
455
481
  commands = self._protocol_engine.state_view.commands.get_all()
456
- return RunResult(commands=commands, state_summary=run_data, parameters=[])
482
+ return RunResult(
483
+ commands=commands,
484
+ state_summary=run_data,
485
+ parameters=[],
486
+ command_annotations=[],
487
+ )
457
488
 
458
489
 
459
490
  AnyRunner = Union[PythonAndLegacyRunner, JsonRunner, LiveRunner]
@@ -66,7 +66,7 @@ class PythonAndLegacyFileReader:
66
66
  namespace=lw.namespace,
67
67
  load_name=lw.parameters.loadName,
68
68
  version=lw.version,
69
- ): cast(LabwareDefinitionTypedDict, lw.dict(exclude_none=True))
69
+ ): cast(LabwareDefinitionTypedDict, lw.model_dump(exclude_none=True))
70
70
  for lw in labware_definitions
71
71
  }
72
72
  data_file_paths = [
@@ -1,11 +1,13 @@
1
1
  """Engine/Runner provider."""
2
+
2
3
  from __future__ import annotations
3
4
 
4
5
  import enum
5
- from typing import Optional, Union, List, Dict, AsyncGenerator
6
+ from typing import Optional, Union, List, Dict, AsyncGenerator, Mapping
6
7
 
7
8
  from anyio import move_on_after
8
9
 
10
+ from opentrons.types import NozzleMapInterface
9
11
  from opentrons_shared_data.labware.types import LabwareUri
10
12
  from opentrons_shared_data.labware.labware_definition import LabwareDefinition
11
13
  from opentrons_shared_data.errors import GeneralError
@@ -14,7 +16,6 @@ from opentrons_shared_data.robot.types import RobotType
14
16
  from . import protocol_runner, RunResult, JsonRunner, PythonAndLegacyRunner
15
17
  from ..hardware_control import HardwareControlAPI
16
18
  from ..hardware_control.modules import AbstractModule as HardwareModuleAPI
17
- from ..hardware_control.nozzle_manager import NozzleMap
18
19
  from ..protocol_engine import (
19
20
  ProtocolEngine,
20
21
  CommandCreate,
@@ -36,6 +37,7 @@ from ..protocol_engine.types import (
36
37
  RunTimeParameter,
37
38
  PrimitiveRunTimeParamValuesType,
38
39
  CSVRuntimeParamPaths,
40
+ CommandAnnotation,
39
41
  )
40
42
  from ..protocol_engine.error_recovery_policy import ErrorRecoveryPolicy
41
43
 
@@ -253,6 +255,14 @@ class RunOrchestrator:
253
255
  else self._protocol_runner.run_time_parameters
254
256
  )
255
257
 
258
+ def get_command_annotations(self) -> List[CommandAnnotation]:
259
+ """Get the list of command annotations defined in the protocol, if any."""
260
+ return (
261
+ []
262
+ if self._protocol_runner is None
263
+ else self._protocol_runner.command_annotations
264
+ )
265
+
256
266
  def get_current_command(self) -> Optional[CommandPointer]:
257
267
  """Get the "current" command, if any."""
258
268
  return self._protocol_engine.state_view.commands.get_current()
@@ -414,7 +424,7 @@ class RunOrchestrator:
414
424
  """Get engine deck type."""
415
425
  return self._protocol_engine.state_view.config.deck_type
416
426
 
417
- def get_nozzle_maps(self) -> Dict[str, NozzleMap]:
427
+ def get_nozzle_maps(self) -> Mapping[str, NozzleMapInterface]:
418
428
  """Get current nozzle maps keyed by pipette id."""
419
429
  return self._protocol_engine.state_view.tips.get_pipette_nozzle_maps()
420
430
 
@@ -0,0 +1,38 @@
1
+ """Common resources for all advanced control functions."""
2
+ import enum
3
+ from typing import NamedTuple, Optional
4
+
5
+
6
+ class MixStrategy(enum.Enum):
7
+ BOTH = enum.auto()
8
+ BEFORE = enum.auto()
9
+ AFTER = enum.auto()
10
+ NEVER = enum.auto()
11
+
12
+
13
+ class MixOpts(NamedTuple):
14
+ """
15
+ Options to customize behavior of mix.
16
+
17
+ These options will be passed to
18
+ :py:meth:`InstrumentContext.mix` when it is called during the
19
+ transfer.
20
+ """
21
+
22
+ repetitions: Optional[int] = None
23
+ volume: Optional[float] = None
24
+ rate: Optional[float] = None
25
+
26
+
27
+ MixOpts.repetitions.__doc__ = ":py:class:`int`"
28
+ MixOpts.volume.__doc__ = ":py:class:`float`"
29
+ MixOpts.rate.__doc__ = ":py:class:`float`"
30
+
31
+
32
+ class Mix(NamedTuple):
33
+ """
34
+ Options to control mix behavior before aspirate and after dispense.
35
+ """
36
+
37
+ mix_before: MixOpts = MixOpts()
38
+ mix_after: MixOpts = MixOpts()
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Dict, Tuple
2
2
 
3
- from opentrons.protocols.advanced_control.transfers import MixStrategy, Mix
3
+ from .common import MixStrategy, Mix
4
4
 
5
5
 
6
6
  def mix_from_kwargs(top_kwargs: Dict[str, Any]) -> Tuple[MixStrategy, Mix]: