opentrons 8.3.2__py2.py3-none-any.whl → 8.4.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 (196) hide show
  1. opentrons/calibration_storage/ot2/mark_bad_calibration.py +2 -0
  2. opentrons/calibration_storage/ot2/tip_length.py +6 -6
  3. opentrons/config/advanced_settings.py +9 -11
  4. opentrons/config/feature_flags.py +0 -4
  5. opentrons/config/reset.py +7 -2
  6. opentrons/drivers/asyncio/communication/__init__.py +2 -0
  7. opentrons/drivers/asyncio/communication/async_serial.py +4 -0
  8. opentrons/drivers/asyncio/communication/errors.py +41 -8
  9. opentrons/drivers/asyncio/communication/serial_connection.py +36 -10
  10. opentrons/drivers/flex_stacker/__init__.py +9 -3
  11. opentrons/drivers/flex_stacker/abstract.py +140 -15
  12. opentrons/drivers/flex_stacker/driver.py +593 -47
  13. opentrons/drivers/flex_stacker/errors.py +64 -0
  14. opentrons/drivers/flex_stacker/simulator.py +222 -24
  15. opentrons/drivers/flex_stacker/types.py +211 -15
  16. opentrons/drivers/flex_stacker/utils.py +19 -0
  17. opentrons/execute.py +4 -2
  18. opentrons/hardware_control/api.py +5 -0
  19. opentrons/hardware_control/backends/flex_protocol.py +4 -0
  20. opentrons/hardware_control/backends/ot3controller.py +12 -1
  21. opentrons/hardware_control/backends/ot3simulator.py +3 -0
  22. opentrons/hardware_control/backends/subsystem_manager.py +8 -4
  23. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +10 -6
  24. opentrons/hardware_control/instruments/ot3/pipette_handler.py +59 -6
  25. opentrons/hardware_control/modules/__init__.py +12 -1
  26. opentrons/hardware_control/modules/absorbance_reader.py +11 -9
  27. opentrons/hardware_control/modules/flex_stacker.py +498 -0
  28. opentrons/hardware_control/modules/heater_shaker.py +12 -10
  29. opentrons/hardware_control/modules/magdeck.py +5 -1
  30. opentrons/hardware_control/modules/tempdeck.py +5 -1
  31. opentrons/hardware_control/modules/thermocycler.py +15 -14
  32. opentrons/hardware_control/modules/types.py +191 -1
  33. opentrons/hardware_control/modules/utils.py +3 -0
  34. opentrons/hardware_control/motion_utilities.py +20 -0
  35. opentrons/hardware_control/ot3api.py +145 -15
  36. opentrons/hardware_control/protocols/liquid_handler.py +47 -1
  37. opentrons/hardware_control/types.py +6 -0
  38. opentrons/legacy_commands/commands.py +102 -5
  39. opentrons/legacy_commands/helpers.py +74 -1
  40. opentrons/legacy_commands/types.py +33 -2
  41. opentrons/protocol_api/__init__.py +2 -0
  42. opentrons/protocol_api/_liquid.py +39 -8
  43. opentrons/protocol_api/_liquid_properties.py +20 -19
  44. opentrons/protocol_api/_transfer_liquid_validation.py +91 -0
  45. opentrons/protocol_api/core/common.py +3 -1
  46. opentrons/protocol_api/core/engine/deck_conflict.py +11 -1
  47. opentrons/protocol_api/core/engine/instrument.py +1356 -107
  48. opentrons/protocol_api/core/engine/labware.py +8 -4
  49. opentrons/protocol_api/core/engine/load_labware_params.py +68 -10
  50. opentrons/protocol_api/core/engine/module_core.py +118 -2
  51. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +6 -14
  52. opentrons/protocol_api/core/engine/protocol.py +253 -11
  53. opentrons/protocol_api/core/engine/stringify.py +19 -8
  54. opentrons/protocol_api/core/engine/transfer_components_executor.py +858 -0
  55. opentrons/protocol_api/core/engine/well.py +73 -5
  56. opentrons/protocol_api/core/instrument.py +71 -21
  57. opentrons/protocol_api/core/labware.py +6 -2
  58. opentrons/protocol_api/core/legacy/labware_offset_provider.py +7 -3
  59. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +76 -49
  60. opentrons/protocol_api/core/legacy/legacy_labware_core.py +8 -4
  61. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +36 -0
  62. opentrons/protocol_api/core/legacy/legacy_well_core.py +27 -2
  63. opentrons/protocol_api/core/legacy/load_info.py +4 -12
  64. opentrons/protocol_api/core/legacy/module_geometry.py +6 -1
  65. opentrons/protocol_api/core/legacy/well_geometry.py +3 -3
  66. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +73 -23
  67. opentrons/protocol_api/core/module.py +43 -0
  68. opentrons/protocol_api/core/protocol.py +33 -0
  69. opentrons/protocol_api/core/well.py +23 -2
  70. opentrons/protocol_api/instrument_context.py +454 -150
  71. opentrons/protocol_api/labware.py +98 -50
  72. opentrons/protocol_api/module_contexts.py +140 -0
  73. opentrons/protocol_api/protocol_context.py +163 -19
  74. opentrons/protocol_api/validation.py +51 -41
  75. opentrons/protocol_engine/__init__.py +21 -2
  76. opentrons/protocol_engine/actions/actions.py +5 -5
  77. opentrons/protocol_engine/clients/sync_client.py +6 -0
  78. opentrons/protocol_engine/commands/__init__.py +66 -36
  79. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +0 -1
  80. opentrons/protocol_engine/commands/air_gap_in_place.py +3 -2
  81. opentrons/protocol_engine/commands/aspirate.py +6 -2
  82. opentrons/protocol_engine/commands/aspirate_in_place.py +3 -1
  83. opentrons/protocol_engine/commands/aspirate_while_tracking.py +210 -0
  84. opentrons/protocol_engine/commands/blow_out.py +2 -0
  85. opentrons/protocol_engine/commands/blow_out_in_place.py +4 -1
  86. opentrons/protocol_engine/commands/command_unions.py +102 -33
  87. opentrons/protocol_engine/commands/configure_for_volume.py +3 -0
  88. opentrons/protocol_engine/commands/dispense.py +3 -1
  89. opentrons/protocol_engine/commands/dispense_in_place.py +3 -0
  90. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  91. opentrons/protocol_engine/commands/drop_tip.py +23 -1
  92. opentrons/protocol_engine/commands/flex_stacker/__init__.py +106 -0
  93. opentrons/protocol_engine/commands/flex_stacker/close_latch.py +72 -0
  94. opentrons/protocol_engine/commands/flex_stacker/common.py +15 -0
  95. opentrons/protocol_engine/commands/flex_stacker/empty.py +161 -0
  96. opentrons/protocol_engine/commands/flex_stacker/fill.py +164 -0
  97. opentrons/protocol_engine/commands/flex_stacker/open_latch.py +70 -0
  98. opentrons/protocol_engine/commands/flex_stacker/prepare_shuttle.py +112 -0
  99. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +394 -0
  100. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +190 -0
  101. opentrons/protocol_engine/commands/flex_stacker/store.py +291 -0
  102. opentrons/protocol_engine/commands/generate_command_schema.py +31 -2
  103. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  104. opentrons/protocol_engine/commands/liquid_probe.py +27 -13
  105. opentrons/protocol_engine/commands/load_labware.py +42 -39
  106. opentrons/protocol_engine/commands/load_lid.py +21 -13
  107. opentrons/protocol_engine/commands/load_lid_stack.py +130 -47
  108. opentrons/protocol_engine/commands/load_module.py +18 -17
  109. opentrons/protocol_engine/commands/load_pipette.py +3 -0
  110. opentrons/protocol_engine/commands/move_labware.py +139 -20
  111. opentrons/protocol_engine/commands/move_to_well.py +5 -11
  112. opentrons/protocol_engine/commands/pick_up_tip.py +5 -2
  113. opentrons/protocol_engine/commands/pipetting_common.py +159 -8
  114. opentrons/protocol_engine/commands/prepare_to_aspirate.py +15 -5
  115. opentrons/protocol_engine/commands/{evotip_dispense.py → pressure_dispense.py} +33 -34
  116. opentrons/protocol_engine/commands/reload_labware.py +6 -19
  117. opentrons/protocol_engine/commands/{evotip_seal_pipette.py → seal_pipette_to_tip.py} +97 -76
  118. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +3 -1
  119. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +6 -1
  120. opentrons/protocol_engine/commands/{evotip_unseal_pipette.py → unseal_pipette_from_tip.py} +31 -40
  121. opentrons/protocol_engine/errors/__init__.py +10 -0
  122. opentrons/protocol_engine/errors/exceptions.py +62 -0
  123. opentrons/protocol_engine/execution/equipment.py +123 -106
  124. opentrons/protocol_engine/execution/labware_movement.py +8 -6
  125. opentrons/protocol_engine/execution/pipetting.py +235 -25
  126. opentrons/protocol_engine/execution/tip_handler.py +82 -32
  127. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  128. opentrons/protocol_engine/protocol_engine.py +22 -13
  129. opentrons/protocol_engine/resources/deck_configuration_provider.py +98 -2
  130. opentrons/protocol_engine/resources/deck_data_provider.py +1 -1
  131. opentrons/protocol_engine/resources/labware_data_provider.py +32 -12
  132. opentrons/protocol_engine/resources/labware_validation.py +7 -5
  133. opentrons/protocol_engine/slot_standardization.py +11 -23
  134. opentrons/protocol_engine/state/addressable_areas.py +84 -46
  135. opentrons/protocol_engine/state/frustum_helpers.py +36 -14
  136. opentrons/protocol_engine/state/geometry.py +892 -227
  137. opentrons/protocol_engine/state/labware.py +252 -55
  138. opentrons/protocol_engine/state/module_substates/__init__.py +4 -0
  139. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +68 -0
  140. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +22 -0
  141. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +13 -0
  142. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +20 -0
  143. opentrons/protocol_engine/state/modules.py +210 -67
  144. opentrons/protocol_engine/state/pipettes.py +54 -0
  145. opentrons/protocol_engine/state/state.py +1 -1
  146. opentrons/protocol_engine/state/tips.py +14 -0
  147. opentrons/protocol_engine/state/update_types.py +180 -25
  148. opentrons/protocol_engine/state/wells.py +55 -9
  149. opentrons/protocol_engine/types/__init__.py +300 -0
  150. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  151. opentrons/protocol_engine/types/command_annotations.py +53 -0
  152. opentrons/protocol_engine/types/deck_configuration.py +72 -0
  153. opentrons/protocol_engine/types/execution.py +96 -0
  154. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  155. opentrons/protocol_engine/types/instrument.py +47 -0
  156. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  157. opentrons/protocol_engine/types/labware.py +111 -0
  158. opentrons/protocol_engine/types/labware_movement.py +22 -0
  159. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  160. opentrons/protocol_engine/types/labware_offset_vector.py +33 -0
  161. opentrons/protocol_engine/types/liquid.py +40 -0
  162. opentrons/protocol_engine/types/liquid_class.py +59 -0
  163. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  164. opentrons/protocol_engine/types/liquid_level_detection.py +131 -0
  165. opentrons/protocol_engine/types/location.py +194 -0
  166. opentrons/protocol_engine/types/module.py +301 -0
  167. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  168. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  169. opentrons/protocol_engine/types/tip.py +18 -0
  170. opentrons/protocol_engine/types/util.py +21 -0
  171. opentrons/protocol_engine/types/well_position.py +124 -0
  172. opentrons/protocol_reader/extract_labware_definitions.py +7 -3
  173. opentrons/protocol_reader/file_format_validator.py +5 -3
  174. opentrons/protocol_runner/json_translator.py +4 -2
  175. opentrons/protocol_runner/legacy_command_mapper.py +6 -2
  176. opentrons/protocol_runner/run_orchestrator.py +4 -1
  177. opentrons/protocols/advanced_control/transfers/common.py +48 -1
  178. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +204 -0
  179. opentrons/protocols/api_support/definitions.py +1 -1
  180. opentrons/protocols/api_support/instrument.py +16 -3
  181. opentrons/protocols/labware.py +27 -23
  182. opentrons/protocols/models/__init__.py +0 -21
  183. opentrons/simulate.py +4 -2
  184. opentrons/types.py +20 -7
  185. opentrons/util/logging_config.py +94 -25
  186. opentrons/util/logging_queue_handler.py +61 -0
  187. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/METADATA +4 -4
  188. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/RECORD +192 -151
  189. opentrons/calibration_storage/ot2/models/defaults.py +0 -0
  190. opentrons/calibration_storage/ot3/models/defaults.py +0 -0
  191. opentrons/protocol_api/core/legacy/legacy_robot_core.py +0 -0
  192. opentrons/protocol_engine/types.py +0 -1311
  193. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/LICENSE +0 -0
  194. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/WHEEL +0 -0
  195. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/entry_points.txt +0 -0
  196. {opentrons-8.3.2.dist-info → opentrons-8.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,19 @@
1
+ NUMBER_OF_ZONES = 10
2
+ NUMBER_OF_BINS = 128
3
+
4
+
5
+ def validate_histogram_frame(data: bytes, next_frame_id: int) -> bool:
6
+ """Validate Histogram frame, Raise error if invalid."""
7
+ start_delimn = data[0]
8
+ assert (
9
+ start_delimn == 0x81 # histogram start byte
10
+ ), f"Invalid delimn, {hex(start_delimn)} != 0x81."
11
+ frame_id = data[4]
12
+ assert (
13
+ next_frame_id == frame_id
14
+ ), f"Invalid frame id, expected {next_frame_id} got {frame_id}."
15
+ frame_len = data[5]
16
+ assert (
17
+ frame_len == 128 # len is always 128
18
+ ), f"Invalid frame length, expected 128 got {frame_len}."
19
+ return True
opentrons/execute.py CHANGED
@@ -23,7 +23,9 @@ from typing import (
23
23
  Union,
24
24
  )
25
25
 
26
- from opentrons_shared_data.labware.labware_definition import LabwareDefinition
26
+ from opentrons_shared_data.labware.labware_definition import (
27
+ labware_definition_type_adapter,
28
+ )
27
29
  from opentrons_shared_data.robot.types import RobotType
28
30
 
29
31
  from opentrons import protocol_api, __version__, should_use_ot3
@@ -560,7 +562,7 @@ def _create_live_context_pe(
560
562
  # Non-async would use call_soon_threadsafe(), which makes the waiting harder.
561
563
  async def add_all_extra_labware() -> None:
562
564
  for labware_definition_dict in extra_labware.values():
563
- labware_definition = LabwareDefinition.model_validate(
565
+ labware_definition = labware_definition_type_adapter.validate_python(
564
566
  labware_definition_dict
565
567
  )
566
568
  pe.add_labware_definition(labware_definition)
@@ -58,6 +58,7 @@ from .types import (
58
58
  SubSystem,
59
59
  SubSystemState,
60
60
  HardwareFeatureFlags,
61
+ TipScrapeType,
61
62
  )
62
63
  from . import modules
63
64
  from .robot_calibration import (
@@ -1040,6 +1041,7 @@ class API(
1040
1041
  mount: top_types.Mount,
1041
1042
  volume: Optional[float] = None,
1042
1043
  rate: float = 1.0,
1044
+ correction_volume: float = 0.0,
1043
1045
  ) -> None:
1044
1046
  """
1045
1047
  Aspirate a volume of liquid (in microliters/uL) using this pipette.
@@ -1074,6 +1076,8 @@ class API(
1074
1076
  volume: Optional[float] = None,
1075
1077
  rate: float = 1.0,
1076
1078
  push_out: Optional[float] = None,
1079
+ correction_volume: float = 0.0,
1080
+ is_full_dispense: bool = False,
1077
1081
  ) -> None:
1078
1082
  """
1079
1083
  Dispense a volume of liquid in microliters(uL) using this pipette.
@@ -1253,6 +1257,7 @@ class API(
1253
1257
  mount: top_types.Mount,
1254
1258
  home_after: bool = True,
1255
1259
  ignore_plunger: bool = False,
1260
+ scrape_type: TipScrapeType = TipScrapeType.NONE,
1256
1261
  ) -> None:
1257
1262
  spec, _ = self.plan_check_drop_tip(mount, home_after)
1258
1263
 
@@ -462,3 +462,7 @@ class FlexBackend(Protocol):
462
462
 
463
463
  async def get_hepa_uv_state(self) -> Optional[HepaUVState]:
464
464
  ...
465
+
466
+ async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
467
+ """Tell a pipette to increase it's evo-tip-dispense-count in eeprom."""
468
+ ...
@@ -216,6 +216,7 @@ from .types import HWStopCondition
216
216
  from .flex_protocol import FlexBackend
217
217
  from .status_bar_state import StatusBarStateController
218
218
  from opentrons_hardware.sensors.types import SensorDataType
219
+ from opentrons_hardware.sensors.utils import send_evo_dispense_count_increase
219
220
 
220
221
  log = logging.getLogger(__name__)
221
222
 
@@ -1397,7 +1398,11 @@ class OT3Controller(FlexBackend):
1397
1398
  return
1398
1399
 
1399
1400
  if hasattr(self, "_event_watcher"):
1400
- if loop.is_running() and self._event_watcher:
1401
+ if (
1402
+ loop.is_running()
1403
+ and self._event_watcher
1404
+ and not self._event_watcher.closed
1405
+ ):
1401
1406
  self._event_watcher.close()
1402
1407
 
1403
1408
  messenger = getattr(self, "_messenger", None)
@@ -1824,3 +1829,9 @@ class OT3Controller(FlexBackend):
1824
1829
  """This is something we only use in the simulator.
1825
1830
  It is required so that PE simulations using ot3api don't break."""
1826
1831
  pass
1832
+
1833
+ async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
1834
+ """Tell a pipette to increase it's evo-tip-dispense-count in eeprom."""
1835
+ await send_evo_dispense_count_increase(
1836
+ self._messenger, sensor_node_for_pipette(OT3Mount(mount.value))
1837
+ )
@@ -871,3 +871,6 @@ class OT3Simulator(FlexBackend):
871
871
  """This is something we only use in the simulator.
872
872
  It is required so that PE simulations using ot3api don't break."""
873
873
  self._sim_tip_state[mount] = status
874
+
875
+ async def increase_evo_disp_count(self, mount: OT3Mount) -> None:
876
+ pass
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Coordinate subsystem detection and updates.
3
3
  """
4
+
4
5
  import asyncio
5
6
  from contextlib import contextmanager, ExitStack
6
7
  from dataclasses import dataclass
@@ -57,7 +58,7 @@ class SubsystemManager:
57
58
  _expected_core_targets: Set[FirmwareTarget]
58
59
  _present_tools: tools.types.ToolSummary
59
60
  _tool_task_condition: asyncio.Condition
60
- _tool_task_state: Union[bool, Exception]
61
+ _tool_task_state: Union[bool, BaseException]
61
62
  _updates_required: Dict[FirmwareTarget, FirmwareUpdateRequirements]
62
63
  _updates_ongoing: Dict[SubSystem, UpdateStatus]
63
64
  _update_bag: FirmwareUpdate
@@ -369,7 +370,8 @@ class SubsystemManager:
369
370
  async def _tool_detection_task_main(self) -> None:
370
371
  try:
371
372
  await self._tool_detection_task_protected()
372
- except Exception as e:
373
+ except BaseException as e:
374
+ log.exception("Tool reader task failed")
373
375
  async with self._tool_task_condition:
374
376
  self._tool_task_state = e
375
377
  self._tool_task_condition.notify_all()
@@ -390,11 +392,12 @@ class SubsystemManager:
390
392
  tool_nodes.add(NodeId.pipette_right)
391
393
  if update.gripper != ToolType.nothing_attached:
392
394
  tool_nodes.add(NodeId.gripper)
393
- base_nodes: Set[FirmwareTarget] = {
395
+ base_can_nodes: List[NodeId] = [
394
396
  NodeId.pipette_left,
395
397
  NodeId.pipette_right,
396
398
  NodeId.gripper,
397
- }
399
+ ]
400
+ base_nodes: Set[FirmwareTarget] = {n for n in base_can_nodes}
398
401
  try:
399
402
  self._network_info.mark_absent(base_nodes - tool_nodes)
400
403
  await self._probe_network_and_cache_fw_updates(
@@ -412,6 +415,7 @@ class SubsystemManager:
412
415
  )
413
416
  self._present_tools = await self._tool_detector.resolve(to_resolve, 10.0)
414
417
  log.info(f"Present tools are now {self._present_tools}")
418
+ await network.log_motor_usage_data(self._can_messenger)
415
419
  async with self._tool_task_condition:
416
420
  self._tool_task_state = True
417
421
  self._tool_task_condition.notify_all()
@@ -9,12 +9,12 @@ from opentrons.calibration_storage import types, helpers
9
9
  from opentrons.types import Mount, Point
10
10
  from opentrons.hardware_control.types import OT3Mount
11
11
 
12
- from opentrons_shared_data.labware.labware_definition import LabwareDefinition
12
+ from opentrons_shared_data.labware.labware_definition import LabwareDefinition2
13
13
 
14
14
  if typing.TYPE_CHECKING:
15
15
  from opentrons_shared_data.pipette.types import LabwareUri
16
16
  from opentrons_shared_data.labware.types import (
17
- LabwareDefinition as TypeDictLabwareDef,
17
+ LabwareDefinition2 as TypeDictLabwareDef2,
18
18
  )
19
19
 
20
20
  # These type aliases aid typechecking in tests that work the same on this and
@@ -119,12 +119,16 @@ def save_pipette_offset_calibration(
119
119
  # TODO (lc 09-26-2022) We should ensure that only LabwareDefinition models are passed
120
120
  # into this function instead of a mixture of TypeDicts and BaseModels
121
121
  def load_tip_length_for_pipette(
122
- pipette_id: str, tiprack: typing.Union["TypeDictLabwareDef", LabwareDefinition]
122
+ pipette_id: str, tiprack: typing.Union["TypeDictLabwareDef2", LabwareDefinition2]
123
123
  ) -> TipLengthCalibration:
124
- if isinstance(tiprack, LabwareDefinition):
124
+ if isinstance(tiprack, LabwareDefinition2):
125
125
  tiprack = typing.cast(
126
- "TypeDictLabwareDef",
127
- tiprack.model_dump(exclude_none=True, exclude_unset=True),
126
+ "TypeDictLabwareDef2",
127
+ tiprack.model_dump(
128
+ exclude_none=True,
129
+ exclude_unset=True
130
+ # todo(mm, 2025-02-13): Do we need by_alias=True here?
131
+ ),
128
132
  )
129
133
 
130
134
  tip_length_data = calibration_storage.load_tip_length_calibration(
@@ -32,6 +32,7 @@ from opentrons.hardware_control.types import (
32
32
  HardwareAction,
33
33
  Axis,
34
34
  OT3Mount,
35
+ TipScrapeType,
35
36
  )
36
37
  from opentrons.hardware_control.constants import (
37
38
  SHAKE_OFF_TIPS_SPEED,
@@ -78,6 +79,8 @@ class TipActionMoveSpec:
78
79
  speed: Optional[
79
80
  float
80
81
  ] # allow speed for a movement to default to its axes' speed settings
82
+ scrape_axis: Optional[Axis] = None
83
+ # add a scrape motion in the middle of a tip drop
81
84
 
82
85
 
83
86
  @dataclass(frozen=True)
@@ -502,10 +505,19 @@ class OT3PipetteHandler:
502
505
  self._ihp_log.debug(f"{action} on {target.name}")
503
506
 
504
507
  def plunger_position(
505
- self, instr: Pipette, ul: float, action: "UlPerMmAction"
508
+ self,
509
+ instr: Pipette,
510
+ ul: float,
511
+ action: "UlPerMmAction",
512
+ correction_volume: float = 0.0,
506
513
  ) -> float:
507
- mm = ul / instr.ul_per_mm(ul, action)
508
- position = instr.plunger_positions.bottom - mm
514
+ if ul == 0:
515
+ position = instr.plunger_positions.bottom
516
+ else:
517
+ multiplier = 1.0 + (correction_volume / ul)
518
+ mm_dist_from_bottom = ul / instr.ul_per_mm(ul, action)
519
+ mm_dist_from_bottom_corrected = mm_dist_from_bottom * multiplier
520
+ position = instr.plunger_positions.bottom - mm_dist_from_bottom_corrected
509
521
  return round(position, 6)
510
522
 
511
523
  def plunger_speed(
@@ -531,6 +543,7 @@ class OT3PipetteHandler:
531
543
  mount: OT3Mount,
532
544
  volume: Optional[float],
533
545
  rate: float,
546
+ correction_volume: float = 0.0,
534
547
  ) -> Optional[LiquidActionSpec]:
535
548
  """Check preconditions for aspirate, parse args, and calculate positions.
536
549
 
@@ -566,7 +579,10 @@ class OT3PipetteHandler:
566
579
  ), "Cannot aspirate more than pipette max volume"
567
580
 
568
581
  dist = self.plunger_position(
569
- instrument, instrument.current_volume + asp_vol, "aspirate"
582
+ instr=instrument,
583
+ ul=instrument.current_volume + asp_vol,
584
+ action="aspirate",
585
+ correction_volume=correction_volume,
570
586
  )
571
587
  speed = self.plunger_speed(
572
588
  instrument, instrument.aspirate_flow_rate * rate, "aspirate"
@@ -591,6 +607,8 @@ class OT3PipetteHandler:
591
607
  volume: Optional[float],
592
608
  rate: float,
593
609
  push_out: Optional[float],
610
+ is_full_dispense: bool,
611
+ correction_volume: float = 0.0,
594
612
  ) -> Optional[LiquidActionSpec]:
595
613
  """Check preconditions for dispense, parse args, and calculate positions.
596
614
 
@@ -627,7 +645,21 @@ class OT3PipetteHandler:
627
645
  # of the OT-2 version of this class. Protocol Engine does its own clamping,
628
646
  # so we don't expect this to trigger in practice.
629
647
  disp_vol = min(instrument.current_volume, disp_vol)
630
- is_full_dispense = numpy.isclose(instrument.current_volume - disp_vol, 0)
648
+
649
+ # TODO (Ryan): Remove this check in the future.
650
+ # we moved this logic up to protocol_engine but replacing with this check to make sure
651
+ # we don't accidentally call this incorrectly from somewhere else.
652
+ if not is_full_dispense and numpy.isclose(
653
+ instrument.current_volume - disp_vol, 0
654
+ ):
655
+ raise CommandPreconditionViolated(
656
+ message="Command created a full-dispense without the full dispense argument",
657
+ detail={
658
+ "command": "dispense",
659
+ "current-volume": str(instrument.current_volume),
660
+ "dispense-volume": str(disp_vol),
661
+ },
662
+ )
631
663
 
632
664
  if disp_vol == 0:
633
665
  return None
@@ -659,7 +691,10 @@ class OT3PipetteHandler:
659
691
  )
660
692
 
661
693
  dist = self.plunger_position(
662
- instrument, instrument.current_volume - disp_vol, "dispense"
694
+ instr=instrument,
695
+ ul=instrument.current_volume - disp_vol,
696
+ action="dispense",
697
+ correction_volume=correction_volume,
663
698
  )
664
699
  speed = self.plunger_speed(
665
700
  instrument, instrument.dispense_flow_rate * rate, "dispense"
@@ -889,6 +924,7 @@ class OT3PipetteHandler:
889
924
  def plan_lt_drop_tip(
890
925
  self,
891
926
  mount: OT3Mount,
927
+ scrape_tips: TipScrapeType = TipScrapeType.NONE,
892
928
  ) -> TipActionSpec:
893
929
  instrument = self.get_pipette(mount)
894
930
  config = instrument.drop_configurations.plunger_eject
@@ -896,6 +932,20 @@ class OT3PipetteHandler:
896
932
  raise CommandPreconditionViolated(
897
933
  f"No plunger-eject drop tip configurations for {instrument.name} on {mount.name}"
898
934
  )
935
+ scrape_move: Optional[TipActionMoveSpec] = None
936
+ match scrape_tips:
937
+ case TipScrapeType.LEFT_ONE_COL:
938
+ scrape_move = TipActionMoveSpec(
939
+ distance=-11, currents=None, speed=None, scrape_axis=Axis.X
940
+ )
941
+ case TipScrapeType.RIGHT_ONE_COL:
942
+ scrape_move = TipActionMoveSpec(
943
+ distance=11, currents=None, speed=None, scrape_axis=Axis.X
944
+ )
945
+ case TipScrapeType.NONE:
946
+ scrape_move = None
947
+ case _:
948
+ scrape_move = None
899
949
  drop_seq = [
900
950
  TipActionMoveSpec(
901
951
  distance=instrument.plunger_positions.drop_tip,
@@ -914,6 +964,9 @@ class OT3PipetteHandler:
914
964
  },
915
965
  ),
916
966
  ]
967
+ if scrape_move:
968
+ # Add the scrape move before the plunger moves back up
969
+ drop_seq.insert(1, scrape_move)
917
970
 
918
971
  return TipActionSpec(
919
972
  tip_action_moves=drop_seq,
@@ -4,6 +4,7 @@ from .magdeck import MagDeck
4
4
  from .thermocycler import Thermocycler
5
5
  from .heater_shaker import HeaterShaker
6
6
  from .absorbance_reader import AbsorbanceReader
7
+ from .flex_stacker import FlexStacker
7
8
  from .update import update_firmware
8
9
  from .utils import MODULE_TYPE_BY_NAME, build
9
10
  from .types import (
@@ -19,8 +20,13 @@ from .types import (
19
20
  MagneticStatus,
20
21
  HeaterShakerStatus,
21
22
  AbsorbanceReaderStatus,
23
+ PlatformState,
24
+ StackerAxisState,
25
+ FlexStackerStatus,
22
26
  SpeedStatus,
23
27
  LiveData,
28
+ ModuleData,
29
+ ModuleDataValidator,
24
30
  )
25
31
  from .errors import (
26
32
  UpdateError,
@@ -51,8 +57,13 @@ __all__ = [
51
57
  "HeaterShakerStatus",
52
58
  "SpeedStatus",
53
59
  "LiveData",
60
+ "ModuleData",
61
+ "ModuleDataValidator",
54
62
  "AbsorbanceReader",
55
63
  "AbsorbanceReaderStatus",
56
64
  "AbsorbanceReaderDisconnectedError",
57
- "ModuleDisconnectedCallback",
65
+ "FlexStacker",
66
+ "FlexStackerStatus",
67
+ "PlatformState",
68
+ "StackerAxisState",
58
69
  ]
@@ -24,6 +24,7 @@ from opentrons.hardware_control.modules.types import (
24
24
  ModuleType,
25
25
  AbsorbanceReaderStatus,
26
26
  LiveData,
27
+ AbsorbanceReaderData,
27
28
  UploadFunction,
28
29
  )
29
30
  from opentrons.hardware_control.modules.errors import AbsorbanceReaderDisconnectedError
@@ -243,17 +244,18 @@ class AbsorbanceReader(mod_abc.AbstractModule):
243
244
  def live_data(self) -> LiveData:
244
245
  """Return a dict of the module's dynamic information"""
245
246
  conf = self._measurement_config.data if self._measurement_config else dict()
247
+ data: AbsorbanceReaderData = {
248
+ "uptime": self.uptime,
249
+ "deviceStatus": self.status.value,
250
+ "lidStatus": self.lid_status.value,
251
+ "platePresence": self.plate_presence.value,
252
+ "measureMode": conf.get("measureMode", ""),
253
+ "sampleWavelengths": conf.get("sampleWavelengths", []),
254
+ "referenceWavelength": conf.get("referenceWavelength", 0),
255
+ }
246
256
  return {
247
257
  "status": self.status.value,
248
- "data": {
249
- "uptime": self.uptime,
250
- "deviceStatus": self.status.value,
251
- "lidStatus": self.lid_status.value,
252
- "platePresence": self.plate_presence.value,
253
- "measureMode": conf.get("measureMode", ""),
254
- "sampleWavelengths": conf.get("sampleWavelengths", []),
255
- "referenceWavelength": conf.get("referenceWavelength", 0),
256
- },
258
+ "data": data,
257
259
  }
258
260
 
259
261
  @property