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.

Potentially problematic release.


This version of opentrons might be problematic. Click here for more details.

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
@@ -45,6 +45,7 @@ from opentrons.hardware_control.types import (
45
45
  EstopPhysicalStatus,
46
46
  HardwareEventHandler,
47
47
  HardwareEventUnsubscriber,
48
+ PipetteSensorResponseQueue,
48
49
  )
49
50
 
50
51
  from opentrons_shared_data.pipette.types import PipetteName, PipetteModel
@@ -62,9 +63,9 @@ from opentrons.hardware_control.dev_types import (
62
63
  )
63
64
  from opentrons.util.async_helpers import ensure_yield
64
65
  from .types import HWStopCondition
65
- from .flex_protocol import FlexBackend
66
- from opentrons_hardware.firmware_bindings.constants import SensorId
67
- from opentrons_hardware.sensors.types import SensorDataType
66
+ from .flex_protocol import (
67
+ FlexBackend,
68
+ )
68
69
 
69
70
  log = logging.getLogger(__name__)
70
71
 
@@ -235,7 +236,11 @@ class OT3Simulator(FlexBackend):
235
236
  self._sim_gantry_load = gantry_load
236
237
 
237
238
  def update_constraints_for_plunger_acceleration(
238
- self, mount: OT3Mount, acceleration: float, gantry_load: GantryLoad
239
+ self,
240
+ mount: OT3Mount,
241
+ acceleration: float,
242
+ gantry_load: GantryLoad,
243
+ high_speed_pipette: bool = False,
239
244
  ) -> None:
240
245
  self._sim_gantry_load = gantry_load
241
246
 
@@ -348,11 +353,10 @@ class OT3Simulator(FlexBackend):
348
353
  threshold_pascals: float,
349
354
  plunger_impulse_time: float,
350
355
  num_baseline_reads: int,
356
+ z_offset_for_plunger_prep: float,
351
357
  probe: InstrumentProbeType = InstrumentProbeType.PRIMARY,
352
358
  force_both_sensors: bool = False,
353
- response_queue: Optional[
354
- asyncio.Queue[Dict[SensorId, List[SensorDataType]]]
355
- ] = None,
359
+ response_queue: Optional[PipetteSensorResponseQueue] = None,
356
360
  ) -> float:
357
361
  z_axis = Axis.by_mount(mount)
358
362
  pos = self._position
@@ -439,10 +443,12 @@ class OT3Simulator(FlexBackend):
439
443
  return self._sim_jaw_state
440
444
 
441
445
  async def tip_action(
442
- self, origin: Dict[Axis, float], targets: List[Tuple[Dict[Axis, float], float]]
446
+ self, origin: float, targets: List[Tuple[float, float]]
443
447
  ) -> None:
444
448
  self._gear_motor_position.update(
445
- coalesce_move_segments(origin, [target[0] for target in targets])
449
+ coalesce_move_segments(
450
+ {Axis.Q: origin}, [{Axis.Q: target[0]} for target in targets]
451
+ )
446
452
  )
447
453
  await asyncio.sleep(0)
448
454
 
@@ -505,6 +511,7 @@ class OT3Simulator(FlexBackend):
505
511
  converted_name.pipette_type,
506
512
  converted_name.pipette_channels,
507
513
  converted_name.pipette_version,
514
+ converted_name.oem_type,
508
515
  ),
509
516
  "id": None,
510
517
  }
@@ -527,6 +534,7 @@ class OT3Simulator(FlexBackend):
527
534
  converted_name.pipette_type,
528
535
  converted_name.pipette_channels,
529
536
  converted_name.pipette_version,
537
+ converted_name.oem_type,
530
538
  ),
531
539
  "id": init_instr["id"],
532
540
  }
@@ -538,6 +546,7 @@ class OT3Simulator(FlexBackend):
538
546
  converted_name.pipette_type,
539
547
  converted_name.pipette_channels,
540
548
  converted_name.pipette_version,
549
+ converted_name.oem_type,
541
550
  ),
542
551
  "id": None,
543
552
  }
@@ -2,7 +2,10 @@
2
2
  from typing import Dict, Iterable, List, Set, Tuple, TypeVar, cast, Sequence, Optional
3
3
  from typing_extensions import Literal
4
4
  from logging import getLogger
5
- from opentrons.config.defaults_ot3 import DEFAULT_CALIBRATION_AXIS_MAX_SPEED
5
+ from opentrons.config.defaults_ot3 import (
6
+ DEFAULT_CALIBRATION_AXIS_MAX_SPEED,
7
+ DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED,
8
+ )
6
9
  from opentrons.config.types import OT3MotionSettings, OT3CurrentSettings, GantryLoad
7
10
  from opentrons.hardware_control.types import (
8
11
  Axis,
@@ -281,12 +284,22 @@ def get_system_constraints_for_plunger_acceleration(
281
284
  gantry_load: GantryLoad,
282
285
  mount: OT3Mount,
283
286
  acceleration: float,
287
+ high_speed_pipette: bool = False,
284
288
  ) -> "SystemConstraints[Axis]":
285
289
  old_constraints = config.by_gantry_load(gantry_load)
286
290
  new_constraints = {}
287
291
  axis_kinds = set([k for _, v in old_constraints.items() for k in v.keys()])
292
+
293
+ def _get_axis_max_speed(ax: Axis) -> float:
294
+ if ax == Axis.of_main_tool_actuator(mount) and high_speed_pipette:
295
+ _max_speed = float(DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED)
296
+ else:
297
+ _max_speed = old_constraints["default_max_speed"][axis_kind]
298
+ return _max_speed
299
+
288
300
  for axis_kind in axis_kinds:
289
301
  for axis in Axis.of_kind(axis_kind):
302
+ _default_max_speed = _get_axis_max_speed(axis)
290
303
  if axis == Axis.of_main_tool_actuator(mount):
291
304
  _accel = acceleration
292
305
  else:
@@ -295,7 +308,32 @@ def get_system_constraints_for_plunger_acceleration(
295
308
  _accel,
296
309
  old_constraints["max_speed_discontinuity"][axis_kind],
297
310
  old_constraints["direction_change_speed_discontinuity"][axis_kind],
298
- old_constraints["default_max_speed"][axis_kind],
311
+ _default_max_speed,
312
+ )
313
+ return new_constraints
314
+
315
+
316
+ def get_system_constraints_for_emulsifying_pipette(
317
+ config: OT3MotionSettings,
318
+ gantry_load: GantryLoad,
319
+ mount: OT3Mount,
320
+ ) -> "SystemConstraints[Axis]":
321
+ old_constraints = config.by_gantry_load(gantry_load)
322
+ new_constraints = {}
323
+ axis_kinds = set([k for _, v in old_constraints.items() for k in v.keys()])
324
+ for axis_kind in axis_kinds:
325
+ for axis in Axis.of_kind(axis_kind):
326
+ if axis == Axis.of_main_tool_actuator(mount):
327
+ _max_speed = float(DEFAULT_EMULSIFYING_PIPETTE_AXIS_MAX_SPEED)
328
+ else:
329
+ _max_speed = old_constraints["default_max_speed"][axis_kind]
330
+ new_constraints[axis] = AxisConstraints.build(
331
+ max_acceleration=old_constraints["acceleration"][axis_kind],
332
+ max_speed_discont=old_constraints["max_speed_discontinuity"][axis_kind],
333
+ max_direction_change_speed_discont=old_constraints[
334
+ "direction_change_speed_discontinuity"
335
+ ][axis_kind],
336
+ max_speed=_max_speed,
299
337
  )
300
338
  return new_constraints
301
339
 
@@ -498,10 +536,10 @@ def create_gripper_jaw_hold_group(encoder_position_um: int) -> MoveGroup:
498
536
  return move_group
499
537
 
500
538
 
501
- def moving_pipettes_in_move_group(group: MoveGroup) -> List[NodeId]:
539
+ def moving_pipettes_in_move_group(
540
+ all_nodes: Set[NodeId], moving_nodes: Set[NodeId]
541
+ ) -> List[NodeId]:
502
542
  """Utility function to get which pipette nodes are moving either in z or their plunger."""
503
- all_nodes = [node for step in group for node, _ in step.items()]
504
- moving_nodes = moving_axes_in_move_group(group)
505
543
  pipettes_moving: List[NodeId] = [
506
544
  k for k in moving_nodes if k in [NodeId.pipette_left, NodeId.pipette_right]
507
545
  ]
@@ -512,16 +550,6 @@ def moving_pipettes_in_move_group(group: MoveGroup) -> List[NodeId]:
512
550
  return pipettes_moving
513
551
 
514
552
 
515
- def moving_axes_in_move_group(group: MoveGroup) -> Set[NodeId]:
516
- """Utility function to get only the moving nodes in a move group."""
517
- ret: Set[NodeId] = set()
518
- for step in group:
519
- for node, node_step in step.items():
520
- if node_step.is_moving_step():
521
- ret.add(node)
522
- return ret
523
-
524
-
525
553
  AxisMapPayload = TypeVar("AxisMapPayload")
526
554
 
527
555
 
@@ -20,6 +20,7 @@ from opentrons_shared_data.pipette.pipette_definition import (
20
20
  PipetteConfigurations,
21
21
  SupportedTipsDefinition,
22
22
  PipetteBoundingBoxOffsetDefinition,
23
+ AvailableSensorDefinition,
23
24
  )
24
25
  from opentrons_shared_data.gripper import (
25
26
  GripperModel,
@@ -100,6 +101,9 @@ class PipetteDict(InstrumentDict):
100
101
  pipette_bounding_box_offsets: PipetteBoundingBoxOffsetDefinition
101
102
  current_nozzle_map: NozzleMap
102
103
  lld_settings: Optional[Dict[str, Dict[str, float]]]
104
+ plunger_positions: Dict[str, float]
105
+ shaft_ul_per_mm: float
106
+ available_sensors: AvailableSensorDefinition
103
107
 
104
108
 
105
109
  class PipetteStateDict(TypedDict):
@@ -45,6 +45,7 @@ class HeaterShakerEmulator(AbstractEmulator):
45
45
  GCODE.HOME.value: self._home,
46
46
  GCODE.ENTER_BOOTLOADER.value: self._enter_bootloader,
47
47
  GCODE.GET_VERSION.value: self._get_version,
48
+ GCODE.GET_RESET_REASON.value: self._get_reset_reason,
48
49
  GCODE.OPEN_LABWARE_LATCH.value: self._open_labware_latch,
49
50
  GCODE.CLOSE_LABWARE_LATCH.value: self._close_labware_latch,
50
51
  GCODE.GET_LABWARE_LATCH_STATE.value: self._get_labware_latch_state,
@@ -126,6 +127,9 @@ class HeaterShakerEmulator(AbstractEmulator):
126
127
  f"SerialNo:{self._settings.serial_number}"
127
128
  )
128
129
 
130
+ def _get_reset_reason(self, command: Command) -> str:
131
+ return "M114 Last Reset Reason: 01"
132
+
129
133
  def _open_labware_latch(self, command: Command) -> str:
130
134
  self._latch_status = HeaterShakerLabwareLatchStatus.IDLE_OPEN
131
135
  return "M242"
@@ -66,7 +66,7 @@ class ModuleStatusClient:
66
66
  """Read a message from the module server."""
67
67
  try:
68
68
  b = await self._reader.readuntil(MessageDelimiter)
69
- m: Message = Message.parse_raw(b)
69
+ m: Message = Message.model_validate_json(b)
70
70
  return m
71
71
  except LimitOverrunError as e:
72
72
  raise ModuleServerClientError(str(e))
@@ -53,7 +53,9 @@ class ModuleStatusServer(ProxyListener):
53
53
  self._connections[identifier] = connection
54
54
  for c in self._clients:
55
55
  c.write(
56
- Message(status="connected", connections=[connection]).json().encode()
56
+ Message(status="connected", connections=[connection])
57
+ .model_dump_json()
58
+ .encode()
57
59
  )
58
60
  c.write(b"\n")
59
61
 
@@ -72,7 +74,7 @@ class ModuleStatusServer(ProxyListener):
72
74
  for c in self._clients:
73
75
  c.write(
74
76
  Message(status="disconnected", connections=[connection])
75
- .json()
77
+ .model_dump_json()
76
78
  .encode()
77
79
  )
78
80
  c.write(MessageDelimiter)
@@ -95,7 +97,7 @@ class ModuleStatusServer(ProxyListener):
95
97
  # A client connected. Send a dump of all connected modules.
96
98
  m = Message(status="dump", connections=list(self._connections.values()))
97
99
 
98
- writer.write(m.json().encode())
100
+ writer.write(m.model_dump_json().encode())
99
101
  writer.write(MessageDelimiter)
100
102
 
101
103
  self._clients.add(writer)
@@ -1,7 +1,8 @@
1
1
  from typing import List
2
2
  from opentrons.hardware_control.emulation.types import ModuleType
3
3
  from opentrons.hardware_control.emulation.util import TEMPERATURE_ROOM
4
- from pydantic import BaseSettings, BaseModel
4
+ from pydantic import BaseModel
5
+ from pydantic_settings import BaseSettings, SettingsConfigDict
5
6
 
6
7
 
7
8
  class PipetteSettings(BaseModel):
@@ -113,8 +114,6 @@ class Settings(BaseSettings):
113
114
  emulator_port=9003, driver_port=9998
114
115
  )
115
116
  magdeck_proxy: ProxySettings = ProxySettings(emulator_port=9004, driver_port=9999)
116
-
117
- class Config:
118
- env_prefix = "OT_EMULATOR_"
117
+ model_config = SettingsConfigDict(env_prefix="OT_EMULATOR_")
119
118
 
120
119
  module_server: ModuleServerSettings = ModuleServerSettings()
@@ -123,7 +123,8 @@ def load_tip_length_for_pipette(
123
123
  ) -> TipLengthCalibration:
124
124
  if isinstance(tiprack, LabwareDefinition):
125
125
  tiprack = typing.cast(
126
- "TypeDictLabwareDef", tiprack.dict(exclude_none=True, exclude_unset=True)
126
+ "TypeDictLabwareDef",
127
+ tiprack.model_dump(exclude_none=True, exclude_unset=True),
127
128
  )
128
129
 
129
130
  tip_length_data = calibration_storage.load_tip_length_calibration(
@@ -28,7 +28,7 @@ from opentrons_shared_data.errors.exceptions import (
28
28
  CommandPreconditionViolated,
29
29
  )
30
30
  from opentrons_shared_data.pipette.ul_per_mm import (
31
- piecewise_volume_conversion,
31
+ calculate_ul_per_mm,
32
32
  PIPETTING_FUNCTION_FALLBACK_VERSION,
33
33
  PIPETTING_FUNCTION_LATEST_VERSION,
34
34
  )
@@ -56,6 +56,7 @@ from opentrons_shared_data.pipette.types import (
56
56
  UlPerMmAction,
57
57
  PipetteName,
58
58
  PipetteModel,
59
+ PipetteOEMType,
59
60
  )
60
61
  from opentrons.hardware_control.dev_types import InstrumentHardwareConfigs
61
62
 
@@ -96,7 +97,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
96
97
  use_old_aspiration_functions: bool = False,
97
98
  ) -> None:
98
99
  self._config = config
99
- self._config_as_dict = config.dict()
100
+ self._config_as_dict = config.model_dump()
100
101
  self._pipette_offset = pipette_offset_cal
101
102
  self._pipette_type = self._config.pipette_type
102
103
  self._pipette_version = self._config.version
@@ -112,17 +113,20 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
112
113
  pipette_type=config.pipette_type,
113
114
  pipette_channels=config.channels,
114
115
  pipette_generation=config.display_category,
116
+ oem_type=PipetteOEMType.OT,
115
117
  )
116
118
  self._acting_as = self._pipette_name
117
119
  self._pipette_model = PipetteModelVersionType(
118
120
  pipette_type=config.pipette_type,
119
121
  pipette_channels=config.channels,
120
122
  pipette_version=config.version,
123
+ oem_type=PipetteOEMType.OT,
121
124
  )
122
125
  self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
123
126
  self._pipette_model.pipette_type,
124
127
  self._pipette_model.pipette_channels,
125
128
  self._pipette_model.pipette_version,
129
+ PipetteOEMType.OT,
126
130
  )
127
131
  self._nozzle_offset = self._config.nozzle_offset
128
132
  self._nozzle_manager = (
@@ -189,7 +193,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
189
193
  ], f"{self.name} is not back-compatible with {name}"
190
194
 
191
195
  liquid_model = load_pipette_data.load_liquid_model(
192
- name.pipette_type, name.pipette_channels, name.get_version()
196
+ name.pipette_type, name.pipette_channels, name.get_version(), name.oem_type
193
197
  )
194
198
  # TODO need to grab name config here to deal with act as test
195
199
  self._liquid_class.max_volume = liquid_model["default"].max_volume
@@ -273,15 +277,16 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
273
277
  self._config, elements, liquid_class
274
278
  )
275
279
  # Update the cached dict representation
276
- self._config_as_dict = self._config.dict()
280
+ self._config_as_dict = self._config.model_dump()
277
281
 
278
282
  def reload_configurations(self) -> None:
279
283
  self._config = load_pipette_data.load_definition(
280
284
  self._pipette_model.pipette_type,
281
285
  self._pipette_model.pipette_channels,
282
286
  self._pipette_model.pipette_version,
287
+ self._pipette_model.oem_type,
283
288
  )
284
- self._config_as_dict = self._config.dict()
289
+ self._config_as_dict = self._config.model_dump()
285
290
 
286
291
  def reset_state(self) -> None:
287
292
  self._current_volume = 0.0
@@ -584,21 +589,9 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
584
589
  # want this to unbounded.
585
590
  @functools.lru_cache(maxsize=100)
586
591
  def ul_per_mm(self, ul: float, action: UlPerMmAction) -> float:
587
- if action == "aspirate":
588
- fallback = self._active_tip_settings.aspirate.default[
589
- PIPETTING_FUNCTION_FALLBACK_VERSION
590
- ]
591
- sequence = self._active_tip_settings.aspirate.default.get(
592
- self._pipetting_function_version, fallback
593
- )
594
- else:
595
- fallback = self._active_tip_settings.dispense.default[
596
- PIPETTING_FUNCTION_FALLBACK_VERSION
597
- ]
598
- sequence = self._active_tip_settings.dispense.default.get(
599
- self._pipetting_function_version, fallback
600
- )
601
- return piecewise_volume_conversion(ul, sequence)
592
+ return calculate_ul_per_mm(
593
+ ul, action, self._active_tip_settings, self._pipetting_function_version
594
+ )
602
595
 
603
596
  def __str__(self) -> str:
604
597
  return "{} current volume {}ul critical point: {} at {}".format(
@@ -668,8 +661,8 @@ def _reload_and_check_skip(
668
661
  # Same config, good enough
669
662
  return attached_instr, True
670
663
  else:
671
- newdict = new_config.dict()
672
- olddict = attached_instr.config.dict()
664
+ newdict = new_config.model_dump()
665
+ olddict = attached_instr.config.model_dump()
673
666
  changed: Set[str] = set()
674
667
  for k in newdict.keys():
675
668
  if newdict[k] != olddict[k]:
@@ -230,7 +230,7 @@ class PipetteHandlerProvider(Generic[MountType]):
230
230
  result["current_nozzle_map"] = instr.nozzle_manager.current_configuration
231
231
  result["min_volume"] = instr.liquid_class.min_volume
232
232
  result["max_volume"] = instr.liquid_class.max_volume
233
- result["channels"] = instr.channels
233
+ result["channels"] = instr._max_channels.value
234
234
  result["has_tip"] = instr.has_tip
235
235
  result["tip_length"] = instr.current_tip_length
236
236
  result["aspirate_speed"] = self.plunger_speed(
@@ -260,6 +260,13 @@ class PipetteHandlerProvider(Generic[MountType]):
260
260
  "pipette_bounding_box_offsets"
261
261
  ] = instr.config.pipette_bounding_box_offsets
262
262
  result["lld_settings"] = instr.config.lld_settings
263
+ result["plunger_positions"] = {
264
+ "top": instr.plunger_positions.top,
265
+ "bottom": instr.plunger_positions.bottom,
266
+ "blow_out": instr.plunger_positions.blow_out,
267
+ "drop_tip": instr.plunger_positions.drop_tip,
268
+ }
269
+ result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
263
270
  return cast(PipetteDict, result)
264
271
 
265
272
  @property
@@ -318,8 +318,8 @@ def _reload_gripper(
318
318
  # Same config, good enough
319
319
  return attached_instr, True
320
320
  else:
321
- newdict = new_config.dict()
322
- olddict = attached_instr.config.dict()
321
+ newdict = new_config.model_dump()
322
+ olddict = attached_instr.config.model_dump()
323
323
  changed: Set[str] = set()
324
324
  for k in newdict.keys():
325
325
  if newdict[k] != olddict[k]:
@@ -27,7 +27,7 @@ from opentrons_shared_data.errors.exceptions import (
27
27
  InvalidInstrumentData,
28
28
  )
29
29
  from opentrons_shared_data.pipette.ul_per_mm import (
30
- piecewise_volume_conversion,
30
+ calculate_ul_per_mm,
31
31
  PIPETTING_FUNCTION_FALLBACK_VERSION,
32
32
  PIPETTING_FUNCTION_LATEST_VERSION,
33
33
  )
@@ -41,6 +41,8 @@ from opentrons_shared_data.pipette.types import (
41
41
  UlPerMmAction,
42
42
  PipetteName,
43
43
  PipetteModel,
44
+ Quirks,
45
+ PipetteOEMType,
44
46
  )
45
47
  from opentrons_shared_data.pipette import (
46
48
  load_data as load_pipette_data,
@@ -78,7 +80,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
78
80
  use_old_aspiration_functions: bool = False,
79
81
  ) -> None:
80
82
  self._config = config
81
- self._config_as_dict = config.dict()
83
+ self._config_as_dict = config.model_dump()
82
84
  self._plunger_motor_current = config.plunger_motor_configurations
83
85
  self._pick_up_configurations = config.pick_up_tip_configurations
84
86
  self._plunger_homing_configurations = config.plunger_homing_configurations
@@ -92,22 +94,26 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
92
94
  self._liquid_class_name = pip_types.LiquidClasses.default
93
95
  self._liquid_class = self._config.liquid_properties[self._liquid_class_name]
94
96
 
97
+ oem = PipetteOEMType.get_oem_from_quirks(config.quirks)
95
98
  # TODO (lc 12-05-2022) figure out how we can safely deprecate "name" and "model"
96
99
  self._pipette_name = PipetteNameType(
97
100
  pipette_type=config.pipette_type,
98
101
  pipette_channels=config.channels,
99
102
  pipette_generation=config.display_category,
103
+ oem_type=oem,
100
104
  )
101
105
  self._acting_as = self._pipette_name
102
106
  self._pipette_model = PipetteModelVersionType(
103
107
  pipette_type=config.pipette_type,
104
108
  pipette_channels=config.channels,
105
109
  pipette_version=config.version,
110
+ oem_type=oem,
106
111
  )
107
112
  self._valid_nozzle_maps = load_pipette_data.load_valid_nozzle_maps(
108
113
  self._pipette_model.pipette_type,
109
114
  self._pipette_model.pipette_channels,
110
115
  self._pipette_model.pipette_version,
116
+ self._pipette_model.oem_type,
111
117
  )
112
118
  self._nozzle_offset = self._config.nozzle_offset
113
119
  self._nozzle_manager = (
@@ -225,6 +231,9 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
225
231
  def push_out_volume(self) -> float:
226
232
  return self._active_tip_settings.default_push_out_volume
227
233
 
234
+ def is_high_speed_pipette(self) -> bool:
235
+ return Quirks.highSpeed in self._config.quirks
236
+
228
237
  def act_as(self, name: PipetteName) -> None:
229
238
  """Reconfigure to act as ``name``. ``name`` must be either the
230
239
  actual name of the pipette, or a name in its back-compatibility
@@ -246,8 +255,9 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
246
255
  self._pipette_model.pipette_type,
247
256
  self._pipette_model.pipette_channels,
248
257
  self._pipette_model.pipette_version,
258
+ self._pipette_model.oem_type,
249
259
  )
250
- self._config_as_dict = self._config.dict()
260
+ self._config_as_dict = self._config.model_dump()
251
261
 
252
262
  def reset_state(self) -> None:
253
263
  self._current_volume = 0.0
@@ -529,23 +539,13 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
529
539
  # want this to unbounded.
530
540
  @functools.lru_cache(maxsize=100)
531
541
  def ul_per_mm(self, ul: float, action: UlPerMmAction) -> float:
532
- if action == "aspirate":
533
- fallback = self._active_tip_settings.aspirate.default[
534
- PIPETTING_FUNCTION_FALLBACK_VERSION
535
- ]
536
- sequence = self._active_tip_settings.aspirate.default.get(
537
- self._pipetting_function_version, fallback
538
- )
539
- elif action == "blowout":
540
- return self._config.shaft_ul_per_mm
541
- else:
542
- fallback = self._active_tip_settings.dispense.default[
543
- PIPETTING_FUNCTION_FALLBACK_VERSION
544
- ]
545
- sequence = self._active_tip_settings.dispense.default.get(
546
- self._pipetting_function_version, fallback
547
- )
548
- return piecewise_volume_conversion(ul, sequence)
542
+ return calculate_ul_per_mm(
543
+ ul,
544
+ action,
545
+ self._active_tip_settings,
546
+ self._pipetting_function_version,
547
+ self._config.shaft_ul_per_mm,
548
+ )
549
549
 
550
550
  def __str__(self) -> str:
551
551
  return "{} current volume {}ul critical point: {} at {}".format(
@@ -585,6 +585,7 @@ class Pipette(AbstractInstrument[PipetteConfigurations]):
585
585
  "versioned_tip_overlap": self.tip_overlap,
586
586
  "back_compat_names": self._config.pipette_backcompat_names,
587
587
  "supported_tips": self.liquid_class.supported_tips,
588
+ "shaft_ul_per_mm": self._config.shaft_ul_per_mm,
588
589
  }
589
590
  )
590
591
  return self._config_as_dict
@@ -775,8 +776,8 @@ def _reload_and_check_skip(
775
776
  # Same config, good enough
776
777
  return attached_instr, True
777
778
  else:
778
- newdict = new_config.dict()
779
- olddict = attached_instr.config.dict()
779
+ newdict = new_config.model_dump()
780
+ olddict = attached_instr.config.model_dump()
780
781
  changed: Set[str] = set()
781
782
  for k in newdict.keys():
782
783
  if newdict[k] != olddict[k]:
@@ -237,6 +237,7 @@ class OT3PipetteHandler:
237
237
  "back_compat_names",
238
238
  "supported_tips",
239
239
  "lld_settings",
240
+ "available_sensors",
240
241
  ]
241
242
 
242
243
  instr_dict = instr.as_dict()
@@ -248,7 +249,7 @@ class OT3PipetteHandler:
248
249
  result["current_nozzle_map"] = instr.nozzle_manager.current_configuration
249
250
  result["min_volume"] = instr.liquid_class.min_volume
250
251
  result["max_volume"] = instr.liquid_class.max_volume
251
- result["channels"] = instr._max_channels
252
+ result["channels"] = instr._max_channels.value
252
253
  result["has_tip"] = instr.has_tip
253
254
  result["tip_length"] = instr.current_tip_length
254
255
  result["aspirate_speed"] = self.plunger_speed(
@@ -282,6 +283,14 @@ class OT3PipetteHandler:
282
283
  "pipette_bounding_box_offsets"
283
284
  ] = instr.config.pipette_bounding_box_offsets
284
285
  result["lld_settings"] = instr.config.lld_settings
286
+ result["plunger_positions"] = {
287
+ "top": instr.plunger_positions.top,
288
+ "bottom": instr.plunger_positions.bottom,
289
+ "blow_out": instr.plunger_positions.blow_out,
290
+ "drop_tip": instr.plunger_positions.drop_tip,
291
+ }
292
+ result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
293
+ result["available_sensors"] = instr.config.available_sensors
285
294
  return cast(PipetteDict, result)
286
295
 
287
296
  @property
@@ -2,7 +2,7 @@ import abc
2
2
  import asyncio
3
3
  import logging
4
4
  import re
5
- from typing import ClassVar, Mapping, Optional, TypeVar, cast
5
+ from typing import ClassVar, Mapping, Optional, TypeVar
6
6
  from packaging.version import InvalidVersion, parse, Version
7
7
  from opentrons.config import IS_ROBOT, ROBOT_FIRMWARE_DIR
8
8
  from opentrons.drivers.rpi_drivers.types import USBPort
@@ -31,7 +31,7 @@ def parse_fw_version(version: str) -> Version:
31
31
  raise InvalidVersion()
32
32
  except InvalidVersion:
33
33
  device_version = parse("v0.0.0")
34
- return cast(Version, device_version)
34
+ return device_version
35
35
 
36
36
 
37
37
  class AbstractModule(abc.ABC):