opentrons 8.1.0__py2.py3-none-any.whl → 8.2.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. opentrons/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 +208 -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/backends/ot3utils.py +1 -0
  20. opentrons/hardware_control/instruments/ot2/pipette_handler.py +22 -82
  21. opentrons/hardware_control/instruments/ot3/pipette_handler.py +10 -2
  22. opentrons/hardware_control/module_control.py +43 -2
  23. opentrons/hardware_control/modules/__init__.py +7 -1
  24. opentrons/hardware_control/modules/absorbance_reader.py +232 -83
  25. opentrons/hardware_control/modules/errors.py +7 -0
  26. opentrons/hardware_control/modules/heater_shaker.py +8 -3
  27. opentrons/hardware_control/modules/magdeck.py +12 -3
  28. opentrons/hardware_control/modules/mod_abc.py +27 -2
  29. opentrons/hardware_control/modules/tempdeck.py +15 -7
  30. opentrons/hardware_control/modules/thermocycler.py +69 -3
  31. opentrons/hardware_control/modules/types.py +11 -5
  32. opentrons/hardware_control/modules/update.py +11 -5
  33. opentrons/hardware_control/modules/utils.py +3 -1
  34. opentrons/hardware_control/ot3_calibration.py +6 -6
  35. opentrons/hardware_control/ot3api.py +131 -94
  36. opentrons/hardware_control/poller.py +15 -11
  37. opentrons/hardware_control/protocols/__init__.py +1 -7
  38. opentrons/hardware_control/protocols/instrument_configurer.py +14 -2
  39. opentrons/hardware_control/protocols/liquid_handler.py +5 -0
  40. opentrons/hardware_control/protocols/position_estimator.py +3 -1
  41. opentrons/hardware_control/types.py +2 -0
  42. opentrons/legacy_commands/helpers.py +8 -2
  43. opentrons/motion_planning/__init__.py +2 -0
  44. opentrons/motion_planning/waypoints.py +32 -0
  45. opentrons/protocol_api/__init__.py +2 -1
  46. opentrons/protocol_api/_liquid.py +87 -1
  47. opentrons/protocol_api/_parameter_context.py +10 -1
  48. opentrons/protocol_api/core/engine/deck_conflict.py +0 -297
  49. opentrons/protocol_api/core/engine/instrument.py +29 -25
  50. opentrons/protocol_api/core/engine/labware.py +20 -4
  51. opentrons/protocol_api/core/engine/module_core.py +166 -17
  52. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +362 -0
  53. opentrons/protocol_api/core/engine/protocol.py +30 -2
  54. opentrons/protocol_api/core/instrument.py +2 -0
  55. opentrons/protocol_api/core/labware.py +4 -0
  56. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +2 -0
  57. opentrons/protocol_api/core/legacy/legacy_labware_core.py +5 -0
  58. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +6 -2
  59. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +2 -0
  60. opentrons/protocol_api/core/module.py +22 -4
  61. opentrons/protocol_api/core/protocol.py +6 -2
  62. opentrons/protocol_api/instrument_context.py +52 -20
  63. opentrons/protocol_api/labware.py +13 -1
  64. opentrons/protocol_api/module_contexts.py +115 -17
  65. opentrons/protocol_api/protocol_context.py +49 -5
  66. opentrons/protocol_api/validation.py +5 -3
  67. opentrons/protocol_engine/__init__.py +10 -9
  68. opentrons/protocol_engine/actions/__init__.py +3 -0
  69. opentrons/protocol_engine/actions/actions.py +30 -25
  70. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  71. opentrons/protocol_engine/clients/sync_client.py +1 -1
  72. opentrons/protocol_engine/clients/transports.py +1 -1
  73. opentrons/protocol_engine/commands/__init__.py +0 -4
  74. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +41 -11
  75. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +148 -0
  76. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +65 -9
  77. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +148 -0
  78. opentrons/protocol_engine/commands/absorbance_reader/read.py +200 -0
  79. opentrons/protocol_engine/commands/aspirate.py +29 -16
  80. opentrons/protocol_engine/commands/aspirate_in_place.py +33 -16
  81. opentrons/protocol_engine/commands/blow_out.py +63 -14
  82. opentrons/protocol_engine/commands/blow_out_in_place.py +55 -13
  83. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +2 -5
  84. opentrons/protocol_engine/commands/calibration/calibrate_module.py +3 -4
  85. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +2 -5
  86. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +6 -4
  87. opentrons/protocol_engine/commands/command.py +31 -18
  88. opentrons/protocol_engine/commands/command_unions.py +37 -24
  89. opentrons/protocol_engine/commands/comment.py +5 -3
  90. opentrons/protocol_engine/commands/configure_for_volume.py +11 -14
  91. opentrons/protocol_engine/commands/configure_nozzle_layout.py +9 -15
  92. opentrons/protocol_engine/commands/custom.py +5 -3
  93. opentrons/protocol_engine/commands/dispense.py +42 -20
  94. opentrons/protocol_engine/commands/dispense_in_place.py +32 -14
  95. opentrons/protocol_engine/commands/drop_tip.py +70 -16
  96. opentrons/protocol_engine/commands/drop_tip_in_place.py +59 -13
  97. opentrons/protocol_engine/commands/get_tip_presence.py +5 -3
  98. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +6 -6
  99. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +6 -6
  100. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +6 -6
  101. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +8 -6
  102. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +8 -4
  103. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +6 -4
  104. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +6 -6
  105. opentrons/protocol_engine/commands/home.py +11 -5
  106. opentrons/protocol_engine/commands/liquid_probe.py +146 -88
  107. opentrons/protocol_engine/commands/load_labware.py +28 -5
  108. opentrons/protocol_engine/commands/load_liquid.py +18 -7
  109. opentrons/protocol_engine/commands/load_module.py +4 -6
  110. opentrons/protocol_engine/commands/load_pipette.py +18 -17
  111. opentrons/protocol_engine/commands/magnetic_module/disengage.py +6 -6
  112. opentrons/protocol_engine/commands/magnetic_module/engage.py +6 -4
  113. opentrons/protocol_engine/commands/move_labware.py +155 -23
  114. opentrons/protocol_engine/commands/move_relative.py +15 -3
  115. opentrons/protocol_engine/commands/move_to_addressable_area.py +29 -4
  116. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +13 -4
  117. opentrons/protocol_engine/commands/move_to_coordinates.py +11 -5
  118. opentrons/protocol_engine/commands/move_to_well.py +37 -10
  119. opentrons/protocol_engine/commands/pick_up_tip.py +51 -30
  120. opentrons/protocol_engine/commands/pipetting_common.py +47 -16
  121. opentrons/protocol_engine/commands/prepare_to_aspirate.py +62 -15
  122. opentrons/protocol_engine/commands/reload_labware.py +13 -4
  123. opentrons/protocol_engine/commands/retract_axis.py +6 -3
  124. opentrons/protocol_engine/commands/save_position.py +2 -3
  125. opentrons/protocol_engine/commands/set_rail_lights.py +5 -3
  126. opentrons/protocol_engine/commands/set_status_bar.py +5 -3
  127. opentrons/protocol_engine/commands/temperature_module/deactivate.py +6 -4
  128. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +3 -4
  129. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +6 -6
  130. opentrons/protocol_engine/commands/thermocycler/__init__.py +19 -0
  131. opentrons/protocol_engine/commands/thermocycler/close_lid.py +8 -8
  132. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +6 -4
  133. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +6 -4
  134. opentrons/protocol_engine/commands/thermocycler/open_lid.py +8 -4
  135. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +165 -0
  136. opentrons/protocol_engine/commands/thermocycler/run_profile.py +6 -6
  137. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +3 -4
  138. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +3 -4
  139. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +6 -4
  140. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +6 -4
  141. opentrons/protocol_engine/commands/touch_tip.py +19 -7
  142. opentrons/protocol_engine/commands/unsafe/__init__.py +30 -0
  143. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +6 -4
  144. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +12 -4
  145. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +5 -3
  146. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  147. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  148. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +10 -4
  149. opentrons/protocol_engine/commands/verify_tip_presence.py +5 -5
  150. opentrons/protocol_engine/commands/wait_for_duration.py +5 -3
  151. opentrons/protocol_engine/commands/wait_for_resume.py +5 -3
  152. opentrons/protocol_engine/create_protocol_engine.py +60 -10
  153. opentrons/protocol_engine/engine_support.py +2 -1
  154. opentrons/protocol_engine/error_recovery_policy.py +14 -3
  155. opentrons/protocol_engine/errors/__init__.py +20 -0
  156. opentrons/protocol_engine/errors/error_occurrence.py +8 -3
  157. opentrons/protocol_engine/errors/exceptions.py +127 -2
  158. opentrons/protocol_engine/execution/__init__.py +2 -0
  159. opentrons/protocol_engine/execution/command_executor.py +22 -13
  160. opentrons/protocol_engine/execution/create_queue_worker.py +5 -1
  161. opentrons/protocol_engine/execution/door_watcher.py +1 -1
  162. opentrons/protocol_engine/execution/equipment.py +2 -1
  163. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  164. opentrons/protocol_engine/execution/gantry_mover.py +4 -2
  165. opentrons/protocol_engine/execution/hardware_stopper.py +3 -3
  166. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +1 -4
  167. opentrons/protocol_engine/execution/labware_movement.py +73 -22
  168. opentrons/protocol_engine/execution/movement.py +17 -7
  169. opentrons/protocol_engine/execution/pipetting.py +7 -4
  170. opentrons/protocol_engine/execution/queue_worker.py +6 -2
  171. opentrons/protocol_engine/execution/run_control.py +1 -1
  172. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +1 -1
  173. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +2 -1
  174. opentrons/protocol_engine/execution/tip_handler.py +77 -43
  175. opentrons/protocol_engine/notes/__init__.py +14 -2
  176. opentrons/protocol_engine/notes/notes.py +18 -1
  177. opentrons/protocol_engine/plugins.py +1 -1
  178. opentrons/protocol_engine/protocol_engine.py +47 -31
  179. opentrons/protocol_engine/resources/__init__.py +2 -0
  180. opentrons/protocol_engine/resources/deck_data_provider.py +19 -5
  181. opentrons/protocol_engine/resources/file_provider.py +161 -0
  182. opentrons/protocol_engine/resources/fixture_validation.py +11 -1
  183. opentrons/protocol_engine/resources/labware_validation.py +10 -0
  184. opentrons/protocol_engine/state/__init__.py +0 -70
  185. opentrons/protocol_engine/state/addressable_areas.py +1 -1
  186. opentrons/protocol_engine/state/command_history.py +21 -2
  187. opentrons/protocol_engine/state/commands.py +110 -31
  188. opentrons/protocol_engine/state/files.py +59 -0
  189. opentrons/protocol_engine/state/frustum_helpers.py +440 -0
  190. opentrons/protocol_engine/state/geometry.py +445 -59
  191. opentrons/protocol_engine/state/labware.py +264 -84
  192. opentrons/protocol_engine/state/liquids.py +1 -1
  193. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +21 -3
  194. opentrons/protocol_engine/state/modules.py +145 -90
  195. opentrons/protocol_engine/state/motion.py +33 -14
  196. opentrons/protocol_engine/state/pipettes.py +157 -317
  197. opentrons/protocol_engine/state/state.py +30 -1
  198. opentrons/protocol_engine/state/state_summary.py +3 -0
  199. opentrons/protocol_engine/state/tips.py +69 -114
  200. opentrons/protocol_engine/state/update_types.py +424 -0
  201. opentrons/protocol_engine/state/wells.py +236 -0
  202. opentrons/protocol_engine/types.py +90 -0
  203. opentrons/protocol_reader/file_format_validator.py +83 -15
  204. opentrons/protocol_runner/json_translator.py +21 -5
  205. opentrons/protocol_runner/legacy_command_mapper.py +27 -6
  206. opentrons/protocol_runner/legacy_context_plugin.py +27 -71
  207. opentrons/protocol_runner/protocol_runner.py +6 -3
  208. opentrons/protocol_runner/run_orchestrator.py +41 -6
  209. opentrons/protocols/advanced_control/mix.py +3 -5
  210. opentrons/protocols/advanced_control/transfers.py +125 -56
  211. opentrons/protocols/api_support/constants.py +1 -1
  212. opentrons/protocols/api_support/definitions.py +1 -1
  213. opentrons/protocols/api_support/labware_like.py +4 -4
  214. opentrons/protocols/api_support/tip_tracker.py +2 -2
  215. opentrons/protocols/api_support/types.py +15 -2
  216. opentrons/protocols/api_support/util.py +30 -42
  217. opentrons/protocols/duration/errors.py +1 -1
  218. opentrons/protocols/duration/estimator.py +50 -29
  219. opentrons/protocols/execution/dev_types.py +2 -2
  220. opentrons/protocols/execution/execute_json_v4.py +15 -10
  221. opentrons/protocols/execution/execute_python.py +8 -3
  222. opentrons/protocols/geometry/planning.py +12 -12
  223. opentrons/protocols/labware.py +17 -33
  224. opentrons/protocols/parameters/csv_parameter_interface.py +3 -1
  225. opentrons/simulate.py +3 -3
  226. opentrons/types.py +30 -3
  227. opentrons/util/logging_config.py +34 -0
  228. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/METADATA +5 -4
  229. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/RECORD +235 -223
  230. opentrons/protocol_engine/commands/absorbance_reader/measure.py +0 -94
  231. opentrons/protocol_engine/commands/configuring_common.py +0 -26
  232. opentrons/protocol_runner/thread_async_queue.py +0 -174
  233. /opentrons/protocol_engine/state/{abstract_store.py → _abstract_store.py} +0 -0
  234. /opentrons/protocol_engine/state/{move_types.py → _move_types.py} +0 -0
  235. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/LICENSE +0 -0
  236. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/WHEEL +0 -0
  237. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/entry_points.txt +0 -0
  238. {opentrons-8.1.0.dist-info → opentrons-8.2.0.dist-info}/top_level.txt +0 -0
@@ -2,9 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from typing import Mapping, Optional
5
+ from typing import Dict, Optional
6
6
 
7
- from opentrons.hardware_control.modules.types import TemperatureStatus
7
+ from opentrons.hardware_control.modules.types import (
8
+ ModuleDisconnectedCallback,
9
+ TemperatureStatus,
10
+ )
8
11
  from opentrons.hardware_control.poller import Reader, Poller
9
12
  from typing_extensions import Final
10
13
  from opentrons.drivers.types import Temperature
@@ -15,7 +18,7 @@ from opentrons.drivers.temp_deck import (
15
18
  )
16
19
  from opentrons.drivers.rpi_drivers.types import USBPort
17
20
  from opentrons.hardware_control.execution_manager import ExecutionManager
18
- from opentrons.hardware_control.modules import update, mod_abc, types
21
+ from opentrons.hardware_control.modules import update, mod_abc, types, errors
19
22
 
20
23
  log = logging.getLogger(__name__)
21
24
 
@@ -26,7 +29,7 @@ SIM_TEMP_POLL_INTERVAL_SECS = TEMP_POLL_INTERVAL_SECS / 20.0
26
29
  class TempDeck(mod_abc.AbstractModule):
27
30
  """Hardware control interface for an attached Temperature Module."""
28
31
 
29
- MODULE_TYPE = types.ModuleType.TEMPERATURE
32
+ MODULE_TYPE = types.ModuleType["TEMPERATURE"]
30
33
  FIRST_GEN2_REVISION = 20
31
34
 
32
35
  @classmethod
@@ -40,6 +43,7 @@ class TempDeck(mod_abc.AbstractModule):
40
43
  simulating: bool = False,
41
44
  sim_model: Optional[str] = None,
42
45
  sim_serial_number: Optional[str] = None,
46
+ disconnected_callback: ModuleDisconnectedCallback = None,
43
47
  ) -> "TempDeck":
44
48
  """
45
49
  Build a TempDeck
@@ -52,6 +56,7 @@ class TempDeck(mod_abc.AbstractModule):
52
56
  poll_interval_seconds: Poll interval override.
53
57
  simulating: whether to build a simulating driver
54
58
  sim_model: The model name used by simulator
59
+ disconnected_callback: Callback to inform the module controller that the device was disconnected
55
60
 
56
61
  Returns:
57
62
  Tempdeck instance
@@ -77,6 +82,7 @@ class TempDeck(mod_abc.AbstractModule):
77
82
  poller=poller,
78
83
  device_info=await driver.get_device_info(),
79
84
  hw_control_loop=hw_control_loop,
85
+ disconnected_callback=disconnected_callback,
80
86
  )
81
87
 
82
88
  try:
@@ -94,8 +100,9 @@ class TempDeck(mod_abc.AbstractModule):
94
100
  driver: AbstractTempDeckDriver,
95
101
  reader: TempDeckReader,
96
102
  poller: Poller,
97
- device_info: Mapping[str, str],
103
+ device_info: Dict[str, str],
98
104
  hw_control_loop: asyncio.AbstractEventLoop,
105
+ disconnected_callback: ModuleDisconnectedCallback = None,
99
106
  ) -> None:
100
107
  """Constructor"""
101
108
  super().__init__(
@@ -103,6 +110,7 @@ class TempDeck(mod_abc.AbstractModule):
103
110
  usb_port=usb_port,
104
111
  hw_control_loop=hw_control_loop,
105
112
  execution_manager=execution_manager,
113
+ disconnected_callback=disconnected_callback,
106
114
  )
107
115
  self._device_info = device_info
108
116
  self._driver = driver
@@ -182,7 +190,7 @@ class TempDeck(mod_abc.AbstractModule):
182
190
  await self._reader.read()
183
191
 
184
192
  @property
185
- def device_info(self) -> Mapping[str, str]:
193
+ def device_info(self) -> Dict[str, str]:
186
194
  return self._device_info
187
195
 
188
196
  @property
@@ -211,7 +219,7 @@ class TempDeck(mod_abc.AbstractModule):
211
219
  async def prep_for_update(self) -> str:
212
220
  model = self._device_info and self._device_info.get("model")
213
221
  if model in ("temp_deck_v1", "temp_deck_v1.1", "temp_deck_v2"):
214
- raise types.UpdateError(
222
+ raise errors.UpdateError(
215
223
  "This Temperature Module can't be updated."
216
224
  "Please contact Opentrons Support."
217
225
  )
@@ -2,12 +2,15 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from typing import Callable, Optional, List, Dict, Mapping
5
+ from typing import Callable, Optional, List, Dict, Mapping, Union, cast
6
6
  from opentrons.drivers.rpi_drivers.types import USBPort
7
7
  from opentrons.drivers.types import ThermocyclerLidStatus, Temperature, PlateTemperature
8
8
  from opentrons.hardware_control.modules.lid_temp_status import LidTemperatureStatus
9
9
  from opentrons.hardware_control.modules.plate_temp_status import PlateTemperatureStatus
10
- from opentrons.hardware_control.modules.types import TemperatureStatus
10
+ from opentrons.hardware_control.modules.types import (
11
+ ModuleDisconnectedCallback,
12
+ TemperatureStatus,
13
+ )
11
14
  from opentrons.hardware_control.poller import Reader, Poller
12
15
 
13
16
  from ..execution_manager import ExecutionManager
@@ -64,6 +67,7 @@ class Thermocycler(mod_abc.AbstractModule):
64
67
  simulating: bool = False,
65
68
  sim_model: Optional[str] = None,
66
69
  sim_serial_number: Optional[str] = None,
70
+ disconnected_callback: ModuleDisconnectedCallback = None,
67
71
  ) -> "Thermocycler":
68
72
  """
69
73
  Build and connect to a Thermocycler
@@ -77,6 +81,7 @@ class Thermocycler(mod_abc.AbstractModule):
77
81
  simulating: whether to build a simulating driver
78
82
  loop: Loop
79
83
  sim_model: The model name used by simulator
84
+ disconnected_callback: Callback to inform the module controller that the device was disconnected
80
85
 
81
86
  Returns:
82
87
  Thermocycler instance.
@@ -102,6 +107,7 @@ class Thermocycler(mod_abc.AbstractModule):
102
107
  device_info=await driver.get_device_info(),
103
108
  hw_control_loop=hw_control_loop,
104
109
  execution_manager=execution_manager,
110
+ disconnected_callback=disconnected_callback,
105
111
  )
106
112
 
107
113
  try:
@@ -121,6 +127,7 @@ class Thermocycler(mod_abc.AbstractModule):
121
127
  poller: Poller,
122
128
  device_info: Dict[str, str],
123
129
  hw_control_loop: asyncio.AbstractEventLoop,
130
+ disconnected_callback: ModuleDisconnectedCallback = None,
124
131
  ) -> None:
125
132
  """
126
133
  Constructor
@@ -134,6 +141,7 @@ class Thermocycler(mod_abc.AbstractModule):
134
141
  poller: A poll controller for reads.
135
142
  device_info: The thermocycler device info.
136
143
  hw_control_loop: The event loop running in the hardware control thread.
144
+ disconnected_callback: Callback to inform the module controller that the device was disconnected
137
145
  """
138
146
  self._driver = driver
139
147
  super().__init__(
@@ -141,6 +149,7 @@ class Thermocycler(mod_abc.AbstractModule):
141
149
  usb_port=usb_port,
142
150
  hw_control_loop=hw_control_loop,
143
151
  execution_manager=execution_manager,
152
+ disconnected_callback=disconnected_callback,
144
153
  )
145
154
  self._device_info = device_info
146
155
  self._reader = reader
@@ -354,6 +363,39 @@ class Thermocycler(mod_abc.AbstractModule):
354
363
  self.make_cancellable(task)
355
364
  await task
356
365
 
366
+ async def execute_profile(
367
+ self,
368
+ profile: List[Union[types.ThermocyclerCycle, types.ThermocyclerStep]],
369
+ volume: Optional[float] = None,
370
+ ) -> None:
371
+ """Begin a set temperature profile, with both repeating and non-repeating steps.
372
+
373
+ Args:
374
+ profile: The temperature profile to follow.
375
+ volume: Optional volume
376
+
377
+ Returns: None
378
+ """
379
+ await self.wait_for_is_running()
380
+ self._total_cycle_count = 0
381
+ self._total_step_count = 0
382
+ self._current_cycle_index = 0
383
+ self._current_step_index = 0
384
+ for step_or_cycle in profile:
385
+ if "steps" in step_or_cycle:
386
+ # basically https://github.com/python/mypy/issues/14766
387
+ this_cycle = cast(types.ThermocyclerCycle, step_or_cycle)
388
+ self._total_cycle_count += this_cycle["repetitions"]
389
+ self._total_step_count += (
390
+ len(this_cycle["steps"]) * this_cycle["repetitions"]
391
+ )
392
+ else:
393
+ self._total_step_count += 1
394
+ self._total_cycle_count += 1
395
+ task = self._loop.create_task(self._execute_profile(profile, volume))
396
+ self.make_cancellable(task)
397
+ await task
398
+
357
399
  async def set_lid_temperature(self, temperature: float) -> None:
358
400
  """Set the lid temperature in degrees Celsius"""
359
401
  await self.wait_for_is_running()
@@ -565,7 +607,7 @@ class Thermocycler(mod_abc.AbstractModule):
565
607
  self,
566
608
  steps: List[types.ThermocyclerStep],
567
609
  repetitions: int,
568
- volume: Optional[float] = None,
610
+ volume: Optional[float],
569
611
  ) -> None:
570
612
  """
571
613
  Execute cycles.
@@ -583,6 +625,30 @@ class Thermocycler(mod_abc.AbstractModule):
583
625
  self._current_step_index = step_idx + 1 # science starts at 1
584
626
  await self._execute_cycle_step(step, volume)
585
627
 
628
+ async def _execute_profile(
629
+ self,
630
+ profile: List[Union[types.ThermocyclerCycle, types.ThermocyclerStep]],
631
+ volume: Optional[float],
632
+ ) -> None:
633
+ """
634
+ Execute profiles.
635
+
636
+ Profiles command a thermocycler pattern that can contain multiple cycles and out-of-cycle steps.
637
+ """
638
+ self._current_cycle_index = 0
639
+ self._current_step_index = 0
640
+ for step_or_cycle in profile:
641
+ self._current_cycle_index += 1
642
+ if "repetitions" in step_or_cycle:
643
+ # basically https://github.com/python/mypy/issues/14766
644
+ this_cycle = cast(types.ThermocyclerCycle, step_or_cycle)
645
+ for rep in range(this_cycle["repetitions"]):
646
+ for step in this_cycle["steps"]:
647
+ self._current_step_index += 1
648
+ await self._execute_cycle_step(step, volume)
649
+ else:
650
+ await self._execute_cycle_step(step_or_cycle, volume)
651
+
586
652
  # TODO(mc, 2022-10-13): why does this exist?
587
653
  # Do the driver and poller really need to be disconnected?
588
654
  # Could we accomplish the same thing by latching the error state
@@ -3,6 +3,7 @@ from enum import Enum
3
3
  from dataclasses import dataclass
4
4
  from typing import (
5
5
  Dict,
6
+ List,
6
7
  NamedTuple,
7
8
  Callable,
8
9
  Any,
@@ -38,12 +39,20 @@ class ThermocyclerStep(ThermocyclerStepBase, total=False):
38
39
  hold_time_minutes: float
39
40
 
40
41
 
42
+ class ThermocyclerCycle(TypedDict):
43
+ steps: List[ThermocyclerStep]
44
+ repetitions: int
45
+
46
+
41
47
  UploadFunction = Callable[[str, str, Dict[str, Any]], Awaitable[Tuple[bool, str]]]
42
48
 
43
49
 
50
+ ModuleDisconnectedCallback = Optional[Callable[[str, str | None], None]]
51
+
52
+
44
53
  class LiveData(TypedDict):
45
54
  status: str
46
- data: Dict[str, Union[float, str, bool, None]]
55
+ data: Dict[str, Union[float, str, bool, List[int], None]]
47
56
 
48
57
 
49
58
  class ModuleType(str, Enum):
@@ -135,6 +144,7 @@ def module_model_from_string(model_string: str) -> ModuleModel:
135
144
  class ModuleAtPort:
136
145
  port: str
137
146
  name: str
147
+ serial: Optional[str] = None
138
148
  usb_port: USBPort = USBPort(name="", port_number=0)
139
149
 
140
150
 
@@ -159,10 +169,6 @@ class BundledFirmware(NamedTuple):
159
169
  return f"<BundledFirmware {self.version}, path={self.path}>"
160
170
 
161
171
 
162
- class UpdateError(RuntimeError):
163
- pass
164
-
165
-
166
172
  class ModuleInfo(NamedTuple):
167
173
  model: str # A module model such as "magneticModuleV2"
168
174
  fw_version: str # The version of the firmware
@@ -4,7 +4,8 @@ import os
4
4
  from pathlib import Path
5
5
  from glob import glob
6
6
  from typing import Any, AsyncGenerator, Dict, Tuple, Union
7
- from .types import UpdateError
7
+
8
+ from .errors import UpdateError
8
9
  from .mod_abc import AbstractModule
9
10
  from opentrons.hardware_control.threaded_async_lock import ThreadedAsyncLock
10
11
  from contextlib import asynccontextmanager
@@ -33,6 +34,7 @@ async def update_firmware(
33
34
  kwargs: Dict[str, Any] = {
34
35
  "stdout": asyncio.subprocess.PIPE,
35
36
  "stderr": asyncio.subprocess.PIPE,
37
+ "module": module,
36
38
  }
37
39
  successful, res = await module.bootloader()(
38
40
  flash_port_or_dfu_serial, str(firmware_file), kwargs
@@ -139,7 +141,8 @@ async def upload_via_avrdude(
139
141
  "-b{}".format(BAUDRATE),
140
142
  "-D",
141
143
  "-Uflash:w:{}:i".format(firmware_file_path),
142
- **kwargs,
144
+ stdout=kwargs["stdout"],
145
+ stderr=kwargs["stderr"],
143
146
  )
144
147
  await proc.wait()
145
148
 
@@ -191,8 +194,9 @@ async def upload_via_bossa(
191
194
  "--offset=0x2000",
192
195
  f"{firmware_file_path}",
193
196
  ]
194
-
195
- proc = await asyncio.create_subprocess_exec(*bossa_args, **kwargs)
197
+ proc = await asyncio.create_subprocess_exec(
198
+ *bossa_args, stdout=kwargs["stdout"], stderr=kwargs["stderr"]
199
+ )
196
200
  stdout, stderr = await proc.communicate()
197
201
  res = stdout.decode()
198
202
  if "Verify successful" in res:
@@ -231,7 +235,9 @@ async def upload_via_dfu(
231
235
  f"-D{firmware_file_path}",
232
236
  "-R",
233
237
  ]
234
- proc = await asyncio.create_subprocess_exec(*dfu_args, **kwargs)
238
+ proc = await asyncio.create_subprocess_exec(
239
+ *dfu_args, stdout=kwargs["stdout"], stderr=kwargs["stderr"]
240
+ )
235
241
  stdout, stderr = await proc.communicate()
236
242
  res = stdout.decode()
237
243
 
@@ -6,7 +6,7 @@ from opentrons.drivers.rpi_drivers.types import USBPort
6
6
 
7
7
  from ..execution_manager import ExecutionManager
8
8
 
9
- from .types import ModuleType, SpeedStatus
9
+ from .types import ModuleDisconnectedCallback, ModuleType, SpeedStatus
10
10
  from .mod_abc import AbstractModule
11
11
  from .tempdeck import TempDeck
12
12
  from .magdeck import MagDeck
@@ -46,6 +46,7 @@ async def build(
46
46
  execution_manager: ExecutionManager,
47
47
  sim_model: Optional[str] = None,
48
48
  sim_serial_number: Optional[str] = None,
49
+ disconnected_callback: ModuleDisconnectedCallback = None,
49
50
  ) -> AbstractModule:
50
51
  return await _MODULE_CLS_BY_TYPE[type].build(
51
52
  port=port,
@@ -55,6 +56,7 @@ async def build(
55
56
  execution_manager=execution_manager,
56
57
  sim_model=sim_model,
57
58
  sim_serial_number=sim_serial_number,
59
+ disconnected_callback=disconnected_callback,
58
60
  )
59
61
 
60
62
 
@@ -819,13 +819,13 @@ async def find_pipette_offset(
819
819
  try:
820
820
  if reset_instrument_offset:
821
821
  await hcapi.reset_instrument_offset(mount)
822
- await hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
822
+ hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
823
823
  offset = await _calibrate_mount(
824
824
  hcapi, mount, slot, method, raise_verify_error, probe=probe
825
825
  )
826
826
  return offset
827
827
  finally:
828
- await hcapi.remove_tip(mount)
828
+ hcapi.remove_tip(mount)
829
829
 
830
830
 
831
831
  async def calibrate_pipette(
@@ -877,7 +877,7 @@ async def calibrate_module(
877
877
  if mount == OT3Mount.GRIPPER:
878
878
  hcapi.add_gripper_probe(GripperProbe.FRONT)
879
879
  else:
880
- await hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
880
+ hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
881
881
 
882
882
  LOG.info(
883
883
  f"Starting module calibration for {module_id} at {nominal_position} using {mount}"
@@ -903,7 +903,7 @@ async def calibrate_module(
903
903
  hcapi.remove_gripper_probe()
904
904
  await hcapi.ungrip()
905
905
  else:
906
- await hcapi.remove_tip(mount)
906
+ hcapi.remove_tip(mount)
907
907
 
908
908
 
909
909
  async def calibrate_belts(
@@ -927,7 +927,7 @@ async def calibrate_belts(
927
927
  raise RuntimeError("Must use pipette mount, not gripper")
928
928
  try:
929
929
  hcapi.reset_deck_calibration()
930
- await hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
930
+ hcapi.add_tip(mount, hcapi.config.calibration.probe_length)
931
931
  belt_attitude, alignment_details = await _determine_transform_matrix(
932
932
  hcapi, mount
933
933
  )
@@ -935,7 +935,7 @@ async def calibrate_belts(
935
935
  return belt_attitude, alignment_details
936
936
  finally:
937
937
  hcapi.load_deck_calibration()
938
- await hcapi.remove_tip(mount)
938
+ hcapi.remove_tip(mount)
939
939
 
940
940
 
941
941
  def apply_machine_transform(