opentrons 8.1.0a0__py2.py3-none-any.whl → 8.2.0a0__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 (230) hide show
  1. opentrons/cli/analyze.py +71 -7
  2. opentrons/config/__init__.py +9 -0
  3. opentrons/config/advanced_settings.py +22 -0
  4. opentrons/config/defaults_ot3.py +14 -36
  5. opentrons/config/feature_flags.py +4 -0
  6. opentrons/config/types.py +6 -17
  7. opentrons/drivers/absorbance_reader/abstract.py +27 -3
  8. opentrons/drivers/absorbance_reader/async_byonoy.py +207 -154
  9. opentrons/drivers/absorbance_reader/driver.py +24 -15
  10. opentrons/drivers/absorbance_reader/hid_protocol.py +79 -50
  11. opentrons/drivers/absorbance_reader/simulator.py +32 -6
  12. opentrons/drivers/types.py +23 -1
  13. opentrons/execute.py +2 -2
  14. opentrons/hardware_control/api.py +18 -10
  15. opentrons/hardware_control/backends/controller.py +3 -2
  16. opentrons/hardware_control/backends/flex_protocol.py +11 -5
  17. opentrons/hardware_control/backends/ot3controller.py +18 -50
  18. opentrons/hardware_control/backends/ot3simulator.py +7 -6
  19. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  20. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  21. opentrons/hardware_control/module_control.py +43 -2
  22. opentrons/hardware_control/modules/__init__.py +7 -1
  23. opentrons/hardware_control/modules/absorbance_reader.py +230 -83
  24. opentrons/hardware_control/modules/errors.py +7 -0
  25. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  26. opentrons/hardware_control/modules/magdeck.py +12 -3
  27. opentrons/hardware_control/modules/mod_abc.py +27 -2
  28. opentrons/hardware_control/modules/tempdeck.py +15 -7
  29. opentrons/hardware_control/modules/thermocycler.py +69 -3
  30. opentrons/hardware_control/modules/types.py +11 -5
  31. opentrons/hardware_control/modules/update.py +11 -5
  32. opentrons/hardware_control/modules/utils.py +3 -1
  33. opentrons/hardware_control/ot3_calibration.py +6 -6
  34. opentrons/hardware_control/ot3api.py +126 -89
  35. opentrons/hardware_control/poller.py +15 -11
  36. opentrons/hardware_control/protocols/__init__.py +1 -7
  37. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  38. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  39. opentrons/motion_planning/__init__.py +2 -0
  40. opentrons/motion_planning/waypoints.py +32 -0
  41. opentrons/protocol_api/__init__.py +2 -1
  42. opentrons/protocol_api/_liquid.py +87 -1
  43. opentrons/protocol_api/_parameter_context.py +10 -1
  44. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  45. opentrons/protocol_api/core/engine/instrument.py +29 -25
  46. opentrons/protocol_api/core/engine/labware.py +10 -2
  47. opentrons/protocol_api/core/engine/module_core.py +129 -17
  48. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +355 -0
  49. opentrons/protocol_api/core/engine/protocol.py +55 -2
  50. opentrons/protocol_api/core/instrument.py +2 -0
  51. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  52. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +5 -2
  53. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  54. opentrons/protocol_api/core/module.py +22 -4
  55. opentrons/protocol_api/core/protocol.py +5 -2
  56. opentrons/protocol_api/instrument_context.py +52 -20
  57. opentrons/protocol_api/labware.py +13 -1
  58. opentrons/protocol_api/module_contexts.py +68 -13
  59. opentrons/protocol_api/protocol_context.py +38 -4
  60. opentrons/protocol_api/validation.py +5 -3
  61. opentrons/protocol_engine/__init__.py +10 -9
  62. opentrons/protocol_engine/actions/__init__.py +5 -0
  63. opentrons/protocol_engine/actions/actions.py +42 -25
  64. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  65. opentrons/protocol_engine/clients/sync_client.py +7 -1
  66. opentrons/protocol_engine/clients/transports.py +1 -1
  67. opentrons/protocol_engine/commands/__init__.py +0 -4
  68. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  69. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +161 -0
  70. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +53 -9
  71. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +160 -0
  72. opentrons/protocol_engine/commands/absorbance_reader/read.py +196 -0
  73. opentrons/protocol_engine/commands/aspirate.py +29 -16
  74. opentrons/protocol_engine/commands/aspirate_in_place.py +32 -15
  75. opentrons/protocol_engine/commands/blow_out.py +63 -14
  76. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  77. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  78. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  79. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  80. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  81. opentrons/protocol_engine/commands/command.py +28 -17
  82. opentrons/protocol_engine/commands/command_unions.py +37 -24
  83. opentrons/protocol_engine/commands/comment.py +5 -3
  84. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  85. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  86. opentrons/protocol_engine/commands/custom.py +5 -3
  87. opentrons/protocol_engine/commands/dispense.py +42 -20
  88. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  89. opentrons/protocol_engine/commands/drop_tip.py +68 -15
  90. opentrons/protocol_engine/commands/drop_tip_in_place.py +52 -11
  91. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  92. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  93. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  94. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  95. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  96. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  97. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  98. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  99. opentrons/protocol_engine/commands/home.py +11 -5
  100. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  101. opentrons/protocol_engine/commands/load_labware.py +19 -5
  102. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  103. opentrons/protocol_engine/commands/load_module.py +43 -6
  104. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  105. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  106. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  107. opentrons/protocol_engine/commands/move_labware.py +106 -19
  108. opentrons/protocol_engine/commands/move_relative.py +15 -3
  109. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  110. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  111. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  112. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  113. opentrons/protocol_engine/commands/pick_up_tip.py +50 -29
  114. opentrons/protocol_engine/commands/pipetting_common.py +39 -15
  115. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  116. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  117. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  118. opentrons/protocol_engine/commands/save_position.py +2 -3
  119. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  120. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  121. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  122. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  123. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  124. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  125. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  126. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  127. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  128. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  129. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  130. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  131. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  132. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  133. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  135. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  136. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  137. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  138. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  139. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  140. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +194 -0
  141. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +75 -0
  142. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +5 -3
  143. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  144. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  145. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  146. opentrons/protocol_engine/create_protocol_engine.py +41 -8
  147. opentrons/protocol_engine/engine_support.py +2 -1
  148. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  149. opentrons/protocol_engine/errors/__init__.py +18 -0
  150. opentrons/protocol_engine/errors/exceptions.py +114 -2
  151. opentrons/protocol_engine/execution/__init__.py +2 -0
  152. opentrons/protocol_engine/execution/command_executor.py +22 -13
  153. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  154. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  155. opentrons/protocol_engine/execution/equipment.py +2 -1
  156. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  157. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  158. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  159. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  160. opentrons/protocol_engine/execution/labware_movement.py +6 -3
  161. opentrons/protocol_engine/execution/movement.py +8 -3
  162. opentrons/protocol_engine/execution/pipetting.py +7 -4
  163. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  164. opentrons/protocol_engine/execution/run_control.py +1 -1
  165. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  166. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  167. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  168. opentrons/protocol_engine/notes/__init__.py +14 -2
  169. opentrons/protocol_engine/notes/notes.py +18 -1
  170. opentrons/protocol_engine/plugins.py +1 -1
  171. opentrons/protocol_engine/protocol_engine.py +54 -31
  172. opentrons/protocol_engine/resources/__init__.py +2 -0
  173. opentrons/protocol_engine/resources/deck_data_provider.py +58 -5
  174. opentrons/protocol_engine/resources/file_provider.py +157 -0
  175. opentrons/protocol_engine/resources/fixture_validation.py +5 -0
  176. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  177. opentrons/protocol_engine/state/__init__.py +0 -70
  178. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  179. opentrons/protocol_engine/state/command_history.py +21 -2
  180. opentrons/protocol_engine/state/commands.py +110 -31
  181. opentrons/protocol_engine/state/files.py +59 -0
  182. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  183. opentrons/protocol_engine/state/geometry.py +359 -15
  184. opentrons/protocol_engine/state/labware.py +166 -63
  185. opentrons/protocol_engine/state/liquids.py +1 -1
  186. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +19 -3
  187. opentrons/protocol_engine/state/modules.py +167 -85
  188. opentrons/protocol_engine/state/motion.py +16 -9
  189. opentrons/protocol_engine/state/pipettes.py +157 -317
  190. opentrons/protocol_engine/state/state.py +30 -1
  191. opentrons/protocol_engine/state/state_summary.py +3 -0
  192. opentrons/protocol_engine/state/tips.py +69 -114
  193. opentrons/protocol_engine/state/update_types.py +408 -0
  194. opentrons/protocol_engine/state/wells.py +236 -0
  195. opentrons/protocol_engine/types.py +90 -0
  196. opentrons/protocol_reader/file_format_validator.py +83 -15
  197. opentrons/protocol_runner/json_translator.py +21 -5
  198. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  199. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  200. opentrons/protocol_runner/protocol_runner.py +6 -3
  201. opentrons/protocol_runner/run_orchestrator.py +26 -6
  202. opentrons/protocols/advanced_control/mix.py +3 -5
  203. opentrons/protocols/advanced_control/transfers.py +125 -56
  204. opentrons/protocols/api_support/constants.py +1 -1
  205. opentrons/protocols/api_support/definitions.py +1 -1
  206. opentrons/protocols/api_support/labware_like.py +4 -4
  207. opentrons/protocols/api_support/tip_tracker.py +2 -2
  208. opentrons/protocols/api_support/types.py +15 -2
  209. opentrons/protocols/api_support/util.py +30 -42
  210. opentrons/protocols/duration/errors.py +1 -1
  211. opentrons/protocols/duration/estimator.py +50 -29
  212. opentrons/protocols/execution/dev_types.py +2 -2
  213. opentrons/protocols/execution/execute_json_v4.py +15 -10
  214. opentrons/protocols/execution/execute_python.py +8 -3
  215. opentrons/protocols/geometry/planning.py +12 -12
  216. opentrons/protocols/labware.py +17 -33
  217. opentrons/simulate.py +3 -3
  218. opentrons/types.py +30 -3
  219. opentrons/util/logging_config.py +34 -0
  220. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/METADATA +5 -4
  221. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/RECORD +227 -215
  222. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  223. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  224. opentrons/protocol_runner/thread_async_queue.py +0 -174
  225. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  226. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  227. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/LICENSE +0 -0
  228. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/WHEEL +0 -0
  229. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/entry_points.txt +0 -0
  230. {opentrons-8.1.0a0.dist-info → opentrons-8.2.0a0.dist-info}/top_level.txt +0 -0
@@ -80,6 +80,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
80
80
  rate: float,
81
81
  flow_rate: float,
82
82
  in_place: bool,
83
+ is_meniscus: Optional[bool] = None,
83
84
  ) -> None:
84
85
  """Aspirate a given volume of liquid from the specified location.
85
86
  Args:
@@ -122,6 +123,7 @@ class LegacyInstrumentCore(AbstractInstrument[LegacyWellCore]):
122
123
  flow_rate: float,
123
124
  in_place: bool,
124
125
  push_out: Optional[float],
126
+ is_meniscus: Optional[bool] = None,
125
127
  ) -> None:
126
128
  """Dispense a given volume of liquid into the specified location.
127
129
  Args:
@@ -17,7 +17,7 @@ from opentrons.protocols import labware as labware_definition
17
17
 
18
18
  from ...labware import Labware
19
19
  from ...disposal_locations import TrashBin, WasteChute
20
- from ..._liquid import Liquid
20
+ from ..._liquid import Liquid, LiquidClass
21
21
  from ..._types import OffDeckType
22
22
  from ..protocol import AbstractProtocol
23
23
  from ..labware import LabwareLoadParams
@@ -267,7 +267,6 @@ class LegacyProtocolCore(
267
267
  """Load an adapter using its identifying parameters"""
268
268
  raise APIVersionError(api_element="Loading adapter")
269
269
 
270
- # TODO (spp, 2022-12-14): https://opentrons.atlassian.net/browse/RLAB-237
271
270
  def move_labware(
272
271
  self,
273
272
  labware_core: LegacyLabwareCore,
@@ -532,6 +531,10 @@ class LegacyProtocolCore(
532
531
  """Define a liquid to load into a well."""
533
532
  assert False, "define_liquid only supported on engine core"
534
533
 
534
+ def define_liquid_class(self, name: str) -> LiquidClass:
535
+ """Define a liquid class."""
536
+ assert False, "define_liquid_class is only supported on engine core"
537
+
535
538
  def get_labware_location(
536
539
  self, labware_core: LegacyLabwareCore
537
540
  ) -> Union[
@@ -91,6 +91,7 @@ class LegacyInstrumentCoreSimulator(AbstractInstrument[LegacyWellCore]):
91
91
  rate: float,
92
92
  flow_rate: float,
93
93
  in_place: bool,
94
+ is_meniscus: Optional[bool] = None,
94
95
  ) -> None:
95
96
  if self.get_current_volume() == 0:
96
97
  # Make sure we're at the top of the labware and clear of any
@@ -132,6 +133,7 @@ class LegacyInstrumentCoreSimulator(AbstractInstrument[LegacyWellCore]):
132
133
  flow_rate: float,
133
134
  in_place: bool,
134
135
  push_out: Optional[float],
136
+ is_meniscus: Optional[bool] = None,
135
137
  ) -> None:
136
138
  if isinstance(location, (TrashBin, WasteChute)):
137
139
  raise APIVersionError(
@@ -2,7 +2,7 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  from abc import ABC, abstractmethod
5
- from typing import List, Optional, TypeVar, ClassVar
5
+ from typing import List, Dict, Optional, TypeVar, ClassVar
6
6
 
7
7
  from opentrons.drivers.types import (
8
8
  HeaterShakerLabwareLatchStatus,
@@ -16,6 +16,7 @@ from opentrons.hardware_control.modules.types import (
16
16
  MagneticStatus,
17
17
  SpeedStatus,
18
18
  )
19
+ from opentrons.protocol_engine.types import ABSMeasureMode
19
20
  from opentrons.types import DeckSlotName
20
21
 
21
22
 
@@ -355,9 +356,26 @@ class AbstractAbsorbanceReaderCore(AbstractModuleCore):
355
356
  """Get the module's unique hardware serial number."""
356
357
 
357
358
  @abstractmethod
358
- def initialize(self, wavelength: int) -> None:
359
+ def initialize(
360
+ self,
361
+ mode: ABSMeasureMode,
362
+ wavelengths: List[int],
363
+ reference_wavelength: Optional[int] = None,
364
+ ) -> None:
359
365
  """Initialize the Absorbance Reader by taking zero reading."""
360
366
 
361
367
  @abstractmethod
362
- def initiate_read(self) -> None:
363
- """Initiate read on the Absorbance Reader."""
368
+ def read(self, filename: Optional[str] = None) -> Dict[int, Dict[str, float]]:
369
+ """Get an absorbance reading from the Absorbance Reader."""
370
+
371
+ @abstractmethod
372
+ def close_lid(self) -> None:
373
+ """Close the Absorbance Reader's lid."""
374
+
375
+ @abstractmethod
376
+ def open_lid(self) -> None:
377
+ """Open the Absorbance Reader's lid."""
378
+
379
+ @abstractmethod
380
+ def is_lid_on(self) -> bool:
381
+ """Return True if the Absorbance Reader's lid is currently closed."""
@@ -18,7 +18,7 @@ from opentrons.protocols.api_support.util import AxisMaxSpeeds
18
18
  from .instrument import InstrumentCoreType
19
19
  from .labware import LabwareCoreType, LabwareLoadParams
20
20
  from .module import ModuleCoreType
21
- from .._liquid import Liquid
21
+ from .._liquid import Liquid, LiquidClass
22
22
  from .._types import OffDeckType
23
23
  from ..disposal_locations import TrashBin, WasteChute
24
24
 
@@ -93,7 +93,6 @@ class AbstractProtocol(
93
93
  """Load an adapter using its identifying parameters"""
94
94
  ...
95
95
 
96
- # TODO (spp, 2022-12-14): https://opentrons.atlassian.net/browse/RLAB-237
97
96
  @abstractmethod
98
97
  def move_labware(
99
98
  self,
@@ -248,6 +247,10 @@ class AbstractProtocol(
248
247
  ) -> Liquid:
249
248
  """Define a liquid to load into a well."""
250
249
 
250
+ @abstractmethod
251
+ def define_liquid_class(self, name: str) -> LiquidClass:
252
+ """Define a liquid class for use in transfer functions."""
253
+
251
254
  @abstractmethod
252
255
  def get_labware_location(
253
256
  self, labware_core: LabwareCoreType
@@ -39,12 +39,7 @@ from ._nozzle_layout import NozzleLayout
39
39
  from . import labware, validation
40
40
 
41
41
 
42
- AdvancedLiquidHandling = Union[
43
- labware.Well,
44
- types.Location,
45
- Sequence[Union[labware.Well, types.Location]],
46
- Sequence[Sequence[labware.Well]],
47
- ]
42
+ AdvancedLiquidHandling = transfers.AdvancedLiquidHandling
48
43
 
49
44
  _DEFAULT_ASPIRATE_CLEARANCE = 1.0
50
45
  _DEFAULT_DISPENSE_CLEARANCE = 1.0
@@ -222,8 +217,9 @@ class InstrumentContext(publisher.CommandPublisher):
222
217
  )
223
218
  )
224
219
 
225
- well: Optional[labware.Well] = None
226
220
  move_to_location: types.Location
221
+ well: Optional[labware.Well] = None
222
+ is_meniscus: Optional[bool] = None
227
223
  last_location = self._get_last_location_by_api_version()
228
224
  try:
229
225
  target = validation.validate_location(
@@ -237,17 +233,13 @@ class InstrumentContext(publisher.CommandPublisher):
237
233
  "knows where it is."
238
234
  ) from e
239
235
 
240
- if isinstance(target, validation.WellTarget):
241
- move_to_location = target.location or target.well.bottom(
242
- z=self._well_bottom_clearances.aspirate
243
- )
244
- well = target.well
245
- if isinstance(target, validation.PointTarget):
246
- move_to_location = target.location
247
236
  if isinstance(target, (TrashBin, WasteChute)):
248
237
  raise ValueError(
249
238
  "Trash Bin and Waste Chute are not acceptable location parameters for Aspirate commands."
250
239
  )
240
+ move_to_location, well, is_meniscus = self._handle_aspirate_target(
241
+ target=target
242
+ )
251
243
  if self.api_version >= APIVersion(2, 11):
252
244
  instrument.validate_takes_liquid(
253
245
  location=move_to_location,
@@ -287,6 +279,7 @@ class InstrumentContext(publisher.CommandPublisher):
287
279
  rate=rate,
288
280
  flow_rate=flow_rate,
289
281
  in_place=target.in_place,
282
+ is_meniscus=is_meniscus,
290
283
  )
291
284
 
292
285
  return self
@@ -389,6 +382,7 @@ class InstrumentContext(publisher.CommandPublisher):
389
382
  )
390
383
  )
391
384
  well: Optional[labware.Well] = None
385
+ is_meniscus: Optional[bool] = None
392
386
  last_location = self._get_last_location_by_api_version()
393
387
 
394
388
  try:
@@ -407,6 +401,7 @@ class InstrumentContext(publisher.CommandPublisher):
407
401
  well = target.well
408
402
  if target.location:
409
403
  move_to_location = target.location
404
+ is_meniscus = target.location.is_meniscus
410
405
  elif well.parent._core.is_fixed_trash():
411
406
  move_to_location = target.well.top()
412
407
  else:
@@ -472,6 +467,7 @@ class InstrumentContext(publisher.CommandPublisher):
472
467
  flow_rate=flow_rate,
473
468
  in_place=target.in_place,
474
469
  push_out=push_out,
470
+ is_meniscus=is_meniscus,
475
471
  )
476
472
 
477
473
  return self
@@ -545,12 +541,12 @@ class InstrumentContext(publisher.CommandPublisher):
545
541
  ),
546
542
  ):
547
543
  self.aspirate(volume, location, rate)
548
- while repetitions - 1 > 0:
549
- self.dispense(volume, rate=rate, **dispense_kwargs)
550
- self.aspirate(volume, rate=rate)
551
- repetitions -= 1
552
- self.dispense(volume, rate=rate)
553
-
544
+ with AutoProbeDisable(self):
545
+ while repetitions - 1 > 0:
546
+ self.dispense(volume, rate=rate, **dispense_kwargs)
547
+ self.aspirate(volume, rate=rate)
548
+ repetitions -= 1
549
+ self.dispense(volume, rate=rate)
554
550
  return self
555
551
 
556
552
  @requires_version(2, 0)
@@ -2196,6 +2192,42 @@ class InstrumentContext(publisher.CommandPublisher):
2196
2192
  )
2197
2193
  # SINGLE, QUADRANT and ALL are supported by all pipettes
2198
2194
 
2195
+ def _handle_aspirate_target(
2196
+ self, target: validation.ValidTarget
2197
+ ) -> tuple[types.Location, Optional[labware.Well], Optional[bool]]:
2198
+ move_to_location: types.Location
2199
+ well: Optional[labware.Well] = None
2200
+ is_meniscus: Optional[bool] = None
2201
+ if isinstance(target, validation.WellTarget):
2202
+ well = target.well
2203
+ if target.location:
2204
+ move_to_location = target.location
2205
+ is_meniscus = target.location.is_meniscus
2206
+
2207
+ else:
2208
+ move_to_location = target.well.bottom(
2209
+ z=self._well_bottom_clearances.aspirate
2210
+ )
2211
+ if isinstance(target, validation.PointTarget):
2212
+ move_to_location = target.location
2213
+ return (move_to_location, well, is_meniscus)
2214
+
2215
+
2216
+ class AutoProbeDisable:
2217
+ """Use this class to temporarily disable automatic liquid presence detection."""
2218
+
2219
+ def __init__(self, instrument: InstrumentContext):
2220
+ self.instrument = instrument
2221
+
2222
+ def __enter__(self) -> None:
2223
+ if self.instrument.api_version >= APIVersion(2, 21):
2224
+ self.auto_presence = self.instrument.liquid_presence_detection
2225
+ self.instrument.liquid_presence_detection = False
2226
+
2227
+ def __exit__(self, *args: Any, **kwargs: Any) -> None:
2228
+ if self.instrument.api_version >= APIVersion(2, 21):
2229
+ self.instrument.liquid_presence_detection = self.auto_presence
2230
+
2199
2231
 
2200
2232
  def _raise_if_has_end_or_front_right_or_back_left(
2201
2233
  style: NozzleLayout,
@@ -30,7 +30,6 @@ from opentrons.hardware_control.nozzle_manager import NozzleMap
30
30
  # remove when their usage is no longer needed
31
31
  from opentrons.protocols.labware import ( # noqa: F401
32
32
  get_labware_definition as get_labware_definition,
33
- get_all_labware_definitions as get_all_labware_definitions,
34
33
  verify_definition as verify_definition,
35
34
  save_definition as save_definition,
36
35
  )
@@ -222,6 +221,19 @@ class Well:
222
221
  """
223
222
  return Location(self._core.get_center(), self)
224
223
 
224
+ @requires_version(2, 21)
225
+ def meniscus(self, z: float = 0.0) -> Location:
226
+ """
227
+ :param z: An offset on the z-axis, in mm. Positive offsets are higher and
228
+ negative offsets are lower.
229
+ :return: A :py:class:`~opentrons.types.Location` that indicates location is meniscus and that holds the ``z`` offset in its point.z field.
230
+
231
+ :meta private:
232
+ """
233
+ return Location(
234
+ point=Point(x=0, y=0, z=z), labware=self, _ot_internal_is_meniscus=True
235
+ )
236
+
225
237
  @requires_version(2, 8)
226
238
  def from_center_cartesian(self, x: float, y: float, z: float) -> Point:
227
239
  """
@@ -1,16 +1,16 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import List, Optional, Union, cast
4
+ from typing import List, Dict, Optional, Union, cast
5
5
 
6
+ from opentrons.protocol_engine.types import ABSMeasureMode
6
7
  from opentrons_shared_data.labware.types import LabwareDefinition
7
8
  from opentrons_shared_data.module.types import ModuleModel, ModuleType
8
9
 
9
10
  from opentrons.legacy_broker import LegacyBroker
10
- from opentrons.hardware_control.modules import ThermocyclerStep
11
11
  from opentrons.legacy_commands import module_commands as cmds
12
12
  from opentrons.legacy_commands.publisher import CommandPublisher, publish
13
- from opentrons.protocols.api_support.types import APIVersion
13
+ from opentrons.protocols.api_support.types import APIVersion, ThermocyclerStep
14
14
  from opentrons.protocols.api_support.util import (
15
15
  APIVersionError,
16
16
  requires_version,
@@ -628,6 +628,13 @@ class ThermocyclerContext(ModuleContext):
628
628
  ``hold_time_minutes`` and ``hold_time_seconds`` must be defined
629
629
  and for each step.
630
630
 
631
+ .. note:
632
+
633
+ Before API Version 2.21, Thermocycler profiles run with this command
634
+ would be listed in the app as having a number of repetitions equal to
635
+ their step count. At or above API Version 2.21, the structure of the
636
+ Thermocycler cycles is preserved.
637
+
631
638
  """
632
639
  repetitions = validation.ensure_thermocycler_repetition_count(repetitions)
633
640
  validated_steps = validation.ensure_thermocycler_profile_steps(steps)
@@ -976,23 +983,71 @@ class AbsorbanceReaderContext(ModuleContext):
976
983
  It should not be instantiated directly; instead, it should be
977
984
  created through :py:meth:`.ProtocolContext.load_module`.
978
985
 
979
- .. versionadded:: 2.18
986
+ .. versionadded:: 2.21
980
987
  """
981
988
 
982
989
  _core: AbsorbanceReaderCore
983
990
 
984
991
  @property
985
- @requires_version(2, 18)
992
+ @requires_version(2, 21)
986
993
  def serial_number(self) -> str:
987
994
  """Get the module's unique hardware serial number."""
988
995
  return self._core.get_serial_number()
989
996
 
990
- @requires_version(2, 18)
991
- def initialize(self, wavelength: int) -> None:
992
- """Initialize the Absorbance Reader by taking zero reading."""
993
- self._core.initialize(wavelength)
997
+ @requires_version(2, 21)
998
+ def close_lid(self) -> None:
999
+ """Close the lid of the Absorbance Reader."""
1000
+ self._core.close_lid()
1001
+
1002
+ @requires_version(2, 21)
1003
+ def open_lid(self) -> None:
1004
+ """Open the lid of the Absorbance Reader."""
1005
+ self._core.open_lid()
1006
+
1007
+ @requires_version(2, 21)
1008
+ def is_lid_on(self) -> bool:
1009
+ """Return ``True`` if the Absorbance Reader's lid is currently closed."""
1010
+ return self._core.is_lid_on()
1011
+
1012
+ @requires_version(2, 21)
1013
+ def initialize(
1014
+ self,
1015
+ mode: ABSMeasureMode,
1016
+ wavelengths: List[int],
1017
+ reference_wavelength: Optional[int] = None,
1018
+ ) -> None:
1019
+ """Take a zero reading on the Absorbance Plate Reader Module.
1020
+
1021
+ :param mode: Either ``"single"`` or ``"multi"``.
1022
+
1023
+ - In single measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1024
+ one sample wavelength and an optional reference wavelength.
1025
+ - In multiple measurement mode, :py:meth:`.AbsorbanceReaderContext.read` uses
1026
+ a list of up to six sample wavelengths.
1027
+ :param wavelengths: A list of wavelengths, in mm, to measure.
1028
+ - Must contain only one item when initializing a single measurement.
1029
+ - Must contain one to six items when initializing a multiple measurement.
1030
+ :param reference_wavelength: An optional reference wavelength, in mm. Cannot be
1031
+ used with multiple measurements.
1032
+ """
1033
+ self._core.initialize(
1034
+ mode, wavelengths, reference_wavelength=reference_wavelength
1035
+ )
1036
+
1037
+ @requires_version(2, 21)
1038
+ def read(
1039
+ self, export_filename: Optional[str] = None
1040
+ ) -> Dict[int, Dict[str, float]]:
1041
+ """Initiate read on the Absorbance Reader.
994
1042
 
995
- @requires_version(2, 18)
996
- def initiate_read(self) -> None:
997
- """Initiate read on the Absorbance Reader."""
998
- self._core.initiate_read()
1043
+ Returns a dictionary of wavelengths to dictionary of values ordered by well name.
1044
+
1045
+ :param export_filename: Optional, if a filename is provided a CSV file will be saved
1046
+ as a result of the read action containing measurement data. The filename will
1047
+ be modified to include the wavelength used during measurement. If multiple
1048
+ measurements are taken, then a file will be generated for each wavelength provided.
1049
+
1050
+ Example: If `export_filename="my_data"` and wavelengths 450 and 531 are used during
1051
+ measurement, the output files will be "my_data_450.csv" and "my_data_531.csv".
1052
+ """
1053
+ return self._core.read(filename=export_filename)
@@ -14,10 +14,15 @@ from typing import (
14
14
 
15
15
  from opentrons_shared_data.labware.types import LabwareDefinition
16
16
  from opentrons_shared_data.pipette.types import PipetteNameType
17
+ from opentrons_shared_data.robot.types import RobotTypeEnum
17
18
 
18
19
  from opentrons.types import Mount, Location, DeckLocation, DeckSlotName, StagingSlotName
20
+ from opentrons.config import feature_flags
19
21
  from opentrons.legacy_broker import LegacyBroker
20
- from opentrons.hardware_control.modules.types import MagneticBlockModel
22
+ from opentrons.hardware_control.modules.types import (
23
+ MagneticBlockModel,
24
+ AbsorbanceReaderModel,
25
+ )
21
26
  from opentrons.legacy_commands import protocol_commands as cmds, types as cmd_types
22
27
  from opentrons.legacy_commands.helpers import stringify_labware_movement_command
23
28
  from opentrons.legacy_commands.publisher import (
@@ -39,6 +44,7 @@ from opentrons.protocols.api_support.util import (
39
44
  RobotTypeError,
40
45
  UnsupportedAPIError,
41
46
  )
47
+ from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated
42
48
 
43
49
  from ._types import OffDeckType
44
50
  from .core.common import ModuleCore, LabwareCore, ProtocolCore
@@ -57,7 +63,7 @@ from .core.engine import ENGINE_CORE_API_VERSION
57
63
  from .core.legacy.legacy_protocol_core import LegacyProtocolCore
58
64
 
59
65
  from . import validation
60
- from ._liquid import Liquid
66
+ from ._liquid import Liquid, LiquidClass
61
67
  from .disposal_locations import TrashBin, WasteChute
62
68
  from .deck import Deck
63
69
  from .instrument_context import InstrumentContext
@@ -205,8 +211,12 @@ class ProtocolContext(CommandPublisher):
205
211
  return self._api_version
206
212
 
207
213
  @property
208
- @requires_version(2, 20)
214
+ @requires_version(2, 21)
209
215
  def robot(self) -> RobotContext:
216
+ """The :py:class:`.RobotContext` for the protocol.
217
+
218
+ :meta private:
219
+ """
210
220
  return self._robot
211
221
 
212
222
  @property
@@ -653,7 +663,6 @@ class ProtocolContext(CommandPublisher):
653
663
  if slot is not None
654
664
  }
655
665
 
656
- # TODO (spp, 2022-12-14): https://opentrons.atlassian.net/browse/RLAB-237
657
666
  @requires_version(2, 15)
658
667
  def move_labware(
659
668
  self,
@@ -704,6 +713,13 @@ class ProtocolContext(CommandPublisher):
704
713
  f"Expected labware of type 'Labware' but got {type(labware)}."
705
714
  )
706
715
 
716
+ # Ensure that when moving to an absorbance reader than the lid is open
717
+ if isinstance(new_location, AbsorbanceReaderContext):
718
+ if new_location.is_lid_on():
719
+ raise CommandPreconditionViolated(
720
+ f"Cannot move {labware.name} onto the Absorbance Reader Module when its lid is closed."
721
+ )
722
+
707
723
  location: Union[
708
724
  ModuleCore,
709
725
  LabwareCore,
@@ -827,6 +843,12 @@ class ProtocolContext(CommandPublisher):
827
843
  until_version="2.15",
828
844
  current_version=f"{self._api_version}",
829
845
  )
846
+ if isinstance(
847
+ requested_model, AbsorbanceReaderModel
848
+ ) and self._api_version < APIVersion(2, 21):
849
+ raise APIVersionError(
850
+ f"Module of type {module_name} is only available in versions 2.21 and above."
851
+ )
830
852
 
831
853
  deck_slot = (
832
854
  None
@@ -1264,6 +1286,18 @@ class ProtocolContext(CommandPublisher):
1264
1286
  display_color=display_color,
1265
1287
  )
1266
1288
 
1289
+ def define_liquid_class(
1290
+ self,
1291
+ name: str,
1292
+ ) -> LiquidClass:
1293
+ """Define a liquid class for use in the protocol."""
1294
+ if feature_flags.allow_liquid_classes(
1295
+ robot_type=RobotTypeEnum.robot_literal_to_enum(self._core.robot_type)
1296
+ ):
1297
+ return self._core.define_liquid_class(name=name)
1298
+ else:
1299
+ raise NotImplementedError("This method is not implemented.")
1300
+
1267
1301
  @property
1268
1302
  @requires_version(2, 5)
1269
1303
  def rail_lights_on(self) -> bool:
@@ -18,7 +18,7 @@ from opentrons_shared_data.labware.labware_definition import LabwareRole
18
18
  from opentrons_shared_data.pipette.types import PipetteNameType
19
19
  from opentrons_shared_data.robot.types import RobotType
20
20
 
21
- from opentrons.protocols.api_support.types import APIVersion
21
+ from opentrons.protocols.api_support.types import APIVersion, ThermocyclerStep
22
22
  from opentrons.protocols.api_support.util import APIVersionError
23
23
  from opentrons.protocols.models import LabwareDefinition
24
24
  from opentrons.types import Mount, DeckSlotName, StagingSlotName, Location
@@ -30,7 +30,6 @@ from opentrons.hardware_control.modules.types import (
30
30
  HeaterShakerModuleModel,
31
31
  MagneticBlockModel,
32
32
  AbsorbanceReaderModel,
33
- ThermocyclerStep,
34
33
  )
35
34
 
36
35
  from .disposal_locations import TrashBin, WasteChute
@@ -436,10 +435,13 @@ class LocationTypeError(TypeError):
436
435
  """Error representing that the location supplied is of different expected type."""
437
436
 
438
437
 
438
+ ValidTarget = Union[WellTarget, PointTarget, TrashBin, WasteChute]
439
+
440
+
439
441
  def validate_location(
440
442
  location: Union[Location, Well, TrashBin, WasteChute, None],
441
443
  last_location: Optional[Location],
442
- ) -> Union[WellTarget, PointTarget, TrashBin, WasteChute]:
444
+ ) -> ValidTarget:
443
445
  """Validate a given location for a liquid handling command.
444
446
 
445
447
  Args:
@@ -18,15 +18,10 @@ from .commands import (
18
18
  CommandType,
19
19
  CommandIntent,
20
20
  )
21
- from .state import (
22
- State,
23
- StateView,
24
- StateSummary,
25
- CommandSlice,
26
- CommandPointer,
27
- Config,
28
- CommandErrorSlice,
29
- )
21
+ from .state.state import State, StateView
22
+ from .state.state_summary import StateSummary
23
+ from .state.commands import CommandSlice, CommandErrorSlice, CommandPointer
24
+ from .state.config import Config
30
25
  from .plugins import AbstractPlugin
31
26
 
32
27
  from .types import (
@@ -52,9 +47,12 @@ from .types import (
52
47
  LoadedPipette,
53
48
  MotorAxis,
54
49
  WellLocation,
50
+ LiquidHandlingWellLocation,
51
+ PickUpTipWellLocation,
55
52
  DropTipWellLocation,
56
53
  WellOrigin,
57
54
  DropTipWellOrigin,
55
+ PickUpTipWellOrigin,
58
56
  WellOffset,
59
57
  ModuleModel,
60
58
  ModuleDefinition,
@@ -114,9 +112,12 @@ __all__ = [
114
112
  "LoadedPipette",
115
113
  "MotorAxis",
116
114
  "WellLocation",
115
+ "LiquidHandlingWellLocation",
116
+ "PickUpTipWellLocation",
117
117
  "DropTipWellLocation",
118
118
  "WellOrigin",
119
119
  "DropTipWellOrigin",
120
+ "PickUpTipWellOrigin",
120
121
  "WellOffset",
121
122
  "ModuleModel",
122
123
  "ModuleDefinition",
@@ -28,7 +28,9 @@ from .actions import (
28
28
  DoorChangeAction,
29
29
  ResetTipsAction,
30
30
  SetPipetteMovementSpeedAction,
31
+ AddAbsorbanceReaderLidAction,
31
32
  )
33
+ from .get_state_update import get_state_updates
32
34
 
33
35
  __all__ = [
34
36
  # action pipeline interface
@@ -56,7 +58,10 @@ __all__ = [
56
58
  "DoorChangeAction",
57
59
  "ResetTipsAction",
58
60
  "SetPipetteMovementSpeedAction",
61
+ "AddAbsorbanceReaderLidAction",
59
62
  # action payload values
60
63
  "PauseSource",
61
64
  "FinishErrorDetails",
65
+ # helper functions
66
+ "get_state_updates",
62
67
  ]