opentrons 8.7.0a9__py3-none-any.whl → 8.8.0a8__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 (190) hide show
  1. opentrons/_version.py +2 -2
  2. opentrons/cli/analyze.py +4 -1
  3. opentrons/config/__init__.py +7 -0
  4. opentrons/drivers/asyncio/communication/serial_connection.py +126 -49
  5. opentrons/drivers/heater_shaker/abstract.py +5 -0
  6. opentrons/drivers/heater_shaker/driver.py +10 -0
  7. opentrons/drivers/heater_shaker/simulator.py +4 -0
  8. opentrons/drivers/thermocycler/abstract.py +6 -0
  9. opentrons/drivers/thermocycler/driver.py +61 -10
  10. opentrons/drivers/thermocycler/simulator.py +6 -0
  11. opentrons/drivers/vacuum_module/__init__.py +5 -0
  12. opentrons/drivers/vacuum_module/abstract.py +93 -0
  13. opentrons/drivers/vacuum_module/driver.py +208 -0
  14. opentrons/drivers/vacuum_module/errors.py +39 -0
  15. opentrons/drivers/vacuum_module/simulator.py +85 -0
  16. opentrons/drivers/vacuum_module/types.py +79 -0
  17. opentrons/execute.py +3 -0
  18. opentrons/hardware_control/api.py +24 -5
  19. opentrons/hardware_control/backends/controller.py +8 -2
  20. opentrons/hardware_control/backends/flex_protocol.py +1 -0
  21. opentrons/hardware_control/backends/ot3controller.py +35 -2
  22. opentrons/hardware_control/backends/ot3simulator.py +3 -1
  23. opentrons/hardware_control/backends/ot3utils.py +37 -0
  24. opentrons/hardware_control/backends/simulator.py +2 -1
  25. opentrons/hardware_control/backends/subsystem_manager.py +5 -2
  26. opentrons/hardware_control/emulation/abstract_emulator.py +6 -4
  27. opentrons/hardware_control/emulation/connection_handler.py +8 -5
  28. opentrons/hardware_control/emulation/heater_shaker.py +12 -3
  29. opentrons/hardware_control/emulation/settings.py +1 -1
  30. opentrons/hardware_control/emulation/thermocycler.py +67 -15
  31. opentrons/hardware_control/module_control.py +105 -10
  32. opentrons/hardware_control/modules/__init__.py +3 -0
  33. opentrons/hardware_control/modules/absorbance_reader.py +11 -4
  34. opentrons/hardware_control/modules/flex_stacker.py +38 -9
  35. opentrons/hardware_control/modules/heater_shaker.py +42 -5
  36. opentrons/hardware_control/modules/magdeck.py +8 -4
  37. opentrons/hardware_control/modules/mod_abc.py +14 -6
  38. opentrons/hardware_control/modules/tempdeck.py +25 -5
  39. opentrons/hardware_control/modules/thermocycler.py +68 -11
  40. opentrons/hardware_control/modules/types.py +20 -1
  41. opentrons/hardware_control/modules/utils.py +11 -4
  42. opentrons/hardware_control/motion_utilities.py +6 -6
  43. opentrons/hardware_control/nozzle_manager.py +3 -0
  44. opentrons/hardware_control/ot3api.py +92 -17
  45. opentrons/hardware_control/poller.py +22 -8
  46. opentrons/hardware_control/protocols/liquid_handler.py +12 -4
  47. opentrons/hardware_control/scripts/update_module_fw.py +5 -0
  48. opentrons/hardware_control/types.py +43 -2
  49. opentrons/legacy_commands/commands.py +58 -5
  50. opentrons/legacy_commands/module_commands.py +52 -0
  51. opentrons/legacy_commands/protocol_commands.py +53 -1
  52. opentrons/legacy_commands/types.py +155 -1
  53. opentrons/motion_planning/deck_conflict.py +17 -12
  54. opentrons/motion_planning/waypoints.py +15 -29
  55. opentrons/protocol_api/__init__.py +5 -1
  56. opentrons/protocol_api/_transfer_liquid_validation.py +17 -2
  57. opentrons/protocol_api/_types.py +8 -1
  58. opentrons/protocol_api/core/common.py +3 -1
  59. opentrons/protocol_api/core/engine/_default_labware_versions.py +33 -11
  60. opentrons/protocol_api/core/engine/deck_conflict.py +3 -1
  61. opentrons/protocol_api/core/engine/instrument.py +109 -26
  62. opentrons/protocol_api/core/engine/labware.py +8 -1
  63. opentrons/protocol_api/core/engine/module_core.py +95 -4
  64. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +4 -18
  65. opentrons/protocol_api/core/engine/protocol.py +51 -2
  66. opentrons/protocol_api/core/engine/stringify.py +2 -0
  67. opentrons/protocol_api/core/engine/tasks.py +48 -0
  68. opentrons/protocol_api/core/engine/well.py +8 -0
  69. opentrons/protocol_api/core/instrument.py +19 -2
  70. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +19 -2
  71. opentrons/protocol_api/core/legacy/legacy_module_core.py +33 -2
  72. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +23 -1
  73. opentrons/protocol_api/core/legacy/legacy_well_core.py +4 -0
  74. opentrons/protocol_api/core/legacy/tasks.py +19 -0
  75. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +19 -2
  76. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +14 -2
  77. opentrons/protocol_api/core/legacy_simulator/tasks.py +19 -0
  78. opentrons/protocol_api/core/module.py +58 -2
  79. opentrons/protocol_api/core/protocol.py +23 -2
  80. opentrons/protocol_api/core/tasks.py +31 -0
  81. opentrons/protocol_api/core/well.py +4 -0
  82. opentrons/protocol_api/instrument_context.py +388 -2
  83. opentrons/protocol_api/labware.py +10 -2
  84. opentrons/protocol_api/module_contexts.py +170 -6
  85. opentrons/protocol_api/protocol_context.py +87 -21
  86. opentrons/protocol_api/robot_context.py +41 -25
  87. opentrons/protocol_api/tasks.py +48 -0
  88. opentrons/protocol_api/validation.py +49 -3
  89. opentrons/protocol_engine/__init__.py +4 -0
  90. opentrons/protocol_engine/actions/__init__.py +6 -2
  91. opentrons/protocol_engine/actions/actions.py +31 -9
  92. opentrons/protocol_engine/clients/sync_client.py +42 -7
  93. opentrons/protocol_engine/commands/__init__.py +56 -0
  94. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +2 -15
  95. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +2 -15
  96. opentrons/protocol_engine/commands/absorbance_reader/read.py +22 -23
  97. opentrons/protocol_engine/commands/aspirate.py +1 -0
  98. opentrons/protocol_engine/commands/aspirate_while_tracking.py +52 -19
  99. opentrons/protocol_engine/commands/capture_image.py +302 -0
  100. opentrons/protocol_engine/commands/command.py +2 -0
  101. opentrons/protocol_engine/commands/command_unions.py +62 -0
  102. opentrons/protocol_engine/commands/create_timer.py +83 -0
  103. opentrons/protocol_engine/commands/dispense.py +1 -0
  104. opentrons/protocol_engine/commands/dispense_while_tracking.py +56 -19
  105. opentrons/protocol_engine/commands/drop_tip.py +32 -8
  106. opentrons/protocol_engine/commands/flex_stacker/common.py +35 -0
  107. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +7 -0
  108. opentrons/protocol_engine/commands/heater_shaker/__init__.py +14 -0
  109. opentrons/protocol_engine/commands/heater_shaker/common.py +20 -0
  110. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +5 -4
  111. opentrons/protocol_engine/commands/heater_shaker/set_shake_speed.py +136 -0
  112. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +31 -5
  113. opentrons/protocol_engine/commands/move_labware.py +3 -4
  114. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +1 -1
  115. opentrons/protocol_engine/commands/movement_common.py +31 -2
  116. opentrons/protocol_engine/commands/pick_up_tip.py +21 -11
  117. opentrons/protocol_engine/commands/pipetting_common.py +48 -3
  118. opentrons/protocol_engine/commands/set_tip_state.py +97 -0
  119. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +38 -7
  120. opentrons/protocol_engine/commands/thermocycler/__init__.py +16 -0
  121. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +6 -0
  122. opentrons/protocol_engine/commands/thermocycler/run_profile.py +8 -0
  123. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +44 -7
  124. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +43 -14
  125. opentrons/protocol_engine/commands/thermocycler/start_run_extended_profile.py +191 -0
  126. opentrons/protocol_engine/commands/touch_tip.py +1 -1
  127. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +6 -22
  128. opentrons/protocol_engine/commands/wait_for_tasks.py +98 -0
  129. opentrons/protocol_engine/create_protocol_engine.py +12 -0
  130. opentrons/protocol_engine/engine_support.py +3 -0
  131. opentrons/protocol_engine/errors/__init__.py +12 -0
  132. opentrons/protocol_engine/errors/exceptions.py +119 -0
  133. opentrons/protocol_engine/execution/__init__.py +4 -0
  134. opentrons/protocol_engine/execution/command_executor.py +62 -1
  135. opentrons/protocol_engine/execution/create_queue_worker.py +9 -2
  136. opentrons/protocol_engine/execution/labware_movement.py +13 -15
  137. opentrons/protocol_engine/execution/movement.py +2 -0
  138. opentrons/protocol_engine/execution/pipetting.py +26 -25
  139. opentrons/protocol_engine/execution/queue_worker.py +4 -0
  140. opentrons/protocol_engine/execution/run_control.py +8 -0
  141. opentrons/protocol_engine/execution/task_handler.py +157 -0
  142. opentrons/protocol_engine/protocol_engine.py +137 -36
  143. opentrons/protocol_engine/resources/__init__.py +4 -0
  144. opentrons/protocol_engine/resources/camera_provider.py +110 -0
  145. opentrons/protocol_engine/resources/concurrency_provider.py +27 -0
  146. opentrons/protocol_engine/resources/deck_configuration_provider.py +7 -0
  147. opentrons/protocol_engine/resources/file_provider.py +133 -58
  148. opentrons/protocol_engine/resources/labware_validation.py +10 -6
  149. opentrons/protocol_engine/slot_standardization.py +2 -0
  150. opentrons/protocol_engine/state/_well_math.py +60 -18
  151. opentrons/protocol_engine/state/addressable_areas.py +2 -0
  152. opentrons/protocol_engine/state/camera.py +54 -0
  153. opentrons/protocol_engine/state/commands.py +37 -14
  154. opentrons/protocol_engine/state/geometry.py +276 -379
  155. opentrons/protocol_engine/state/labware.py +62 -108
  156. opentrons/protocol_engine/state/labware_origin_math/errors.py +94 -0
  157. opentrons/protocol_engine/state/labware_origin_math/stackup_origin_to_labware_origin.py +1336 -0
  158. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +37 -0
  159. opentrons/protocol_engine/state/modules.py +30 -8
  160. opentrons/protocol_engine/state/motion.py +60 -18
  161. opentrons/protocol_engine/state/preconditions.py +59 -0
  162. opentrons/protocol_engine/state/state.py +44 -0
  163. opentrons/protocol_engine/state/state_summary.py +4 -0
  164. opentrons/protocol_engine/state/tasks.py +139 -0
  165. opentrons/protocol_engine/state/tips.py +177 -258
  166. opentrons/protocol_engine/state/update_types.py +26 -9
  167. opentrons/protocol_engine/types/__init__.py +23 -4
  168. opentrons/protocol_engine/types/command_preconditions.py +18 -0
  169. opentrons/protocol_engine/types/deck_configuration.py +5 -1
  170. opentrons/protocol_engine/types/instrument.py +8 -1
  171. opentrons/protocol_engine/types/labware.py +1 -13
  172. opentrons/protocol_engine/types/location.py +26 -2
  173. opentrons/protocol_engine/types/module.py +11 -1
  174. opentrons/protocol_engine/types/tasks.py +38 -0
  175. opentrons/protocol_engine/types/tip.py +9 -0
  176. opentrons/protocol_runner/create_simulating_orchestrator.py +29 -2
  177. opentrons/protocol_runner/protocol_runner.py +14 -1
  178. opentrons/protocol_runner/run_orchestrator.py +49 -2
  179. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +2 -2
  180. opentrons/protocols/api_support/definitions.py +1 -1
  181. opentrons/protocols/api_support/types.py +2 -1
  182. opentrons/simulate.py +51 -15
  183. opentrons/system/camera.py +334 -4
  184. opentrons/system/ffmpeg.py +110 -0
  185. {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/METADATA +4 -4
  186. {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/RECORD +189 -161
  187. opentrons/protocol_engine/state/_labware_origin_math.py +0 -636
  188. {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/WHEEL +0 -0
  189. {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/entry_points.txt +0 -0
  190. {opentrons-8.7.0a9.dist-info → opentrons-8.8.0a8.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  """ProtocolEngine-based Protocol API core implementation."""
2
2
 
3
3
  from __future__ import annotations
4
- from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING
4
+ from typing import Dict, Optional, Type, Union, List, Tuple, TYPE_CHECKING, Sequence
5
5
 
6
6
  from opentrons_shared_data.liquid_classes import LiquidClassDefinitionDoesNotExist
7
7
  from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
@@ -49,6 +49,7 @@ from opentrons.protocol_engine.types import (
49
49
  OFF_DECK_LOCATION,
50
50
  SYSTEM_LOCATION,
51
51
  LoadableLabwareLocation,
52
+ WASTE_CHUTE_LOCATION,
52
53
  NonStackedLocation,
53
54
  )
54
55
  from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
@@ -64,6 +65,7 @@ from ...disposal_locations import TrashBin, WasteChute
64
65
  from ..protocol import AbstractProtocol
65
66
  from ..labware import LabwareLoadParams
66
67
  from .labware import LabwareCore
68
+ from .tasks import EngineTaskCore
67
69
  from .instrument import InstrumentCore
68
70
  from .robot import RobotCore
69
71
  from .module_core import (
@@ -95,6 +97,7 @@ class ProtocolCore(
95
97
  InstrumentCore,
96
98
  LabwareCore,
97
99
  Union[ModuleCore, NonConnectedModuleCore],
100
+ EngineTaskCore,
98
101
  ]
99
102
  ):
100
103
  """Protocol API core using a ProtocolEngine.
@@ -912,6 +915,21 @@ class ProtocolCore(
912
915
  cmd.WaitForDurationParams(seconds=seconds, message=msg)
913
916
  )
914
917
 
918
+ def wait_for_tasks(self, task_cores: Sequence[EngineTaskCore]) -> None:
919
+ """Wait for specified tasks to complete."""
920
+ task_ids = task_ids = [task._id for task in task_cores if task._id is not None]
921
+ self._engine_client.execute_command(cmd.WaitForTasksParams(task_ids=task_ids))
922
+
923
+ def create_timer(self, seconds: float) -> EngineTaskCore:
924
+ """Create a timer task that runs in the background."""
925
+ result = self._engine_client.execute_command_without_recovery(
926
+ cmd.CreateTimerParams(time=seconds)
927
+ )
928
+ timer_task = EngineTaskCore(
929
+ engine_client=self._engine_client, task_id=result.task_id
930
+ )
931
+ return timer_task
932
+
915
933
  def home(self) -> None:
916
934
  """Move all axes to their home positions."""
917
935
  self._engine_client.execute_command(cmd.HomeParams(axes=None))
@@ -1151,9 +1169,38 @@ class ProtocolCore(
1151
1169
  return self._module_cores_by_id[labware_location.moduleId]
1152
1170
  elif isinstance(labware_location, OnLabwareLocation):
1153
1171
  return self._labware_cores_by_id[labware_location.labwareId]
1154
-
1172
+ elif labware_location == WASTE_CHUTE_LOCATION:
1173
+ return OffDeckType.WASTE_CHUTE
1155
1174
  return OffDeckType.OFF_DECK
1156
1175
 
1176
+ def capture_image(
1177
+ self,
1178
+ filename: Optional[str] = None,
1179
+ resolution: Optional[Tuple[int, int]] = None,
1180
+ zoom: Optional[float] = None,
1181
+ contrast: Optional[float] = None,
1182
+ brightness: Optional[float] = None,
1183
+ saturation: Optional[float] = None,
1184
+ ) -> None:
1185
+ """Capture an image using a camera.
1186
+ Args:
1187
+ resolution: Width by height resolution in pixels for the image to be captured with.
1188
+ zoom: Multiplier to use when cropping and scaling a captured image. Scale is 1.0 to 2.0.
1189
+ contrast: The contrast to use when processing an image. Scale is 0% to 100%
1190
+ brightness: The brightness to use when processing an image. Scale is 0% to 100%.
1191
+ saturation: The saturation to use when processing an image. Scale is 0% to 100%.
1192
+ """
1193
+ self._engine_client.execute_command(
1194
+ cmd.CaptureImageParams(
1195
+ fileName=filename,
1196
+ resolution=resolution,
1197
+ zoom=zoom,
1198
+ contrast=contrast,
1199
+ brightness=brightness,
1200
+ saturation=saturation,
1201
+ )
1202
+ )
1203
+
1157
1204
  def _convert_labware_location(
1158
1205
  self,
1159
1206
  location: Union[
@@ -1188,6 +1235,8 @@ class ProtocolCore(
1188
1235
  return ModuleLocation(moduleId=location.module_id)
1189
1236
  elif location is OffDeckType.OFF_DECK:
1190
1237
  return OFF_DECK_LOCATION
1238
+ elif location is OffDeckType.WASTE_CHUTE:
1239
+ return AddressableAreaLocation(addressableAreaName="gripperWasteChute")
1191
1240
  elif isinstance(location, DeckSlotName):
1192
1241
  return DeckSlotLocation(slotName=location)
1193
1242
  elif isinstance(location, StagingSlotName):
@@ -60,6 +60,8 @@ def _labware_location_string(
60
60
  return (
61
61
  f"stored in {_module_in_location_string(location.moduleId, engine_client)}"
62
62
  )
63
+ elif location == "wasteChuteLocation":
64
+ return "in waste chute"
63
65
 
64
66
 
65
67
  def _labware_name(engine_client: SyncClient, labware_id: str) -> str:
@@ -0,0 +1,48 @@
1
+ from datetime import datetime
2
+ from ..tasks import AbstractTaskCore
3
+ from opentrons.protocol_engine.clients import SyncClient as ProtocolEngineClient
4
+ from opentrons.protocol_engine.errors.exceptions import NoTaskFoundError
5
+
6
+
7
+ class EngineTaskCore(AbstractTaskCore):
8
+ def __init__(
9
+ self, task_id: str | None, engine_client: ProtocolEngineClient
10
+ ) -> None:
11
+ self._id = task_id
12
+ self._engine_client = engine_client
13
+
14
+ def get_created_at_timestamp(self) -> datetime:
15
+ if self._id is None:
16
+ raise NoTaskFoundError
17
+ try:
18
+ task = self._engine_client.state.tasks.get(self._id)
19
+ return task.createdAt
20
+ except NoTaskFoundError:
21
+ raise NoTaskFoundError
22
+
23
+ def is_done(self) -> bool:
24
+ if self._id is None:
25
+ raise NoTaskFoundError
26
+ try:
27
+ self._engine_client.state.tasks.get_finished(self._id)
28
+ return True
29
+ except NoTaskFoundError:
30
+ return False
31
+
32
+ def is_started(self) -> bool:
33
+ if self._id is None:
34
+ raise NoTaskFoundError
35
+ try:
36
+ self._engine_client.state.tasks.get_current(self._id)
37
+ return True
38
+ except NoTaskFoundError:
39
+ return self.is_done()
40
+
41
+ def get_finished_at_timestamp(self) -> datetime | None:
42
+ if self._id is None:
43
+ raise NoTaskFoundError
44
+ try:
45
+ finished_task = self._engine_client.state.tasks.get_finished(self._id)
46
+ return finished_task.finishedAt
47
+ except NoTaskFoundError:
48
+ return None
@@ -216,6 +216,14 @@ class WellCore(AbstractWellCore):
216
216
  labware_id=labware_id, well_name=well_name
217
217
  )
218
218
 
219
+ def has_tracked_liquid(self) -> bool:
220
+ """Return true if liquid has been loaded or probed."""
221
+ labware_id = self.labware_id
222
+ well_name = self._name
223
+ return self._engine_client.state.geometry.well_has_tracked_liquid(
224
+ labware_id=labware_id, well_name=well_name
225
+ )
226
+
219
227
  def get_liquid_volume(self) -> LiquidTrackingType:
220
228
  """Return the current volume in a well."""
221
229
  labware_id = self.labware_id
@@ -48,7 +48,10 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
48
48
  flow_rate: float,
49
49
  in_place: bool,
50
50
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
51
+ end_location: Optional[types.Location] = None,
52
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
51
53
  correction_volume: Optional[float] = None,
54
+ movement_delay: Optional[float] = None,
52
55
  ) -> None:
53
56
  """Aspirate a given volume of liquid from the specified location.
54
57
  Args:
@@ -74,7 +77,10 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
74
77
  in_place: bool,
75
78
  push_out: Optional[float],
76
79
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
80
+ end_location: Optional[types.Location] = None,
81
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
77
82
  correction_volume: Optional[float] = None,
83
+ movement_delay: Optional[float] = None,
78
84
  ) -> None:
79
85
  """Dispense a given volume of liquid into the specified location.
80
86
  Args:
@@ -378,6 +384,7 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
378
384
  trash_location: Union[types.Location, TrashBin, WasteChute],
379
385
  return_tip: bool,
380
386
  keep_last_tip: bool,
387
+ tips: Optional[List[WellCoreType]],
381
388
  ) -> None:
382
389
  """Transfer a liquid from source to dest according to liquid class properties."""
383
390
  ...
@@ -389,12 +396,17 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
389
396
  volume: float,
390
397
  source: Tuple[types.Location, WellCoreType],
391
398
  dest: List[Tuple[types.Location, WellCoreType]],
392
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
399
+ new_tip: Literal[
400
+ TransferTipPolicyV2.NEVER,
401
+ TransferTipPolicyV2.ONCE,
402
+ TransferTipPolicyV2.ALWAYS,
403
+ ],
393
404
  tip_racks: List[Tuple[types.Location, LabwareCoreType]],
394
405
  starting_tip: Optional[WellCoreType],
395
406
  trash_location: Union[types.Location, TrashBin, WasteChute],
396
407
  return_tip: bool,
397
408
  keep_last_tip: bool,
409
+ tips: Optional[List[WellCoreType]],
398
410
  ) -> None:
399
411
  """
400
412
  Distribute a liquid from single source to multiple destinations
@@ -409,12 +421,17 @@ class AbstractInstrument(ABC, Generic[WellCoreType, LabwareCoreType]):
409
421
  volume: float,
410
422
  source: List[Tuple[types.Location, WellCoreType]],
411
423
  dest: Union[Tuple[types.Location, WellCoreType], TrashBin, WasteChute],
412
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
424
+ new_tip: Literal[
425
+ TransferTipPolicyV2.NEVER,
426
+ TransferTipPolicyV2.ONCE,
427
+ TransferTipPolicyV2.ALWAYS,
428
+ ],
413
429
  tip_racks: List[Tuple[types.Location, LabwareCoreType]],
414
430
  starting_tip: Optional[WellCoreType],
415
431
  trash_location: Union[types.Location, TrashBin, WasteChute],
416
432
  return_tip: bool,
417
433
  keep_last_tip: bool,
434
+ tips: Optional[List[WellCoreType]],
418
435
  ) -> None:
419
436
  """
420
437
  Consolidate liquid from multiple sources to a single destination
@@ -93,7 +93,10 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
93
93
  flow_rate: float,
94
94
  in_place: bool,
95
95
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
96
+ end_location: Optional[types.Location] = None,
97
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
96
98
  correction_volume: Optional[float] = None,
99
+ movement_delay: Optional[float] = None,
97
100
  ) -> None:
98
101
  """Aspirate a given volume of liquid from the specified location.
99
102
  Args:
@@ -139,7 +142,10 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
139
142
  in_place: bool,
140
143
  push_out: Optional[float],
141
144
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
145
+ end_location: Optional[types.Location] = None,
146
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
142
147
  correction_volume: Optional[float] = None,
148
+ movement_delay: Optional[float] = None,
143
149
  ) -> None:
144
150
  """Dispense a given volume of liquid into the specified location.
145
151
  Args:
@@ -622,6 +628,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
622
628
  trash_location: Union[types.Location, TrashBin, WasteChute],
623
629
  return_tip: bool,
624
630
  keep_last_tip: bool,
631
+ tips: Optional[List[LegacyWellCore]],
625
632
  ) -> None:
626
633
  """This will never be called because it was added in API 2.23"""
627
634
  assert False, "transfer_liquid is not supported in legacy context"
@@ -632,12 +639,17 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
632
639
  volume: float,
633
640
  source: Tuple[types.Location, LegacyWellCore],
634
641
  dest: List[Tuple[types.Location, LegacyWellCore]],
635
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
642
+ new_tip: Literal[
643
+ TransferTipPolicyV2.NEVER,
644
+ TransferTipPolicyV2.ONCE,
645
+ TransferTipPolicyV2.ALWAYS,
646
+ ],
636
647
  tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
637
648
  starting_tip: Optional[LegacyWellCore],
638
649
  trash_location: Union[types.Location, TrashBin, WasteChute],
639
650
  return_tip: bool,
640
651
  keep_last_tip: bool,
652
+ tips: Optional[List[LegacyWellCore]],
641
653
  ) -> None:
642
654
  """This will never be called because it was added in API 2.23"""
643
655
  assert False, "distribute_liquid is not supported in legacy context"
@@ -648,12 +660,17 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore, LegacyLabwareCore]
648
660
  volume: float,
649
661
  source: List[Tuple[types.Location, LegacyWellCore]],
650
662
  dest: Union[Tuple[types.Location, LegacyWellCore], TrashBin, WasteChute],
651
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
663
+ new_tip: Literal[
664
+ TransferTipPolicyV2.NEVER,
665
+ TransferTipPolicyV2.ONCE,
666
+ TransferTipPolicyV2.ALWAYS,
667
+ ],
652
668
  tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
653
669
  starting_tip: Optional[LegacyWellCore],
654
670
  trash_location: Union[types.Location, TrashBin, WasteChute],
655
671
  return_tip: bool,
656
672
  keep_last_tip: bool,
673
+ tips: Optional[List[LegacyWellCore]],
657
674
  ) -> None:
658
675
  """This will never be called because it was added in API 2.23."""
659
676
  assert False, "consolidate_liquid is not supported in legacy context"
@@ -31,6 +31,7 @@ from ..module import (
31
31
  )
32
32
 
33
33
  from .legacy_labware_core import LegacyLabwareCore
34
+ from .tasks import LegacyTaskCore
34
35
  from .module_geometry import ModuleGeometry, ThermocyclerGeometry, HeaterShakerGeometry
35
36
  from ...labware import Labware
36
37
 
@@ -116,9 +117,10 @@ class LegacyTemperatureModuleCore(
116
117
 
117
118
  _sync_module_hardware: SynchronousAdapter[hw_modules.TempDeck]
118
119
 
119
- def set_target_temperature(self, celsius: float) -> None:
120
+ def set_target_temperature(self, celsius: float) -> LegacyTaskCore:
120
121
  """Set the Temperature Module's target temperature in °C."""
121
122
  self._sync_module_hardware.start_set_temperature(celsius)
123
+ return LegacyTaskCore()
122
124
 
123
125
  def wait_for_target_temperature(self, celsius: Optional[float] = None) -> None:
124
126
  """Wait until the module's target temperature is reached.
@@ -263,6 +265,7 @@ class LegacyThermocyclerCore(
263
265
  def set_target_block_temperature(
264
266
  self,
265
267
  celsius: float,
268
+ ramp_rate: Optional[float],
266
269
  hold_time_seconds: Optional[float] = None,
267
270
  block_max_volume: Optional[float] = None,
268
271
  ) -> None:
@@ -271,6 +274,7 @@ class LegacyThermocyclerCore(
271
274
  celsius=celsius,
272
275
  hold_time_seconds=hold_time_seconds,
273
276
  volume=block_max_volume,
277
+ ramp_rate=ramp_rate,
274
278
  )
275
279
 
276
280
  def wait_for_block_temperature(self) -> None:
@@ -281,6 +285,19 @@ class LegacyThermocyclerCore(
281
285
  """Set the target temperature for the heated lid, in °C."""
282
286
  self._sync_module_hardware.set_target_lid_temperature(celsius=celsius)
283
287
 
288
+ def start_set_target_lid_temperature(self, celsius: float) -> LegacyTaskCore:
289
+ """Set the target temperature for the heated lid, in °C."""
290
+ assert False, "start_set_target_lid_temperature only supported on engine core"
291
+
292
+ def start_set_target_block_temperature(
293
+ self,
294
+ celsius: float,
295
+ ramp_rate: Optional[float],
296
+ block_max_volume: Optional[float] = None,
297
+ ) -> LegacyTaskCore:
298
+ """Set the target temperature for the heated block, in °C."""
299
+ assert False, "start_set_target_block_temperature only supported on engine core"
300
+
284
301
  def wait_for_lid_temperature(self) -> None:
285
302
  """Wait for target lid temperature to be reached."""
286
303
  self._sync_module_hardware.wait_for_lid_target()
@@ -296,6 +313,15 @@ class LegacyThermocyclerCore(
296
313
  steps=steps, repetitions=repetitions, volume=block_max_volume
297
314
  )
298
315
 
316
+ def start_execute_profile(
317
+ self,
318
+ steps: List[ThermocyclerStep],
319
+ repetitions: int,
320
+ block_max_volume: Optional[float] = None,
321
+ ) -> LegacyTaskCore:
322
+ """Start a Thermocycler Profile."""
323
+ assert False, "start_execute_profile only supported on engine core"
324
+
299
325
  def deactivate_lid(self) -> None:
300
326
  """Turn off the heated lid."""
301
327
  self._sync_module_hardware.deactivate_lid()
@@ -413,9 +439,10 @@ class LegacyHeaterShakerCore(
413
439
  _sync_module_hardware: SynchronousAdapter[hw_modules.HeaterShaker]
414
440
  _geometry: HeaterShakerGeometry
415
441
 
416
- def set_target_temperature(self, celsius: float) -> None:
442
+ def set_target_temperature(self, celsius: float) -> LegacyTaskCore:
417
443
  """Set the labware plate's target temperature in °C."""
418
444
  self._sync_module_hardware.start_set_temperature(celsius)
445
+ return LegacyTaskCore()
419
446
 
420
447
  def wait_for_target_temperature(self) -> None:
421
448
  """Wait for the labware plate's target temperature to be reached."""
@@ -442,6 +469,10 @@ class LegacyHeaterShakerCore(
442
469
  "Cannot start shaking unless labware latch is closed."
443
470
  )
444
471
 
472
+ def set_shake_speed(self, rpm: int) -> LegacyTaskCore:
473
+ """Set heatershaker speed."""
474
+ assert False, "set_shake_speed only supported on engine core."
475
+
445
476
  def open_labware_latch(self) -> None:
446
477
  """Open the labware latch."""
447
478
  if self.get_speed_status() != SpeedStatus.IDLE:
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, List, Optional, Set, Union, cast, Tuple
2
+ from typing import Dict, List, Optional, Set, Union, cast, Tuple, Sequence
3
3
 
4
4
  from opentrons_shared_data.deck.types import DeckDefinitionV5, SlotDefV3
5
5
  from opentrons_shared_data.labware.types import LabwareDefinition
@@ -34,6 +34,7 @@ from .legacy_instrument_core import LegacyInstrumentCore
34
34
  from .labware_offset_provider import AbstractLabwareOffsetProvider
35
35
  from .legacy_labware_core import LegacyLabwareCore
36
36
  from .load_info import LoadInfo, InstrumentLoadInfo, LabwareLoadInfo, ModuleLoadInfo
37
+ from .tasks import LegacyTaskCore
37
38
 
38
39
  logger = logging.getLogger(__name__)
39
40
 
@@ -43,6 +44,7 @@ class LegacyProtocolCore(
43
44
  LegacyInstrumentCore,
44
45
  LegacyLabwareCore,
45
46
  legacy_module_core.LegacyModuleCore,
47
+ LegacyTaskCore,
46
48
  ]
47
49
  ):
48
50
  def __init__(
@@ -610,3 +612,23 @@ class LegacyProtocolCore(
610
612
  ]:
611
613
  """Get labware parent location."""
612
614
  assert False, "get_labware_location only supported on engine core"
615
+
616
+ def capture_image(
617
+ self,
618
+ filename: Optional[str] = None,
619
+ resolution: Optional[Tuple[int, int]] = None,
620
+ zoom: Optional[float] = None,
621
+ contrast: Optional[float] = None,
622
+ brightness: Optional[float] = None,
623
+ saturation: Optional[float] = None,
624
+ ) -> None:
625
+ "Capture an image using a camera."
626
+ assert False, "capture_image only supported on engine core"
627
+
628
+ def wait_for_tasks(self, task: Sequence[LegacyTaskCore]) -> None:
629
+ """Wait for list of tasks to complete before executing subsequent commands."""
630
+ assert False, "wait_for_tasks only supported on engine core"
631
+
632
+ def create_timer(self, seconds: float) -> LegacyTaskCore:
633
+ """Create a timer task that runs in the background."""
634
+ assert False, "create_timer only supported on engine core"
@@ -151,6 +151,10 @@ class LegacyWellCore(AbstractWellCore):
151
151
  """Return the volume contained in a well at any height."""
152
152
  return 0.0
153
153
 
154
+ def has_tracked_liquid(self) -> bool:
155
+ """Return true if liquid has been loaded or probed."""
156
+ return False
157
+
154
158
  # TODO(mc, 2022-10-28): is this used and/or necessary?
155
159
  def __repr__(self) -> str:
156
160
  """Use the well's display name as its repr."""
@@ -0,0 +1,19 @@
1
+ from ..tasks import AbstractTaskCore
2
+ from datetime import datetime
3
+
4
+
5
+ class LegacyTaskCore(AbstractTaskCore):
6
+ def __init__(self) -> None:
7
+ pass
8
+
9
+ def get_created_at_timestamp(self) -> datetime:
10
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
11
+
12
+ def is_done(self) -> bool:
13
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
14
+
15
+ def is_started(self) -> bool:
16
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
17
+
18
+ def get_finished_at_timestamp(self) -> datetime | None:
19
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
@@ -106,7 +106,10 @@ class LegacyInstrumentCoreSimulator(
106
106
  flow_rate: float,
107
107
  in_place: bool,
108
108
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
109
+ end_location: Optional[types.Location] = None,
110
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
109
111
  correction_volume: Optional[float] = None,
112
+ movement_delay: Optional[float] = None,
110
113
  ) -> None:
111
114
  if self.get_current_volume() == 0:
112
115
  # Make sure we're at the top of the labware and clear of any
@@ -149,7 +152,10 @@ class LegacyInstrumentCoreSimulator(
149
152
  in_place: bool,
150
153
  push_out: Optional[float],
151
154
  meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
155
+ end_location: Optional[types.Location] = None,
156
+ end_meniscus_tracking: Optional[types.MeniscusTrackingTarget] = None,
152
157
  correction_volume: Optional[float] = None,
158
+ movement_delay: Optional[float] = None,
153
159
  ) -> None:
154
160
  if isinstance(location, (TrashBin, WasteChute)):
155
161
  raise APIVersionError(
@@ -536,6 +542,7 @@ class LegacyInstrumentCoreSimulator(
536
542
  trash_location: Union[types.Location, TrashBin, WasteChute],
537
543
  return_tip: bool,
538
544
  keep_last_tip: bool,
545
+ tips: Optional[List[LegacyWellCore]],
539
546
  ) -> None:
540
547
  """This will never be called because it was added in API 2.23."""
541
548
  assert False, "transfer_liquid is not supported in legacy context"
@@ -546,12 +553,17 @@ class LegacyInstrumentCoreSimulator(
546
553
  volume: float,
547
554
  source: Tuple[types.Location, LegacyWellCore],
548
555
  dest: List[Tuple[types.Location, LegacyWellCore]],
549
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
556
+ new_tip: Literal[
557
+ TransferTipPolicyV2.NEVER,
558
+ TransferTipPolicyV2.ONCE,
559
+ TransferTipPolicyV2.ALWAYS,
560
+ ],
550
561
  tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
551
562
  starting_tip: Optional[LegacyWellCore],
552
563
  trash_location: Union[types.Location, TrashBin, WasteChute],
553
564
  return_tip: bool,
554
565
  keep_last_tip: bool,
566
+ tips: Optional[List[LegacyWellCore]],
555
567
  ) -> None:
556
568
  """This will never be called because it was added in API 2.23."""
557
569
  assert False, "distribute_liquid is not supported in legacy context"
@@ -562,12 +574,17 @@ class LegacyInstrumentCoreSimulator(
562
574
  volume: float,
563
575
  source: List[Tuple[types.Location, LegacyWellCore]],
564
576
  dest: Union[Tuple[types.Location, LegacyWellCore], TrashBin, WasteChute],
565
- new_tip: Literal[TransferTipPolicyV2.NEVER, TransferTipPolicyV2.ONCE],
577
+ new_tip: Literal[
578
+ TransferTipPolicyV2.NEVER,
579
+ TransferTipPolicyV2.ONCE,
580
+ TransferTipPolicyV2.ALWAYS,
581
+ ],
566
582
  tip_racks: List[Tuple[types.Location, LegacyLabwareCore]],
567
583
  starting_tip: Optional[LegacyWellCore],
568
584
  trash_location: Union[types.Location, TrashBin, WasteChute],
569
585
  return_tip: bool,
570
586
  keep_last_tip: bool,
587
+ tips: Optional[List[LegacyWellCore]],
571
588
  ) -> None:
572
589
  """This will never be called because it was added in API 2.23."""
573
590
  assert False, "consolidate_liquid is not supported in legacy context"
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, Optional
2
+ from typing import Dict, Optional, Sequence
3
3
 
4
4
  from opentrons_shared_data.pipette.types import PipetteNameType
5
5
  from opentrons_shared_data.pipette.pipette_load_name_conversions import (
@@ -16,6 +16,7 @@ from ..legacy.legacy_module_core import LegacyModuleCore
16
16
  from ..legacy.load_info import InstrumentLoadInfo
17
17
 
18
18
  from .legacy_instrument_core import LegacyInstrumentCoreSimulator
19
+ from .tasks import LegacyTaskCore
19
20
 
20
21
  logger = logging.getLogger(__name__)
21
22
 
@@ -23,7 +24,10 @@ logger = logging.getLogger(__name__)
23
24
  class LegacyProtocolCoreSimulator(
24
25
  LegacyProtocolCore,
25
26
  AbstractProtocol[
26
- LegacyInstrumentCoreSimulator, LegacyLabwareCore, LegacyModuleCore
27
+ LegacyInstrumentCoreSimulator,
28
+ LegacyLabwareCore,
29
+ LegacyModuleCore,
30
+ LegacyTaskCore,
27
31
  ],
28
32
  ):
29
33
  _instruments: Dict[Mount, Optional[LegacyInstrumentCoreSimulator]] # type: ignore[assignment]
@@ -83,3 +87,11 @@ class LegacyProtocolCoreSimulator(
83
87
  )
84
88
 
85
89
  return new_instr
90
+
91
+ def wait_for_tasks(self, task: Sequence[LegacyTaskCore]) -> None:
92
+ """Wait for list of tasks to complete before executing subsequent commands."""
93
+ assert False, "wait_for_tasks only supported on engine core"
94
+
95
+ def create_timer(self, seconds: float) -> LegacyTaskCore:
96
+ """Create a timer task that runs in the background."""
97
+ assert False, "create_timer only supported on engine core"
@@ -0,0 +1,19 @@
1
+ from ..tasks import AbstractTaskCore
2
+ from datetime import datetime
3
+
4
+
5
+ class LegacyTaskCore(AbstractTaskCore):
6
+ def __init__(self, created_at: datetime) -> None:
7
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
8
+
9
+ def get_created_at_timestamp(self) -> datetime:
10
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
11
+
12
+ def is_done(self) -> bool:
13
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
14
+
15
+ def is_started(self) -> bool:
16
+ raise NotImplementedError("Legacy protocols do not implement tasks.")
17
+
18
+ def get_finished_at_timestamp(self) -> datetime | None:
19
+ raise NotImplementedError("Legacy protocols do not implement tasks.")