opentrons 8.6.0a1__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 (600) hide show
  1. opentrons/__init__.py +150 -0
  2. opentrons/_version.py +34 -0
  3. opentrons/calibration_storage/__init__.py +54 -0
  4. opentrons/calibration_storage/deck_configuration.py +62 -0
  5. opentrons/calibration_storage/encoder_decoder.py +31 -0
  6. opentrons/calibration_storage/file_operators.py +142 -0
  7. opentrons/calibration_storage/helpers.py +103 -0
  8. opentrons/calibration_storage/ot2/__init__.py +34 -0
  9. opentrons/calibration_storage/ot2/deck_attitude.py +85 -0
  10. opentrons/calibration_storage/ot2/mark_bad_calibration.py +27 -0
  11. opentrons/calibration_storage/ot2/models/__init__.py +0 -0
  12. opentrons/calibration_storage/ot2/models/v1.py +149 -0
  13. opentrons/calibration_storage/ot2/pipette_offset.py +129 -0
  14. opentrons/calibration_storage/ot2/tip_length.py +281 -0
  15. opentrons/calibration_storage/ot3/__init__.py +31 -0
  16. opentrons/calibration_storage/ot3/deck_attitude.py +83 -0
  17. opentrons/calibration_storage/ot3/gripper_offset.py +156 -0
  18. opentrons/calibration_storage/ot3/models/__init__.py +0 -0
  19. opentrons/calibration_storage/ot3/models/v1.py +122 -0
  20. opentrons/calibration_storage/ot3/module_offset.py +138 -0
  21. opentrons/calibration_storage/ot3/pipette_offset.py +95 -0
  22. opentrons/calibration_storage/types.py +45 -0
  23. opentrons/cli/__init__.py +21 -0
  24. opentrons/cli/__main__.py +5 -0
  25. opentrons/cli/analyze.py +501 -0
  26. opentrons/config/__init__.py +631 -0
  27. opentrons/config/advanced_settings.py +871 -0
  28. opentrons/config/defaults_ot2.py +214 -0
  29. opentrons/config/defaults_ot3.py +499 -0
  30. opentrons/config/feature_flags.py +86 -0
  31. opentrons/config/gripper_config.py +55 -0
  32. opentrons/config/reset.py +203 -0
  33. opentrons/config/robot_configs.py +187 -0
  34. opentrons/config/types.py +183 -0
  35. opentrons/drivers/__init__.py +0 -0
  36. opentrons/drivers/absorbance_reader/__init__.py +11 -0
  37. opentrons/drivers/absorbance_reader/abstract.py +72 -0
  38. opentrons/drivers/absorbance_reader/async_byonoy.py +352 -0
  39. opentrons/drivers/absorbance_reader/driver.py +81 -0
  40. opentrons/drivers/absorbance_reader/hid_protocol.py +161 -0
  41. opentrons/drivers/absorbance_reader/simulator.py +84 -0
  42. opentrons/drivers/asyncio/__init__.py +0 -0
  43. opentrons/drivers/asyncio/communication/__init__.py +22 -0
  44. opentrons/drivers/asyncio/communication/async_serial.py +183 -0
  45. opentrons/drivers/asyncio/communication/errors.py +88 -0
  46. opentrons/drivers/asyncio/communication/serial_connection.py +552 -0
  47. opentrons/drivers/command_builder.py +102 -0
  48. opentrons/drivers/flex_stacker/__init__.py +13 -0
  49. opentrons/drivers/flex_stacker/abstract.py +214 -0
  50. opentrons/drivers/flex_stacker/driver.py +768 -0
  51. opentrons/drivers/flex_stacker/errors.py +68 -0
  52. opentrons/drivers/flex_stacker/simulator.py +309 -0
  53. opentrons/drivers/flex_stacker/types.py +367 -0
  54. opentrons/drivers/flex_stacker/utils.py +19 -0
  55. opentrons/drivers/heater_shaker/__init__.py +5 -0
  56. opentrons/drivers/heater_shaker/abstract.py +76 -0
  57. opentrons/drivers/heater_shaker/driver.py +204 -0
  58. opentrons/drivers/heater_shaker/simulator.py +94 -0
  59. opentrons/drivers/mag_deck/__init__.py +6 -0
  60. opentrons/drivers/mag_deck/abstract.py +44 -0
  61. opentrons/drivers/mag_deck/driver.py +208 -0
  62. opentrons/drivers/mag_deck/simulator.py +63 -0
  63. opentrons/drivers/rpi_drivers/__init__.py +33 -0
  64. opentrons/drivers/rpi_drivers/dev_types.py +94 -0
  65. opentrons/drivers/rpi_drivers/gpio.py +282 -0
  66. opentrons/drivers/rpi_drivers/gpio_simulator.py +127 -0
  67. opentrons/drivers/rpi_drivers/interfaces.py +15 -0
  68. opentrons/drivers/rpi_drivers/types.py +364 -0
  69. opentrons/drivers/rpi_drivers/usb.py +102 -0
  70. opentrons/drivers/rpi_drivers/usb_simulator.py +22 -0
  71. opentrons/drivers/serial_communication.py +151 -0
  72. opentrons/drivers/smoothie_drivers/__init__.py +4 -0
  73. opentrons/drivers/smoothie_drivers/connection.py +51 -0
  74. opentrons/drivers/smoothie_drivers/constants.py +121 -0
  75. opentrons/drivers/smoothie_drivers/driver_3_0.py +1933 -0
  76. opentrons/drivers/smoothie_drivers/errors.py +49 -0
  77. opentrons/drivers/smoothie_drivers/parse_utils.py +143 -0
  78. opentrons/drivers/smoothie_drivers/simulator.py +99 -0
  79. opentrons/drivers/smoothie_drivers/types.py +16 -0
  80. opentrons/drivers/temp_deck/__init__.py +10 -0
  81. opentrons/drivers/temp_deck/abstract.py +54 -0
  82. opentrons/drivers/temp_deck/driver.py +197 -0
  83. opentrons/drivers/temp_deck/simulator.py +57 -0
  84. opentrons/drivers/thermocycler/__init__.py +12 -0
  85. opentrons/drivers/thermocycler/abstract.py +99 -0
  86. opentrons/drivers/thermocycler/driver.py +395 -0
  87. opentrons/drivers/thermocycler/simulator.py +126 -0
  88. opentrons/drivers/types.py +107 -0
  89. opentrons/drivers/utils.py +222 -0
  90. opentrons/execute.py +742 -0
  91. opentrons/hardware_control/__init__.py +65 -0
  92. opentrons/hardware_control/__main__.py +77 -0
  93. opentrons/hardware_control/adapters.py +98 -0
  94. opentrons/hardware_control/api.py +1347 -0
  95. opentrons/hardware_control/backends/__init__.py +7 -0
  96. opentrons/hardware_control/backends/controller.py +400 -0
  97. opentrons/hardware_control/backends/errors.py +9 -0
  98. opentrons/hardware_control/backends/estop_state.py +164 -0
  99. opentrons/hardware_control/backends/flex_protocol.py +497 -0
  100. opentrons/hardware_control/backends/ot3controller.py +1930 -0
  101. opentrons/hardware_control/backends/ot3simulator.py +900 -0
  102. opentrons/hardware_control/backends/ot3utils.py +664 -0
  103. opentrons/hardware_control/backends/simulator.py +442 -0
  104. opentrons/hardware_control/backends/status_bar_state.py +240 -0
  105. opentrons/hardware_control/backends/subsystem_manager.py +431 -0
  106. opentrons/hardware_control/backends/tip_presence_manager.py +173 -0
  107. opentrons/hardware_control/backends/types.py +14 -0
  108. opentrons/hardware_control/constants.py +6 -0
  109. opentrons/hardware_control/dev_types.py +125 -0
  110. opentrons/hardware_control/emulation/__init__.py +0 -0
  111. opentrons/hardware_control/emulation/abstract_emulator.py +21 -0
  112. opentrons/hardware_control/emulation/app.py +56 -0
  113. opentrons/hardware_control/emulation/connection_handler.py +38 -0
  114. opentrons/hardware_control/emulation/heater_shaker.py +150 -0
  115. opentrons/hardware_control/emulation/magdeck.py +60 -0
  116. opentrons/hardware_control/emulation/module_server/__init__.py +8 -0
  117. opentrons/hardware_control/emulation/module_server/client.py +78 -0
  118. opentrons/hardware_control/emulation/module_server/helpers.py +130 -0
  119. opentrons/hardware_control/emulation/module_server/models.py +31 -0
  120. opentrons/hardware_control/emulation/module_server/server.py +110 -0
  121. opentrons/hardware_control/emulation/parser.py +74 -0
  122. opentrons/hardware_control/emulation/proxy.py +241 -0
  123. opentrons/hardware_control/emulation/run_emulator.py +68 -0
  124. opentrons/hardware_control/emulation/scripts/__init__.py +0 -0
  125. opentrons/hardware_control/emulation/scripts/run_app.py +54 -0
  126. opentrons/hardware_control/emulation/scripts/run_module_emulator.py +72 -0
  127. opentrons/hardware_control/emulation/scripts/run_smoothie.py +37 -0
  128. opentrons/hardware_control/emulation/settings.py +119 -0
  129. opentrons/hardware_control/emulation/simulations.py +133 -0
  130. opentrons/hardware_control/emulation/smoothie.py +192 -0
  131. opentrons/hardware_control/emulation/tempdeck.py +69 -0
  132. opentrons/hardware_control/emulation/thermocycler.py +128 -0
  133. opentrons/hardware_control/emulation/types.py +10 -0
  134. opentrons/hardware_control/emulation/util.py +38 -0
  135. opentrons/hardware_control/errors.py +43 -0
  136. opentrons/hardware_control/execution_manager.py +164 -0
  137. opentrons/hardware_control/instruments/__init__.py +5 -0
  138. opentrons/hardware_control/instruments/instrument_abc.py +39 -0
  139. opentrons/hardware_control/instruments/ot2/__init__.py +0 -0
  140. opentrons/hardware_control/instruments/ot2/instrument_calibration.py +152 -0
  141. opentrons/hardware_control/instruments/ot2/pipette.py +777 -0
  142. opentrons/hardware_control/instruments/ot2/pipette_handler.py +995 -0
  143. opentrons/hardware_control/instruments/ot3/__init__.py +0 -0
  144. opentrons/hardware_control/instruments/ot3/gripper.py +420 -0
  145. opentrons/hardware_control/instruments/ot3/gripper_handler.py +173 -0
  146. opentrons/hardware_control/instruments/ot3/instrument_calibration.py +214 -0
  147. opentrons/hardware_control/instruments/ot3/pipette.py +858 -0
  148. opentrons/hardware_control/instruments/ot3/pipette_handler.py +1030 -0
  149. opentrons/hardware_control/module_control.py +332 -0
  150. opentrons/hardware_control/modules/__init__.py +69 -0
  151. opentrons/hardware_control/modules/absorbance_reader.py +373 -0
  152. opentrons/hardware_control/modules/errors.py +7 -0
  153. opentrons/hardware_control/modules/flex_stacker.py +948 -0
  154. opentrons/hardware_control/modules/heater_shaker.py +426 -0
  155. opentrons/hardware_control/modules/lid_temp_status.py +35 -0
  156. opentrons/hardware_control/modules/magdeck.py +233 -0
  157. opentrons/hardware_control/modules/mod_abc.py +245 -0
  158. opentrons/hardware_control/modules/module_calibration.py +93 -0
  159. opentrons/hardware_control/modules/plate_temp_status.py +61 -0
  160. opentrons/hardware_control/modules/tempdeck.py +299 -0
  161. opentrons/hardware_control/modules/thermocycler.py +731 -0
  162. opentrons/hardware_control/modules/types.py +417 -0
  163. opentrons/hardware_control/modules/update.py +255 -0
  164. opentrons/hardware_control/modules/utils.py +73 -0
  165. opentrons/hardware_control/motion_utilities.py +318 -0
  166. opentrons/hardware_control/nozzle_manager.py +422 -0
  167. opentrons/hardware_control/ot3_calibration.py +1171 -0
  168. opentrons/hardware_control/ot3api.py +3227 -0
  169. opentrons/hardware_control/pause_manager.py +31 -0
  170. opentrons/hardware_control/poller.py +112 -0
  171. opentrons/hardware_control/protocols/__init__.py +106 -0
  172. opentrons/hardware_control/protocols/asyncio_configurable.py +11 -0
  173. opentrons/hardware_control/protocols/calibratable.py +45 -0
  174. opentrons/hardware_control/protocols/chassis_accessory_manager.py +90 -0
  175. opentrons/hardware_control/protocols/configurable.py +48 -0
  176. opentrons/hardware_control/protocols/event_sourcer.py +18 -0
  177. opentrons/hardware_control/protocols/execution_controllable.py +33 -0
  178. opentrons/hardware_control/protocols/flex_calibratable.py +96 -0
  179. opentrons/hardware_control/protocols/flex_instrument_configurer.py +52 -0
  180. opentrons/hardware_control/protocols/gripper_controller.py +55 -0
  181. opentrons/hardware_control/protocols/hardware_manager.py +51 -0
  182. opentrons/hardware_control/protocols/identifiable.py +16 -0
  183. opentrons/hardware_control/protocols/instrument_configurer.py +206 -0
  184. opentrons/hardware_control/protocols/liquid_handler.py +266 -0
  185. opentrons/hardware_control/protocols/module_provider.py +16 -0
  186. opentrons/hardware_control/protocols/motion_controller.py +243 -0
  187. opentrons/hardware_control/protocols/position_estimator.py +45 -0
  188. opentrons/hardware_control/protocols/simulatable.py +10 -0
  189. opentrons/hardware_control/protocols/stoppable.py +9 -0
  190. opentrons/hardware_control/protocols/types.py +27 -0
  191. opentrons/hardware_control/robot_calibration.py +224 -0
  192. opentrons/hardware_control/scripts/README.md +28 -0
  193. opentrons/hardware_control/scripts/__init__.py +1 -0
  194. opentrons/hardware_control/scripts/gripper_control.py +208 -0
  195. opentrons/hardware_control/scripts/ot3gripper +7 -0
  196. opentrons/hardware_control/scripts/ot3repl +7 -0
  197. opentrons/hardware_control/scripts/repl.py +187 -0
  198. opentrons/hardware_control/scripts/tc_control.py +97 -0
  199. opentrons/hardware_control/simulator_setup.py +260 -0
  200. opentrons/hardware_control/thread_manager.py +431 -0
  201. opentrons/hardware_control/threaded_async_lock.py +97 -0
  202. opentrons/hardware_control/types.py +792 -0
  203. opentrons/hardware_control/util.py +234 -0
  204. opentrons/legacy_broker.py +53 -0
  205. opentrons/legacy_commands/__init__.py +1 -0
  206. opentrons/legacy_commands/commands.py +483 -0
  207. opentrons/legacy_commands/helpers.py +153 -0
  208. opentrons/legacy_commands/module_commands.py +215 -0
  209. opentrons/legacy_commands/protocol_commands.py +54 -0
  210. opentrons/legacy_commands/publisher.py +155 -0
  211. opentrons/legacy_commands/robot_commands.py +51 -0
  212. opentrons/legacy_commands/types.py +1115 -0
  213. opentrons/motion_planning/__init__.py +32 -0
  214. opentrons/motion_planning/adjacent_slots_getters.py +168 -0
  215. opentrons/motion_planning/deck_conflict.py +396 -0
  216. opentrons/motion_planning/errors.py +35 -0
  217. opentrons/motion_planning/types.py +42 -0
  218. opentrons/motion_planning/waypoints.py +218 -0
  219. opentrons/ordered_set.py +138 -0
  220. opentrons/protocol_api/__init__.py +105 -0
  221. opentrons/protocol_api/_liquid.py +157 -0
  222. opentrons/protocol_api/_liquid_properties.py +814 -0
  223. opentrons/protocol_api/_nozzle_layout.py +31 -0
  224. opentrons/protocol_api/_parameter_context.py +300 -0
  225. opentrons/protocol_api/_parameters.py +31 -0
  226. opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
  227. opentrons/protocol_api/_types.py +43 -0
  228. opentrons/protocol_api/config.py +23 -0
  229. opentrons/protocol_api/core/__init__.py +23 -0
  230. opentrons/protocol_api/core/common.py +33 -0
  231. opentrons/protocol_api/core/core_map.py +74 -0
  232. opentrons/protocol_api/core/engine/__init__.py +22 -0
  233. opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
  234. opentrons/protocol_api/core/engine/deck_conflict.py +348 -0
  235. opentrons/protocol_api/core/engine/exceptions.py +19 -0
  236. opentrons/protocol_api/core/engine/instrument.py +2391 -0
  237. opentrons/protocol_api/core/engine/labware.py +238 -0
  238. opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
  239. opentrons/protocol_api/core/engine/module_core.py +1025 -0
  240. opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
  241. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
  242. opentrons/protocol_api/core/engine/point_calculations.py +64 -0
  243. opentrons/protocol_api/core/engine/protocol.py +1153 -0
  244. opentrons/protocol_api/core/engine/robot.py +139 -0
  245. opentrons/protocol_api/core/engine/stringify.py +74 -0
  246. opentrons/protocol_api/core/engine/transfer_components_executor.py +990 -0
  247. opentrons/protocol_api/core/engine/well.py +241 -0
  248. opentrons/protocol_api/core/instrument.py +459 -0
  249. opentrons/protocol_api/core/labware.py +151 -0
  250. opentrons/protocol_api/core/legacy/__init__.py +11 -0
  251. opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
  252. opentrons/protocol_api/core/legacy/deck.py +369 -0
  253. opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
  254. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
  255. opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
  256. opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
  257. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
  258. opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
  259. opentrons/protocol_api/core/legacy/load_info.py +67 -0
  260. opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
  261. opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
  262. opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
  263. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
  264. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
  265. opentrons/protocol_api/core/module.py +484 -0
  266. opentrons/protocol_api/core/protocol.py +311 -0
  267. opentrons/protocol_api/core/robot.py +51 -0
  268. opentrons/protocol_api/core/well.py +116 -0
  269. opentrons/protocol_api/core/well_grid.py +45 -0
  270. opentrons/protocol_api/create_protocol_context.py +177 -0
  271. opentrons/protocol_api/deck.py +223 -0
  272. opentrons/protocol_api/disposal_locations.py +244 -0
  273. opentrons/protocol_api/instrument_context.py +3212 -0
  274. opentrons/protocol_api/labware.py +1579 -0
  275. opentrons/protocol_api/module_contexts.py +1425 -0
  276. opentrons/protocol_api/module_validation_and_errors.py +61 -0
  277. opentrons/protocol_api/protocol_context.py +1688 -0
  278. opentrons/protocol_api/robot_context.py +303 -0
  279. opentrons/protocol_api/validation.py +761 -0
  280. opentrons/protocol_engine/__init__.py +155 -0
  281. opentrons/protocol_engine/actions/__init__.py +65 -0
  282. opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
  283. opentrons/protocol_engine/actions/action_handler.py +13 -0
  284. opentrons/protocol_engine/actions/actions.py +302 -0
  285. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  286. opentrons/protocol_engine/clients/__init__.py +5 -0
  287. opentrons/protocol_engine/clients/sync_client.py +174 -0
  288. opentrons/protocol_engine/clients/transports.py +197 -0
  289. opentrons/protocol_engine/commands/__init__.py +757 -0
  290. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
  291. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
  292. opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
  293. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
  294. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
  295. opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
  296. opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
  297. opentrons/protocol_engine/commands/aspirate.py +244 -0
  298. opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
  299. opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
  300. opentrons/protocol_engine/commands/blow_out.py +146 -0
  301. opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
  302. opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
  303. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
  304. opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
  305. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
  306. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
  307. opentrons/protocol_engine/commands/command.py +308 -0
  308. opentrons/protocol_engine/commands/command_unions.py +974 -0
  309. opentrons/protocol_engine/commands/comment.py +57 -0
  310. opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
  311. opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
  312. opentrons/protocol_engine/commands/custom.py +67 -0
  313. opentrons/protocol_engine/commands/dispense.py +194 -0
  314. opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
  315. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  316. opentrons/protocol_engine/commands/drop_tip.py +232 -0
  317. opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
  318. opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
  319. opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
  320. opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
  321. opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
  322. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
  323. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
  324. opentrons/protocol_engine/commands/flex_stacker/store.py +326 -0
  325. opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
  326. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  327. opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
  328. opentrons/protocol_engine/commands/hash_command_params.py +38 -0
  329. opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
  330. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
  331. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
  332. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
  333. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
  334. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
  335. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
  336. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
  337. opentrons/protocol_engine/commands/home.py +100 -0
  338. opentrons/protocol_engine/commands/identify_module.py +86 -0
  339. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  340. opentrons/protocol_engine/commands/liquid_probe.py +464 -0
  341. opentrons/protocol_engine/commands/load_labware.py +210 -0
  342. opentrons/protocol_engine/commands/load_lid.py +154 -0
  343. opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
  344. opentrons/protocol_engine/commands/load_liquid.py +95 -0
  345. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  346. opentrons/protocol_engine/commands/load_module.py +223 -0
  347. opentrons/protocol_engine/commands/load_pipette.py +167 -0
  348. opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
  349. opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
  350. opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
  351. opentrons/protocol_engine/commands/move_labware.py +546 -0
  352. opentrons/protocol_engine/commands/move_relative.py +102 -0
  353. opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
  354. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
  355. opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
  356. opentrons/protocol_engine/commands/move_to_well.py +119 -0
  357. opentrons/protocol_engine/commands/movement_common.py +338 -0
  358. opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
  359. opentrons/protocol_engine/commands/pipetting_common.py +443 -0
  360. opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
  361. opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
  362. opentrons/protocol_engine/commands/reload_labware.py +90 -0
  363. opentrons/protocol_engine/commands/retract_axis.py +75 -0
  364. opentrons/protocol_engine/commands/robot/__init__.py +70 -0
  365. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
  366. opentrons/protocol_engine/commands/robot/common.py +18 -0
  367. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  368. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  369. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  370. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
  371. opentrons/protocol_engine/commands/save_position.py +109 -0
  372. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
  373. opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
  374. opentrons/protocol_engine/commands/set_status_bar.py +89 -0
  375. opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
  376. opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
  377. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
  378. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
  379. opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
  380. opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
  381. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
  382. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
  383. opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
  384. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
  385. opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
  386. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
  387. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
  388. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
  389. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
  390. opentrons/protocol_engine/commands/touch_tip.py +189 -0
  391. opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
  392. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
  393. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
  394. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
  395. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  396. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
  397. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
  398. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
  399. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
  400. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  401. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
  402. opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
  403. opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
  404. opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
  405. opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
  406. opentrons/protocol_engine/create_protocol_engine.py +193 -0
  407. opentrons/protocol_engine/engine_support.py +28 -0
  408. opentrons/protocol_engine/error_recovery_policy.py +81 -0
  409. opentrons/protocol_engine/errors/__init__.py +191 -0
  410. opentrons/protocol_engine/errors/error_occurrence.py +182 -0
  411. opentrons/protocol_engine/errors/exceptions.py +1308 -0
  412. opentrons/protocol_engine/execution/__init__.py +50 -0
  413. opentrons/protocol_engine/execution/command_executor.py +216 -0
  414. opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
  415. opentrons/protocol_engine/execution/door_watcher.py +119 -0
  416. opentrons/protocol_engine/execution/equipment.py +819 -0
  417. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  418. opentrons/protocol_engine/execution/gantry_mover.py +686 -0
  419. opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
  420. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
  421. opentrons/protocol_engine/execution/labware_movement.py +297 -0
  422. opentrons/protocol_engine/execution/movement.py +349 -0
  423. opentrons/protocol_engine/execution/pipetting.py +607 -0
  424. opentrons/protocol_engine/execution/queue_worker.py +86 -0
  425. opentrons/protocol_engine/execution/rail_lights.py +25 -0
  426. opentrons/protocol_engine/execution/run_control.py +33 -0
  427. opentrons/protocol_engine/execution/status_bar.py +34 -0
  428. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
  429. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
  430. opentrons/protocol_engine/execution/tip_handler.py +550 -0
  431. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  432. opentrons/protocol_engine/notes/__init__.py +17 -0
  433. opentrons/protocol_engine/notes/notes.py +59 -0
  434. opentrons/protocol_engine/plugins.py +104 -0
  435. opentrons/protocol_engine/protocol_engine.py +683 -0
  436. opentrons/protocol_engine/resources/__init__.py +26 -0
  437. opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
  438. opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
  439. opentrons/protocol_engine/resources/file_provider.py +161 -0
  440. opentrons/protocol_engine/resources/fixture_validation.py +58 -0
  441. opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
  442. opentrons/protocol_engine/resources/labware_validation.py +73 -0
  443. opentrons/protocol_engine/resources/model_utils.py +32 -0
  444. opentrons/protocol_engine/resources/module_data_provider.py +44 -0
  445. opentrons/protocol_engine/resources/ot3_validation.py +21 -0
  446. opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
  447. opentrons/protocol_engine/slot_standardization.py +128 -0
  448. opentrons/protocol_engine/state/__init__.py +1 -0
  449. opentrons/protocol_engine/state/_abstract_store.py +27 -0
  450. opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
  451. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  452. opentrons/protocol_engine/state/_move_types.py +83 -0
  453. opentrons/protocol_engine/state/_well_math.py +193 -0
  454. opentrons/protocol_engine/state/addressable_areas.py +699 -0
  455. opentrons/protocol_engine/state/command_history.py +309 -0
  456. opentrons/protocol_engine/state/commands.py +1158 -0
  457. opentrons/protocol_engine/state/config.py +39 -0
  458. opentrons/protocol_engine/state/files.py +57 -0
  459. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  460. opentrons/protocol_engine/state/geometry.py +2359 -0
  461. opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
  462. opentrons/protocol_engine/state/labware.py +1459 -0
  463. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  464. opentrons/protocol_engine/state/liquids.py +73 -0
  465. opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
  466. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
  467. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
  468. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
  469. opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
  470. opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
  471. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
  472. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
  473. opentrons/protocol_engine/state/modules.py +1500 -0
  474. opentrons/protocol_engine/state/motion.py +373 -0
  475. opentrons/protocol_engine/state/pipettes.py +905 -0
  476. opentrons/protocol_engine/state/state.py +421 -0
  477. opentrons/protocol_engine/state/state_summary.py +36 -0
  478. opentrons/protocol_engine/state/tips.py +420 -0
  479. opentrons/protocol_engine/state/update_types.py +904 -0
  480. opentrons/protocol_engine/state/wells.py +290 -0
  481. opentrons/protocol_engine/types/__init__.py +308 -0
  482. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  483. opentrons/protocol_engine/types/command_annotations.py +53 -0
  484. opentrons/protocol_engine/types/deck_configuration.py +81 -0
  485. opentrons/protocol_engine/types/execution.py +96 -0
  486. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  487. opentrons/protocol_engine/types/instrument.py +47 -0
  488. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  489. opentrons/protocol_engine/types/labware.py +131 -0
  490. opentrons/protocol_engine/types/labware_movement.py +22 -0
  491. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  492. opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
  493. opentrons/protocol_engine/types/liquid.py +40 -0
  494. opentrons/protocol_engine/types/liquid_class.py +59 -0
  495. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  496. opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
  497. opentrons/protocol_engine/types/location.py +194 -0
  498. opentrons/protocol_engine/types/module.py +303 -0
  499. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  500. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  501. opentrons/protocol_engine/types/tip.py +18 -0
  502. opentrons/protocol_engine/types/util.py +21 -0
  503. opentrons/protocol_engine/types/well_position.py +124 -0
  504. opentrons/protocol_reader/__init__.py +37 -0
  505. opentrons/protocol_reader/extract_labware_definitions.py +66 -0
  506. opentrons/protocol_reader/file_format_validator.py +152 -0
  507. opentrons/protocol_reader/file_hasher.py +27 -0
  508. opentrons/protocol_reader/file_identifier.py +284 -0
  509. opentrons/protocol_reader/file_reader_writer.py +90 -0
  510. opentrons/protocol_reader/input_file.py +16 -0
  511. opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
  512. opentrons/protocol_reader/protocol_reader.py +188 -0
  513. opentrons/protocol_reader/protocol_source.py +124 -0
  514. opentrons/protocol_reader/role_analyzer.py +86 -0
  515. opentrons/protocol_runner/__init__.py +26 -0
  516. opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
  517. opentrons/protocol_runner/json_file_reader.py +55 -0
  518. opentrons/protocol_runner/json_translator.py +314 -0
  519. opentrons/protocol_runner/legacy_command_mapper.py +848 -0
  520. opentrons/protocol_runner/legacy_context_plugin.py +116 -0
  521. opentrons/protocol_runner/protocol_runner.py +530 -0
  522. opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
  523. opentrons/protocol_runner/run_orchestrator.py +496 -0
  524. opentrons/protocol_runner/task_queue.py +95 -0
  525. opentrons/protocols/__init__.py +6 -0
  526. opentrons/protocols/advanced_control/__init__.py +0 -0
  527. opentrons/protocols/advanced_control/common.py +38 -0
  528. opentrons/protocols/advanced_control/mix.py +60 -0
  529. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  530. opentrons/protocols/advanced_control/transfers/common.py +180 -0
  531. opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
  532. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
  533. opentrons/protocols/api_support/__init__.py +0 -0
  534. opentrons/protocols/api_support/constants.py +8 -0
  535. opentrons/protocols/api_support/deck_type.py +110 -0
  536. opentrons/protocols/api_support/definitions.py +18 -0
  537. opentrons/protocols/api_support/instrument.py +151 -0
  538. opentrons/protocols/api_support/labware_like.py +233 -0
  539. opentrons/protocols/api_support/tip_tracker.py +175 -0
  540. opentrons/protocols/api_support/types.py +32 -0
  541. opentrons/protocols/api_support/util.py +403 -0
  542. opentrons/protocols/bundle.py +89 -0
  543. opentrons/protocols/duration/__init__.py +4 -0
  544. opentrons/protocols/duration/errors.py +5 -0
  545. opentrons/protocols/duration/estimator.py +628 -0
  546. opentrons/protocols/execution/__init__.py +0 -0
  547. opentrons/protocols/execution/dev_types.py +181 -0
  548. opentrons/protocols/execution/errors.py +40 -0
  549. opentrons/protocols/execution/execute.py +84 -0
  550. opentrons/protocols/execution/execute_json_v3.py +275 -0
  551. opentrons/protocols/execution/execute_json_v4.py +359 -0
  552. opentrons/protocols/execution/execute_json_v5.py +28 -0
  553. opentrons/protocols/execution/execute_python.py +169 -0
  554. opentrons/protocols/execution/json_dispatchers.py +87 -0
  555. opentrons/protocols/execution/types.py +7 -0
  556. opentrons/protocols/geometry/__init__.py +0 -0
  557. opentrons/protocols/geometry/planning.py +297 -0
  558. opentrons/protocols/labware.py +312 -0
  559. opentrons/protocols/models/__init__.py +0 -0
  560. opentrons/protocols/models/json_protocol.py +679 -0
  561. opentrons/protocols/parameters/__init__.py +0 -0
  562. opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
  563. opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
  564. opentrons/protocols/parameters/exceptions.py +34 -0
  565. opentrons/protocols/parameters/parameter_definition.py +272 -0
  566. opentrons/protocols/parameters/types.py +17 -0
  567. opentrons/protocols/parameters/validation.py +267 -0
  568. opentrons/protocols/parse.py +671 -0
  569. opentrons/protocols/types.py +159 -0
  570. opentrons/py.typed +0 -0
  571. opentrons/resources/scripts/lpc21isp +0 -0
  572. opentrons/resources/smoothie-edge-8414642.hex +23010 -0
  573. opentrons/simulate.py +1065 -0
  574. opentrons/system/__init__.py +6 -0
  575. opentrons/system/camera.py +51 -0
  576. opentrons/system/log_control.py +59 -0
  577. opentrons/system/nmcli.py +856 -0
  578. opentrons/system/resin.py +24 -0
  579. opentrons/system/smoothie_update.py +15 -0
  580. opentrons/system/wifi.py +204 -0
  581. opentrons/tools/__init__.py +0 -0
  582. opentrons/tools/args_handler.py +22 -0
  583. opentrons/tools/write_pipette_memory.py +157 -0
  584. opentrons/types.py +618 -0
  585. opentrons/util/__init__.py +1 -0
  586. opentrons/util/async_helpers.py +166 -0
  587. opentrons/util/broker.py +84 -0
  588. opentrons/util/change_notifier.py +47 -0
  589. opentrons/util/entrypoint_util.py +278 -0
  590. opentrons/util/get_union_elements.py +26 -0
  591. opentrons/util/helpers.py +6 -0
  592. opentrons/util/linal.py +178 -0
  593. opentrons/util/logging_config.py +265 -0
  594. opentrons/util/logging_queue_handler.py +61 -0
  595. opentrons/util/performance_helpers.py +157 -0
  596. opentrons-8.6.0a1.dist-info/METADATA +37 -0
  597. opentrons-8.6.0a1.dist-info/RECORD +600 -0
  598. opentrons-8.6.0a1.dist-info/WHEEL +4 -0
  599. opentrons-8.6.0a1.dist-info/entry_points.txt +3 -0
  600. opentrons-8.6.0a1.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,1688 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from copy import deepcopy
5
+ from typing import (
6
+ Callable,
7
+ Dict,
8
+ List,
9
+ Optional,
10
+ Type,
11
+ Union,
12
+ Mapping,
13
+ cast,
14
+ )
15
+
16
+ from opentrons_shared_data.labware.types import LabwareDefinition
17
+ from opentrons_shared_data.liquid_classes.liquid_class_definition import (
18
+ TransferProperties as SharedTransferProperties,
19
+ )
20
+ from opentrons_shared_data.liquid_classes import DEFAULT_LC_VERSION, definition_exists
21
+ from opentrons_shared_data.liquid_classes.types import TransferPropertiesDict
22
+ from opentrons_shared_data.pipette.types import PipetteNameType
23
+
24
+ from opentrons.types import Mount, Location, DeckLocation, DeckSlotName, StagingSlotName
25
+ from opentrons.legacy_broker import LegacyBroker
26
+ from opentrons.hardware_control.modules.types import (
27
+ MagneticBlockModel,
28
+ AbsorbanceReaderModel,
29
+ FlexStackerModuleModel,
30
+ )
31
+ from opentrons.legacy_commands import protocol_commands as cmds, types as cmd_types
32
+ from opentrons.legacy_commands.helpers import (
33
+ stringify_labware_movement_command,
34
+ stringify_lid_movement_command,
35
+ )
36
+ from opentrons.legacy_commands.publisher import (
37
+ CommandPublisher,
38
+ publish,
39
+ publish_context,
40
+ )
41
+ from opentrons.protocols.api_support import instrument as instrument_support
42
+ from opentrons.protocols.api_support.deck_type import (
43
+ NoTrashDefinedError,
44
+ should_load_fixed_trash_labware_for_python_protocol,
45
+ should_load_fixed_trash_area_for_python_protocol,
46
+ )
47
+ from opentrons.protocols.api_support.types import APIVersion
48
+ from opentrons.protocols.api_support.util import (
49
+ AxisMaxSpeeds,
50
+ requires_version,
51
+ APIVersionError,
52
+ RobotTypeError,
53
+ UnsupportedAPIError,
54
+ )
55
+ from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated
56
+ from opentrons.protocol_engine.errors import LabwareMovementNotAllowedError
57
+ from ._liquid_properties import build_transfer_properties
58
+
59
+ from ._types import OffDeckType
60
+ from .core.common import ModuleCore, LabwareCore, ProtocolCore
61
+ from .core.core_map import LoadedCoreMap
62
+ from .core.engine.module_core import NonConnectedModuleCore
63
+ from .core.module import (
64
+ AbstractTemperatureModuleCore,
65
+ AbstractMagneticModuleCore,
66
+ AbstractThermocyclerCore,
67
+ AbstractHeaterShakerCore,
68
+ AbstractMagneticBlockCore,
69
+ AbstractAbsorbanceReaderCore,
70
+ AbstractFlexStackerCore,
71
+ )
72
+ from .robot_context import RobotContext, HardwareManager
73
+ from .core.engine import ENGINE_CORE_API_VERSION
74
+ from .core.legacy.legacy_protocol_core import LegacyProtocolCore
75
+
76
+ from . import validation
77
+ from ._liquid import Liquid, LiquidClass
78
+ from .disposal_locations import TrashBin, WasteChute
79
+ from .deck import Deck
80
+ from .instrument_context import InstrumentContext
81
+ from .labware import Labware
82
+ from .module_contexts import (
83
+ MagneticModuleContext,
84
+ TemperatureModuleContext,
85
+ ThermocyclerContext,
86
+ HeaterShakerContext,
87
+ MagneticBlockContext,
88
+ AbsorbanceReaderContext,
89
+ FlexStackerContext,
90
+ ModuleContext,
91
+ )
92
+ from ._parameters import Parameters
93
+
94
+
95
+ logger = logging.getLogger(__name__)
96
+
97
+
98
+ ModuleTypes = Union[
99
+ TemperatureModuleContext,
100
+ MagneticModuleContext,
101
+ ThermocyclerContext,
102
+ HeaterShakerContext,
103
+ MagneticBlockContext,
104
+ AbsorbanceReaderContext,
105
+ FlexStackerContext,
106
+ ]
107
+
108
+
109
+ class _Unset:
110
+ """A sentinel value for when no value has been supplied for an argument,
111
+ when `None` is already taken for some other meaning.
112
+
113
+ User code should never use this explicitly.
114
+ """
115
+
116
+ pass
117
+
118
+
119
+ class ProtocolContext(CommandPublisher):
120
+ """A context for the state of a protocol.
121
+
122
+ The ``ProtocolContext`` class provides the objects, attributes, and methods that
123
+ allow you to configure and control the protocol.
124
+
125
+ Methods generally fall into one of two categories.
126
+
127
+ - They can change the state of the ``ProtocolContext`` object, such as adding
128
+ pipettes, hardware modules, or labware to your protocol.
129
+ - They can control the flow of a running protocol, such as pausing, displaying
130
+ messages, or controlling built-in robot hardware like the ambient lighting.
131
+
132
+ Do not instantiate a ``ProtocolContext`` directly.
133
+ The ``run()`` function of your protocol does that for you.
134
+ See the :ref:`Tutorial <run-function>` for more information.
135
+
136
+ Use :py:meth:`opentrons.execute.get_protocol_api` to instantiate a ``ProtocolContext`` when
137
+ using Jupyter Notebook. See :ref:`advanced-control`.
138
+
139
+ .. versionadded:: 2.0
140
+
141
+ """
142
+
143
+ def __init__(
144
+ self,
145
+ api_version: APIVersion,
146
+ core: ProtocolCore,
147
+ broker: Optional[LegacyBroker] = None,
148
+ core_map: Optional[LoadedCoreMap] = None,
149
+ deck: Optional[Deck] = None,
150
+ bundled_data: Optional[Dict[str, bytes]] = None,
151
+ ) -> None:
152
+ """Build a :py:class:`.ProtocolContext`.
153
+
154
+ :param api_version: The API version to use.
155
+ :param core: The protocol implementation core.
156
+ :param labware_offset_provider: Where this protocol context and its child
157
+ module contexts will get labware offsets from.
158
+ :param broker: An optional command broker to link to. If not
159
+ specified, a dummy one is used.
160
+ :param bundled_data: A dict mapping filenames to the contents of data
161
+ files. Can be used by the protocol, since it is
162
+ exposed as
163
+ :py:attr:`.ProtocolContext.bundled_data`
164
+ """
165
+ super().__init__(broker)
166
+ self._api_version = api_version
167
+ self._core = core
168
+ self._core_map = core_map or LoadedCoreMap()
169
+ self._deck = deck or Deck(
170
+ protocol_core=core, core_map=self._core_map, api_version=api_version
171
+ )
172
+
173
+ # With the introduction of Extension mount type, this dict initializes to include
174
+ # the extension mount, for both ot2 & 3. While it doesn't seem like it would
175
+ # create an issue in the current PAPI context, it would be much safer to
176
+ # only use mounts available on the robot.
177
+ self._instruments: Dict[Mount, Optional[InstrumentContext]] = {
178
+ mount: None for mount in Mount
179
+ }
180
+ self._bundled_data: Dict[str, bytes] = bundled_data or {}
181
+
182
+ # With the addition of Movable Trashes and Waste Chute support, it is not necessary
183
+ # to ensure that the list of "disposal locations", essentially the list of trashes,
184
+ # is initialized correctly on protocols utilizing former API versions prior to 2.16
185
+ # and also to ensure that any protocols after 2.16 initialize a Fixed Trash for OT-2
186
+ # protocols so that no load trash bin behavior is required within the protocol itself.
187
+ # Protocols prior to 2.16 expect the Fixed Trash to exist as a Labware object, while
188
+ # protocols after 2.16 expect trash to exist as either a TrashBin or WasteChute object.
189
+
190
+ self._load_fixed_trash()
191
+ if should_load_fixed_trash_labware_for_python_protocol(self._api_version):
192
+ self._core.append_disposal_location(self.fixed_trash)
193
+ elif should_load_fixed_trash_area_for_python_protocol(
194
+ self._api_version, self._core.robot_type
195
+ ):
196
+ self._core.load_ot2_fixed_trash_bin()
197
+
198
+ self._commands: List[str] = []
199
+ self._params: Parameters = Parameters()
200
+ self._unsubscribe_commands: Optional[Callable[[], None]] = None
201
+ try:
202
+ self._robot: Optional[RobotContext] = RobotContext(
203
+ core=self._core.load_robot(),
204
+ protocol_core=self._core,
205
+ api_version=self._api_version,
206
+ broker=broker,
207
+ )
208
+ except APIVersionError:
209
+ self._robot = None
210
+ self.clear_commands()
211
+
212
+ @property
213
+ @requires_version(2, 0)
214
+ def api_version(self) -> APIVersion:
215
+ """Return the API version specified for this protocol context.
216
+
217
+ This value is set when the protocol context
218
+ is initialized.
219
+
220
+ - When the context is the argument of ``run()``, the ``"apiLevel"`` key of the
221
+ :ref:`metadata <tutorial-metadata>` or :ref:`requirements
222
+ <tutorial-requirements>` dictionary determines ``api_version``.
223
+ - When the context is instantiated with
224
+ :py:meth:`opentrons.execute.get_protocol_api` or
225
+ :py:meth:`opentrons.simulate.get_protocol_api`, the value of its ``version``
226
+ argument determines ``api_version``.
227
+
228
+ It may be lower than the :ref:`maximum version <max-version>` supported by the
229
+ robot software, which is accessible via the
230
+ ``protocol_api.MAX_SUPPORTED_VERSION`` constant.
231
+ """
232
+ return self._api_version
233
+
234
+ @property
235
+ @requires_version(2, 22)
236
+ def robot(self) -> RobotContext:
237
+ """The :py:class:`.RobotContext` for the protocol.
238
+
239
+ :meta private:
240
+ """
241
+ if self._core.robot_type != "OT-3 Standard" or not self._robot:
242
+ raise RobotTypeError("The RobotContext is only available on Flex robots.")
243
+ return self._robot
244
+
245
+ @property
246
+ def _hw_manager(self) -> HardwareManager:
247
+ # TODO (lc 01-05-2021) remove this once we have a more
248
+ # user facing hardware control http api.
249
+ # HardwareManager(hardware=self._core.get_hardware())
250
+ logger.warning(
251
+ "This function will be deprecated in later versions."
252
+ "Please use with caution."
253
+ )
254
+ if self._robot:
255
+ return self._robot.hardware
256
+ return HardwareManager(hardware=self._core.get_hardware())
257
+
258
+ @property
259
+ @requires_version(2, 0)
260
+ def bundled_data(self) -> Dict[str, bytes]:
261
+ """Accessor for data files bundled with this protocol, if any.
262
+
263
+ This is a dictionary mapping the filenames of bundled datafiles to their
264
+ contents. The filename keys are formatted with extensions but without paths. For
265
+ example, a file stored in the bundle as ``data/mydata/aspirations.csv`` will
266
+ have the key ``"aspirations.csv"``. The values are :py:class:`bytes` objects
267
+ representing the contents of the files.
268
+ """
269
+ return self._bundled_data
270
+
271
+ @property
272
+ @requires_version(2, 18)
273
+ def params(self) -> Parameters:
274
+ """
275
+ The values of runtime parameters, as set during run setup.
276
+
277
+ Each attribute of this object corresponds to the ``variable_name`` of a parameter.
278
+ See :ref:`using-rtp` for details.
279
+
280
+ Parameter values can only be set during run setup. If you try to alter the value
281
+ of any attribute of ``params``, the API will raise an error.
282
+ """
283
+ return self._params
284
+
285
+ def cleanup(self) -> None:
286
+ """Finalize and clean up the protocol context."""
287
+ if self._unsubscribe_commands:
288
+ self._unsubscribe_commands()
289
+ self._unsubscribe_commands = None
290
+
291
+ @property
292
+ @requires_version(2, 0)
293
+ def max_speeds(self) -> AxisMaxSpeeds:
294
+ """Per-axis speed limits for moving instruments.
295
+
296
+ Changing values within this property sets the speed limit for each non-plunger
297
+ axis of the robot. Note that this property only sets upper limits and can't
298
+ exceed the physical speed limits of the movement system.
299
+
300
+ This property is a dict mapping string names of axes to float values
301
+ of maximum speeds in mm/s. To change a speed, set that axis's value. To
302
+ reset an axis's speed to default, delete the entry for that axis
303
+ or assign it to ``None``.
304
+
305
+ See :ref:`axis_speed_limits` for examples.
306
+
307
+ .. note::
308
+ This property is not yet supported in API version 2.14 or higher.
309
+ """
310
+ if self._api_version >= ENGINE_CORE_API_VERSION:
311
+ # TODO(mc, 2023-02-23): per-axis max speeds not yet supported on the engine
312
+ # See https://opentrons.atlassian.net/browse/RCORE-373
313
+ raise UnsupportedAPIError(
314
+ api_element="ProtocolContext.max_speeds",
315
+ since_version=f"{ENGINE_CORE_API_VERSION}",
316
+ current_version=f"{self._api_version}",
317
+ extra_message="Set speeds using InstrumentContext.default_speed or the per-method 'speed' argument.",
318
+ )
319
+
320
+ return self._core.get_max_speeds()
321
+
322
+ @requires_version(2, 0)
323
+ def commands(self) -> List[str]:
324
+ """Return the run log.
325
+
326
+ This is a list of human-readable strings representing what's been done in the protocol so
327
+ far. For example, "Aspirating 123 µL from well A1 of 96 well plate in slot 1."
328
+
329
+ The exact format of these entries is not guaranteed. The format here may differ from other
330
+ places that show the run log, such as the Opentrons App or touchscreen.
331
+ """
332
+ return self._commands
333
+
334
+ @requires_version(2, 0)
335
+ def clear_commands(self) -> None:
336
+ self._commands.clear()
337
+ if self._unsubscribe_commands:
338
+ self._unsubscribe_commands()
339
+
340
+ self._unsubscribe_commands = self.broker.subscribe(
341
+ cmd_types.COMMAND, self._on_command_callback
342
+ )
343
+
344
+ def _on_command_callback(self, message: cmd_types.CommandMessage) -> None:
345
+ """Callback for command messages."""
346
+ payload = message.get("payload")
347
+
348
+ if payload is None:
349
+ return
350
+
351
+ text = payload.get("text")
352
+
353
+ if text is None:
354
+ return
355
+
356
+ if message["$"] == "before":
357
+ self._commands.append(text)
358
+
359
+ @requires_version(2, 0)
360
+ def is_simulating(self) -> bool:
361
+ """Returns ``True`` if the protocol is running in simulation.
362
+
363
+ Returns ``False`` if the protocol is running on actual hardware.
364
+
365
+ You can evaluate the result of this method in an ``if`` statement to make your
366
+ protocol behave differently in different environments. For example, you could
367
+ refer to a data file on your computer when simulating and refer to a data file
368
+ stored on the robot when not simulating.
369
+
370
+ You can also use it to skip time-consuming aspects of your protocol. Most Python
371
+ Protocol API methods, like :py:meth:`.delay`, are designed to evaluate
372
+ instantaneously in simulation. But external methods, like those from the
373
+ :py:mod:`time` module, will run at normal speed if not skipped.
374
+ """
375
+ return self._core.is_simulating()
376
+
377
+ @requires_version(2, 0)
378
+ def load_labware_from_definition(
379
+ self,
380
+ labware_def: "LabwareDefinition",
381
+ location: Union[DeckLocation, OffDeckType],
382
+ label: Optional[str] = None,
383
+ ) -> Labware:
384
+ """Specify the presence of a labware on the deck.
385
+
386
+ This function loads the labware definition specified by ``labware_def``
387
+ to the location specified by ``location``.
388
+
389
+ :param labware_def: The labware's definition.
390
+ :param location: The slot into which to load the labware,
391
+ such as ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
392
+ :type location: int or str or :py:obj:`OFF_DECK`
393
+ :param str label: An optional special name to give the labware. If specified,
394
+ this is how the labware will appear in the run log, Labware Position
395
+ Check, and elsewhere in the Opentrons App and on the touchscreen.
396
+ """
397
+ load_params = self._core.add_labware_definition(labware_def)
398
+
399
+ return self.load_labware(
400
+ load_name=load_params.load_name,
401
+ namespace=load_params.namespace,
402
+ version=load_params.version,
403
+ location=location,
404
+ label=label,
405
+ )
406
+
407
+ @requires_version(2, 0)
408
+ def load_labware(
409
+ self,
410
+ load_name: str,
411
+ location: Union[DeckLocation, OffDeckType],
412
+ label: Optional[str] = None,
413
+ namespace: Optional[str] = None,
414
+ version: Optional[int] = None,
415
+ adapter: Optional[str] = None,
416
+ lid: Optional[str] = None,
417
+ ) -> Labware:
418
+ """Load a labware onto a location.
419
+
420
+ For Opentrons-verified labware, this is a convenient way
421
+ to collapse the two stages of labware initialization (creating
422
+ the labware and adding it to the protocol) into one.
423
+
424
+ This function returns the created and initialized labware for use
425
+ later in the protocol.
426
+
427
+ :param str load_name: A string to use for looking up a labware definition.
428
+ You can find the ``load_name`` for any Opentrons-verified labware on the
429
+ `Labware Library <https://labware.opentrons.com>`__.
430
+
431
+ :param location: Either a :ref:`deck slot <deck-slots>`,
432
+ like ``1``, ``"1"``, or ``"D1"``, or the special value :py:obj:`OFF_DECK`.
433
+
434
+ .. versionchanged:: 2.15
435
+ You can now specify a deck slot as a coordinate, like ``"D1"``.
436
+
437
+ :type location: int or str or :py:obj:`OFF_DECK`
438
+
439
+ :param str label: An optional special name to give the labware. If specified,
440
+ this is how the labware will appear in the run log, Labware Position
441
+ Check, and elsewhere in the Opentrons App and on the touchscreen.
442
+
443
+ :param str namespace: The namespace that the labware definition belongs to.
444
+ If unspecified, the API will automatically search two namespaces:
445
+
446
+ - ``"opentrons"``, to load standard Opentrons labware definitions.
447
+ - ``"custom_beta"``, to load custom labware definitions created with the
448
+ `Custom Labware Creator <https://labware.opentrons.com/create>`__.
449
+
450
+ You might need to specify an explicit ``namespace`` if you have a custom
451
+ definition whose ``load_name`` is the same as an Opentrons-verified
452
+ definition, and you want to explicitly choose one or the other.
453
+
454
+ :param version: The version of the labware definition. You should normally
455
+ leave this unspecified to let ``load_labware()`` choose a version
456
+ automatically.
457
+ :param adapter: An adapter to load the labware on top of. Accepts the same
458
+ values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
459
+ adapter will use the same namespace as the labware, and the API will
460
+ choose the adapter's version automatically.
461
+
462
+ .. versionadded:: 2.15
463
+ :param lid: A lid to load on the top of the main labware. Accepts the same
464
+ values as the ``load_name`` parameter of :py:meth:`.load_lid_stack`. The
465
+ lid will use the same namespace as the labware, and the API will
466
+ choose the adapter's version automatically.
467
+
468
+ .. versionadded:: 2.23
469
+ """
470
+
471
+ if isinstance(location, OffDeckType) and self._api_version < APIVersion(2, 15):
472
+ raise APIVersionError(
473
+ api_element="Loading a labware off-deck",
474
+ until_version="2.15",
475
+ current_version=f"{self._api_version}",
476
+ )
477
+
478
+ load_name = validation.ensure_lowercase_name(load_name)
479
+ load_location: Union[OffDeckType, DeckSlotName, StagingSlotName, LabwareCore]
480
+ if adapter is not None:
481
+ if self._api_version < APIVersion(2, 15):
482
+ raise APIVersionError(
483
+ api_element="Loading a labware on an adapter",
484
+ until_version="2.15",
485
+ current_version=f"{self._api_version}",
486
+ )
487
+ loaded_adapter = self.load_adapter(
488
+ load_name=adapter,
489
+ location=location,
490
+ namespace=namespace,
491
+ )
492
+ load_location = loaded_adapter._core
493
+ elif isinstance(location, OffDeckType):
494
+ load_location = location
495
+ else:
496
+ load_location = validation.ensure_and_convert_deck_slot(
497
+ location, self._api_version, self._core.robot_type
498
+ )
499
+
500
+ labware_core = self._core.load_labware(
501
+ load_name=load_name,
502
+ location=load_location,
503
+ label=label if label is None else str(label),
504
+ namespace=namespace,
505
+ version=version,
506
+ )
507
+
508
+ if lid is not None:
509
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
510
+ raise APIVersionError(
511
+ api_element="Loading a Lid on a Labware",
512
+ until_version="2.23",
513
+ current_version=f"{self._api_version}",
514
+ )
515
+ self._core.load_lid(
516
+ load_name=lid,
517
+ location=labware_core,
518
+ namespace=namespace,
519
+ version=version,
520
+ )
521
+
522
+ labware = Labware(
523
+ core=labware_core,
524
+ api_version=self._api_version,
525
+ protocol_core=self._core,
526
+ core_map=self._core_map,
527
+ )
528
+ self._core_map.add(labware_core, labware)
529
+
530
+ return labware
531
+
532
+ @requires_version(2, 0)
533
+ def load_labware_by_name(
534
+ self,
535
+ load_name: str,
536
+ location: DeckLocation,
537
+ label: Optional[str] = None,
538
+ namespace: Optional[str] = None,
539
+ version: int = 1,
540
+ ) -> Labware:
541
+ """
542
+ .. deprecated:: 2.0
543
+ Use :py:meth:`load_labware` instead.
544
+ """
545
+ logger.warning("load_labware_by_name is deprecated. Use load_labware instead.")
546
+ return self.load_labware(load_name, location, label, namespace, version)
547
+
548
+ @requires_version(2, 15)
549
+ def load_adapter_from_definition(
550
+ self,
551
+ adapter_def: "LabwareDefinition",
552
+ location: Union[DeckLocation, OffDeckType],
553
+ ) -> Labware:
554
+ """Specify the presence of an adapter on the deck.
555
+
556
+ This function loads the adapter definition specified by ``adapter_def``
557
+ to the location specified by ``location``.
558
+
559
+ :param adapter_def: The adapter's labware definition.
560
+ :param location: The slot into which to load the labware,
561
+ such as ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
562
+ :type location: int or str or :py:obj:`OFF_DECK`
563
+ """
564
+ load_params = self._core.add_labware_definition(adapter_def)
565
+
566
+ return self.load_adapter(
567
+ load_name=load_params.load_name,
568
+ namespace=load_params.namespace,
569
+ version=load_params.version,
570
+ location=location,
571
+ )
572
+
573
+ @requires_version(2, 16)
574
+ def load_trash_bin(self, location: DeckLocation) -> TrashBin:
575
+ """Load a trash bin on the deck of a Flex.
576
+
577
+ See :ref:`configure-trash-bin` for details.
578
+
579
+ If you try to load a trash bin on an OT-2, the API will raise an error.
580
+
581
+ :param location: The :ref:`deck slot <deck-slots>` where the trash bin is. The
582
+ location can be any unoccupied slot in column 1 or 3.
583
+
584
+ If you try to load a trash bin in column 2 or 4, the API will raise an error.
585
+ """
586
+ slot_name = validation.ensure_and_convert_deck_slot(
587
+ location,
588
+ api_version=self._api_version,
589
+ robot_type=self._core.robot_type,
590
+ )
591
+ if not isinstance(slot_name, DeckSlotName):
592
+ raise ValueError("Staging areas not permitted for trash bin.")
593
+ addressable_area_name = validation.ensure_and_convert_trash_bin_location(
594
+ location,
595
+ api_version=self._api_version,
596
+ robot_type=self._core.robot_type,
597
+ )
598
+ trash_bin = self._core.load_trash_bin(slot_name, addressable_area_name)
599
+ return trash_bin
600
+
601
+ @requires_version(2, 16)
602
+ def load_waste_chute(
603
+ self,
604
+ ) -> WasteChute:
605
+ """Load the waste chute on the deck of a Flex.
606
+
607
+ See :ref:`configure-waste-chute` for details, including the deck configuration
608
+ variants of the waste chute.
609
+
610
+ The deck plate adapter for the waste chute can only go in slot D3. If you try to
611
+ load another item in slot D3 after loading the waste chute, or vice versa, the
612
+ API will raise an error.
613
+ """
614
+ return self._core.load_waste_chute()
615
+
616
+ @requires_version(2, 15)
617
+ def load_adapter(
618
+ self,
619
+ load_name: str,
620
+ location: Union[DeckLocation, OffDeckType],
621
+ namespace: Optional[str] = None,
622
+ version: Optional[int] = None,
623
+ ) -> Labware:
624
+ """Load an adapter onto a location.
625
+
626
+ For adapters already defined by Opentrons, this is a convenient way
627
+ to collapse the two stages of adapter initialization (creating
628
+ the adapter and adding it to the protocol) into one.
629
+
630
+ This function returns the created and initialized adapter for use
631
+ later in the protocol.
632
+
633
+ :param str load_name: A string to use for looking up a labware definition for the adapter.
634
+ You can find the ``load_name`` for any standard adapter on the Opentrons
635
+ `Labware Library <https://labware.opentrons.com>`_.
636
+
637
+ :param location: Either a :ref:`deck slot <deck-slots>`,
638
+ like ``1``, ``"1"``, or ``"D1"``, or the special value :py:obj:`OFF_DECK`.
639
+
640
+ :type location: int or str or :py:obj:`OFF_DECK`
641
+
642
+ :param str namespace: The namespace that the labware definition belongs to.
643
+ If unspecified, the API will automatically search two namespaces:
644
+
645
+ * ``"opentrons"``, to load standard Opentrons labware definitions.
646
+ * ``"custom_beta"``, to load custom labware definitions created with the
647
+ `Custom Labware Creator <https://labware.opentrons.com/create>`_.
648
+
649
+ You might need to specify an explicit ``namespace`` if you have a custom
650
+ definition whose ``load_name`` is the same as an Opentrons standard
651
+ definition, and you want to explicitly choose one or the other.
652
+
653
+ :param version: The version of the labware definition. You should normally
654
+ leave this unspecified to let ``load_adapter()`` choose a version automatically.
655
+ """
656
+ load_name = validation.ensure_lowercase_name(load_name)
657
+ load_location: Union[OffDeckType, DeckSlotName, StagingSlotName]
658
+ if isinstance(location, OffDeckType):
659
+ load_location = location
660
+ else:
661
+ load_location = validation.ensure_and_convert_deck_slot(
662
+ location, self._api_version, self._core.robot_type
663
+ )
664
+
665
+ labware_core = self._core.load_adapter(
666
+ load_name=load_name,
667
+ location=load_location,
668
+ namespace=namespace,
669
+ version=version,
670
+ )
671
+
672
+ adapter = Labware(
673
+ core=labware_core,
674
+ api_version=self._api_version,
675
+ protocol_core=self._core,
676
+ core_map=self._core_map,
677
+ )
678
+ self._core_map.add(labware_core, adapter)
679
+
680
+ return adapter
681
+
682
+ # TODO(mm, 2023-06-07): Figure out what to do with this, now that the Flex has non-integer
683
+ # slot names and labware can be stacked. https://opentrons.atlassian.net/browse/RLAB-354
684
+ @property
685
+ @requires_version(2, 0)
686
+ def loaded_labwares(self) -> Dict[int, Labware]:
687
+ """Get the labwares that have been loaded into the protocol context.
688
+
689
+ Slots with nothing in them will not be present in the return value.
690
+
691
+ .. note::
692
+
693
+ If a module is present on the deck but no labware has been loaded
694
+ into it with ``module.load_labware()``, there will
695
+ be no entry for that slot in this value. That means you should not
696
+ use ``loaded_labwares`` to determine if a slot is available or not,
697
+ only to get a list of labwares. If you want a data structure of all
698
+ objects on the deck regardless of type, use :py:attr:`deck`.
699
+
700
+
701
+ :returns: Dict mapping deck slot number to labware, sorted in order of
702
+ the locations.
703
+ """
704
+ labware_cores = (
705
+ (core.get_deck_slot(), core) for core in self._core.get_labware_cores()
706
+ )
707
+
708
+ return {
709
+ slot.as_int(): self._core_map.get(core)
710
+ for slot, core in labware_cores
711
+ if slot is not None
712
+ }
713
+
714
+ @requires_version(2, 15)
715
+ def move_labware(
716
+ self,
717
+ labware: Labware,
718
+ new_location: Union[
719
+ DeckLocation, Labware, ModuleTypes, OffDeckType, WasteChute, TrashBin
720
+ ],
721
+ use_gripper: bool = False,
722
+ pick_up_offset: Optional[Mapping[str, float]] = None,
723
+ drop_offset: Optional[Mapping[str, float]] = None,
724
+ ) -> None:
725
+ """Move a loaded labware to a new location.
726
+
727
+ See :ref:`moving-labware` for more details.
728
+
729
+ :param labware: The labware to move. It should be a labware already loaded
730
+ using :py:meth:`load_labware`.
731
+
732
+ :param new_location: Where to move the labware to. This is either:
733
+
734
+ * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
735
+ * A hardware module that's already been loaded on the deck
736
+ with :py:meth:`load_module`.
737
+ * A labware or adapter that's already been loaded on the deck
738
+ with :py:meth:`load_labware` or :py:meth:`load_adapter`.
739
+ * The special constant :py:obj:`OFF_DECK`.
740
+
741
+ :param use_gripper: Whether to use the Flex Gripper for this movement.
742
+
743
+ * If ``True``, use the gripper to perform an automatic
744
+ movement. This will raise an error in an OT-2 protocol.
745
+ * If ``False``, pause protocol execution until the user
746
+ performs the movement. Protocol execution remains paused until
747
+ the user presses **Confirm and resume**.
748
+
749
+ Gripper-only parameters:
750
+
751
+ :param pick_up_offset: Optional x, y, z vector offset to use when picking up labware.
752
+ :param drop_offset: Optional x, y, z vector offset to use when dropping off labware.
753
+
754
+ Before moving a labware to or from a hardware module, make sure that the labware's
755
+ current and new locations are accessible, i.e., open the Thermocycler lid or
756
+ open the Heater-Shaker's labware latch.
757
+ """
758
+
759
+ if not isinstance(labware, Labware):
760
+ raise ValueError(
761
+ f"Expected labware of type 'Labware' but got {type(labware)}."
762
+ )
763
+
764
+ # Ensure that when moving to an absorbance reader that the lid is open
765
+ # todo(mm, 2024-11-08): Unify this with opentrons.protocol_api.core.engine.deck_conflict.
766
+ if isinstance(new_location, AbsorbanceReaderContext):
767
+ if new_location.is_lid_on():
768
+ raise CommandPreconditionViolated(
769
+ f"Cannot move {labware.name} onto the Absorbance Reader Module when its lid is closed."
770
+ )
771
+
772
+ location: Union[
773
+ ModuleCore,
774
+ LabwareCore,
775
+ WasteChute,
776
+ OffDeckType,
777
+ DeckSlotName,
778
+ StagingSlotName,
779
+ TrashBin,
780
+ ]
781
+ if isinstance(new_location, (Labware, ModuleContext)):
782
+ location = new_location._core
783
+ elif isinstance(new_location, (OffDeckType, WasteChute)):
784
+ location = new_location
785
+ elif isinstance(new_location, TrashBin):
786
+ if labware._core.is_lid():
787
+ location = new_location
788
+ else:
789
+ raise LabwareMovementNotAllowedError(
790
+ "Can only dispose of tips and Lid-type labware in a Trash Bin. Did you mean to use a Waste Chute?"
791
+ )
792
+ else:
793
+ location = validation.ensure_and_convert_deck_slot(
794
+ new_location, self._api_version, self._core.robot_type
795
+ )
796
+
797
+ _pick_up_offset = (
798
+ validation.ensure_valid_labware_offset_vector(pick_up_offset)
799
+ if pick_up_offset
800
+ else None
801
+ )
802
+ _drop_offset = (
803
+ validation.ensure_valid_labware_offset_vector(drop_offset)
804
+ if drop_offset
805
+ else None
806
+ )
807
+ with publish_context(
808
+ broker=self.broker,
809
+ command=cmds.move_labware(
810
+ # This needs to be called from protocol context and not the command for import loop reasons
811
+ text=stringify_labware_movement_command(
812
+ labware, new_location, use_gripper
813
+ )
814
+ ),
815
+ ):
816
+ self._core.move_labware(
817
+ labware_core=labware._core,
818
+ new_location=location,
819
+ use_gripper=use_gripper,
820
+ pause_for_manual_move=True,
821
+ pick_up_offset=_pick_up_offset,
822
+ drop_offset=_drop_offset,
823
+ )
824
+
825
+ @requires_version(2, 0)
826
+ def load_module(
827
+ self,
828
+ module_name: str,
829
+ location: Optional[DeckLocation] = None,
830
+ configuration: Optional[str] = None,
831
+ ) -> ModuleTypes:
832
+ """Load a module onto the deck, given its name or model.
833
+
834
+ This is the function to call to use a module in your protocol, like
835
+ :py:meth:`load_instrument` is the method to call to use an instrument
836
+ in your protocol. It returns the created and initialized module
837
+ context, which will be a different class depending on the kind of
838
+ module loaded.
839
+
840
+ After loading modules, you can access a map of deck positions to loaded modules
841
+ with :py:attr:`loaded_modules`.
842
+
843
+ :param str module_name: The name or model of the module.
844
+ See :ref:`available_modules` for possible values.
845
+
846
+ :param location: The location of the module.
847
+
848
+ This is usually the name or number of the slot on the deck where you
849
+ will be placing the module, like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
850
+
851
+ The Thermocycler is only valid in one deck location.
852
+ You don't have to specify a location when loading it, but if you do,
853
+ it must be ``7``, ``"7"``, or ``"B1"``. See :ref:`thermocycler-module`.
854
+
855
+ .. versionchanged:: 2.15
856
+ You can now specify a deck slot as a coordinate, like ``"D1"``.
857
+
858
+ :param configuration: Configure a Thermocycler to be in the ``semi`` position.
859
+ This parameter does not work. Do not use it.
860
+
861
+ .. versionchanged:: 2.14
862
+ This parameter dangerously modified the protocol's geometry system,
863
+ and it didn't function properly, so it was removed.
864
+
865
+ :type location: str or int or None
866
+ :returns: The loaded and initialized module---a
867
+ :py:class:`HeaterShakerContext`,
868
+ :py:class:`MagneticBlockContext`,
869
+ :py:class:`MagneticModuleContext`,
870
+ :py:class:`TemperatureModuleContext`, or
871
+ :py:class:`ThermocyclerContext`,
872
+ depending on what you requested with ``module_name``.
873
+
874
+ .. versionchanged:: 2.13
875
+ Added ``HeaterShakerContext`` return value.
876
+
877
+ .. versionchanged:: 2.15
878
+ Added ``MagneticBlockContext`` return value.
879
+
880
+ .. TODO uncomment when 2.23 is ready
881
+ versionchanged:: 2.23
882
+ Added ``FlexStackerModuleContext`` return value.
883
+ """
884
+ if configuration:
885
+ if self._api_version < APIVersion(2, 4):
886
+ raise APIVersionError(
887
+ api_element="Thermocycler parameters",
888
+ until_version="2.4",
889
+ current_version=f"{self._api_version}",
890
+ )
891
+ if self._api_version >= ENGINE_CORE_API_VERSION:
892
+ raise UnsupportedAPIError(
893
+ api_element="The configuration parameter of load_module",
894
+ since_version=f"{ENGINE_CORE_API_VERSION}",
895
+ current_version=f"{self._api_version}",
896
+ )
897
+
898
+ requested_model = validation.ensure_module_model(module_name)
899
+ if isinstance(
900
+ requested_model, MagneticBlockModel
901
+ ) and self._api_version < APIVersion(2, 15):
902
+ raise APIVersionError(
903
+ api_element=f"Module of type {module_name}",
904
+ until_version="2.15",
905
+ current_version=f"{self._api_version}",
906
+ )
907
+ if isinstance(
908
+ requested_model, AbsorbanceReaderModel
909
+ ) and self._api_version < APIVersion(2, 21):
910
+ raise APIVersionError(
911
+ api_element=f"Module of type {module_name}",
912
+ until_version="2.21",
913
+ current_version=f"{self._api_version}",
914
+ )
915
+ if (
916
+ isinstance(requested_model, FlexStackerModuleModel)
917
+ and self._api_version < validation.FLEX_STACKER_VERSION_GATE
918
+ ):
919
+ raise APIVersionError(
920
+ api_element=f"Module of type {module_name}",
921
+ until_version=str(validation.FLEX_STACKER_VERSION_GATE),
922
+ current_version=f"{self._api_version}",
923
+ )
924
+
925
+ deck_slot = (
926
+ None
927
+ if location is None
928
+ else validation.ensure_and_convert_deck_slot(
929
+ location, self._api_version, self._core.robot_type
930
+ )
931
+ )
932
+ if isinstance(deck_slot, StagingSlotName):
933
+ # flex stacker modules can only be loaded into staging slot inside a protocol
934
+ if isinstance(requested_model, FlexStackerModuleModel):
935
+ deck_slot = validation.convert_flex_stacker_load_slot(deck_slot)
936
+ else:
937
+ raise ValueError(f"Cannot load {module_name} onto a staging slot.")
938
+
939
+ module_core = self._core.load_module(
940
+ model=requested_model,
941
+ deck_slot=deck_slot,
942
+ configuration=configuration,
943
+ )
944
+
945
+ module_context = _create_module_context(
946
+ module_core=module_core,
947
+ protocol_core=self._core,
948
+ core_map=self._core_map,
949
+ broker=self._broker,
950
+ api_version=self._api_version,
951
+ )
952
+
953
+ self._core_map.add(module_core, module_context)
954
+
955
+ return module_context
956
+
957
+ # TODO(mm, 2023-06-07): Figure out what to do with this, now that the Flex has non-integer
958
+ # slot names and labware can be stacked. https://opentrons.atlassian.net/browse/RLAB-354
959
+ @property
960
+ @requires_version(2, 0)
961
+ def loaded_modules(self) -> Dict[int, ModuleTypes]:
962
+ """Get the modules loaded into the protocol context.
963
+
964
+ This is a map of deck positions to modules loaded by previous calls to
965
+ :py:meth:`load_module`. It does not reflect what modules are actually attached
966
+ to the robot. For example, if the robot has a Magnetic Module and a Temperature
967
+ Module attached, but the protocol has only loaded the Temperature Module with
968
+ :py:meth:`load_module`, only the Temperature Module will be included in
969
+ ``loaded_modules``.
970
+
971
+ :returns: Dict mapping slot name to module contexts. The elements may not be
972
+ ordered by slot number.
973
+ """
974
+ return {
975
+ core.get_deck_slot().as_int(): self._core_map.get(core)
976
+ for core in self._core.get_module_cores()
977
+ }
978
+
979
+ @requires_version(2, 0)
980
+ def load_instrument(
981
+ self,
982
+ instrument_name: str,
983
+ mount: Union[Mount, str, None] = None,
984
+ tip_racks: Optional[List[Labware]] = None,
985
+ replace: bool = False,
986
+ liquid_presence_detection: Optional[bool] = None,
987
+ ) -> InstrumentContext:
988
+ """Load a specific instrument for use in the protocol.
989
+
990
+ When analyzing the protocol on the robot, instruments loaded with this method
991
+ are compared against the instruments attached to the robot. You won't be able to
992
+ start the protocol until the correct instruments are attached and calibrated.
993
+
994
+ Currently, this method only loads pipettes. You do not need to load the Flex
995
+ Gripper to use it in protocols. See :ref:`automatic-manual-moves`.
996
+
997
+ :param str instrument_name: The instrument to load. See :ref:`new-pipette-models`
998
+ for the valid values.
999
+ :param mount: The mount where the instrument should be attached.
1000
+ This can either be an instance of :py:class:`.types.Mount` or one
1001
+ of the strings ``"left"`` or ``"right"``. When loading a Flex
1002
+ 96-Channel Pipette (``instrument_name="flex_96channel_1000"``),
1003
+ you can leave this unspecified, since it always occupies both
1004
+ mounts; if you do specify a value, it will be ignored.
1005
+ :type mount: types.Mount or str or ``None``
1006
+ :param tip_racks: A list of tip racks from which to pick tips when calling
1007
+ :py:meth:`.InstrumentContext.pick_up_tip` without arguments.
1008
+ :type tip_racks: List[:py:class:`.Labware`]
1009
+ :param bool replace: If ``True``, replace the currently loaded instrument in
1010
+ ``mount``, if any. This is intended for :ref:`advanced
1011
+ control <advanced-control>` applications. You cannot
1012
+ replace an instrument in the middle of a protocol being run
1013
+ from the Opentrons App or touchscreen.
1014
+ :param bool liquid_presence_detection: If ``True``, enable automatic
1015
+ :ref:`liquid presence detection <lpd>` for Flex 1-, 8-, or 96-channel pipettes.
1016
+
1017
+ .. versionadded:: 2.20
1018
+ """
1019
+ instrument_name = validation.ensure_lowercase_name(instrument_name)
1020
+ checked_instrument_name = validation.ensure_pipette_name(instrument_name)
1021
+ checked_mount = validation.ensure_mount_for_pipette(
1022
+ mount, checked_instrument_name
1023
+ )
1024
+
1025
+ is_96_channel = checked_instrument_name in [
1026
+ PipetteNameType.P1000_96,
1027
+ PipetteNameType.P200_96,
1028
+ ]
1029
+
1030
+ tip_racks = tip_racks or []
1031
+
1032
+ # TODO (tz, 9-12-23): move validation into PE
1033
+ on_right_mount = self._instruments[Mount.RIGHT]
1034
+ if is_96_channel and on_right_mount is not None:
1035
+ raise RuntimeError(
1036
+ f"Instrument already present on right:"
1037
+ f" {on_right_mount.name}. In order to load a 96-channel pipette, both mounts need to be available."
1038
+ )
1039
+
1040
+ existing_instrument = self._instruments[checked_mount]
1041
+ if existing_instrument is not None and not replace:
1042
+ # TODO(mc, 2022-08-25): create specific exception type
1043
+ raise RuntimeError(
1044
+ f"Instrument already present on {checked_mount.name.lower()}:"
1045
+ f" {existing_instrument.name}"
1046
+ )
1047
+
1048
+ logger.info(
1049
+ f"Loading {checked_instrument_name} on {checked_mount.name.lower()} mount"
1050
+ )
1051
+
1052
+ if (
1053
+ self._api_version < APIVersion(2, 20)
1054
+ and liquid_presence_detection is not None
1055
+ ):
1056
+ raise APIVersionError(
1057
+ api_element="Liquid Presence Detection",
1058
+ until_version="2.20",
1059
+ current_version=f"{self._api_version}",
1060
+ )
1061
+ if (
1062
+ self._core.robot_type != "OT-3 Standard"
1063
+ and liquid_presence_detection is not None
1064
+ ):
1065
+ raise RobotTypeError(
1066
+ "Liquid presence detection only available on Flex robot."
1067
+ )
1068
+ instrument_core = self._core.load_instrument(
1069
+ instrument_name=checked_instrument_name,
1070
+ mount=checked_mount,
1071
+ liquid_presence_detection=liquid_presence_detection or False,
1072
+ )
1073
+
1074
+ for tip_rack in tip_racks:
1075
+ instrument_support.validate_tiprack(
1076
+ instrument_name=instrument_core.get_pipette_name(),
1077
+ tip_rack=tip_rack,
1078
+ log=logger,
1079
+ )
1080
+
1081
+ trash: Optional[Union[Labware, TrashBin]]
1082
+ try:
1083
+ trash = self.fixed_trash
1084
+ except (NoTrashDefinedError, UnsupportedAPIError):
1085
+ trash = None
1086
+
1087
+ instrument = InstrumentContext(
1088
+ core=instrument_core,
1089
+ protocol_core=self._core,
1090
+ broker=self._broker,
1091
+ api_version=self._api_version,
1092
+ tip_racks=tip_racks,
1093
+ trash=trash,
1094
+ requested_as=instrument_name,
1095
+ core_map=self._core_map,
1096
+ )
1097
+
1098
+ self._instruments[checked_mount] = instrument
1099
+
1100
+ return instrument
1101
+
1102
+ @property
1103
+ @requires_version(2, 0)
1104
+ def loaded_instruments(self) -> Dict[str, InstrumentContext]:
1105
+ """Get the instruments that have been loaded into the protocol.
1106
+
1107
+ This is a map of mount name to instruments previously loaded with
1108
+ :py:meth:`load_instrument`. It does not reflect what instruments are actually
1109
+ installed on the robot. For example, if the robot has instruments installed on
1110
+ both mounts but your protocol has only loaded one of them with
1111
+ :py:meth:`load_instrument`, the unused one will not be included in
1112
+ ``loaded_instruments``.
1113
+
1114
+ :returns: A dict mapping mount name (``"left"`` or ``"right"``) to the
1115
+ instrument in that mount. If a mount has no loaded instrument, that key
1116
+ will be missing from the dict.
1117
+ """
1118
+ return {
1119
+ mount.name.lower(): instr
1120
+ for mount, instr in self._instruments.items()
1121
+ if instr
1122
+ }
1123
+
1124
+ @publish(command=cmds.pause)
1125
+ @requires_version(2, 0)
1126
+ def pause(self, msg: Optional[str] = None) -> None:
1127
+ """Pause execution of the protocol until it's resumed.
1128
+
1129
+ A human can resume the protocol in the Opentrons App or on the touchscreen.
1130
+
1131
+ .. note::
1132
+ In Python Protocol API version 2.13 and earlier, the pause will only
1133
+ take effect on the next function call that involves moving the robot.
1134
+
1135
+ :param str msg: An optional message to show in the run log entry for the pause step.
1136
+ """
1137
+ self._core.pause(msg=msg)
1138
+
1139
+ @publish(command=cmds.resume)
1140
+ @requires_version(2, 0)
1141
+ def resume(self) -> None:
1142
+ """Resume the protocol after :py:meth:`pause`.
1143
+
1144
+ .. deprecated:: 2.12
1145
+ The Python Protocol API supports no safe way for a protocol to resume itself.
1146
+ If you're looking for a way for your protocol to resume automatically
1147
+ after a period of time, use :py:meth:`delay`.
1148
+ """
1149
+ if self._api_version >= ENGINE_CORE_API_VERSION:
1150
+ raise UnsupportedAPIError(
1151
+ api_element="A Python Protocol safely resuming itself after a pause",
1152
+ since_version=f"{ENGINE_CORE_API_VERSION}",
1153
+ current_version=f"{self._api_version}",
1154
+ extra_message="To wait automatically for a period of time, use ProtocolContext.delay().",
1155
+ )
1156
+
1157
+ # TODO(mc, 2023-02-13): this assert should be enough for mypy
1158
+ # investigate if upgrading mypy allows the `cast` to be removed
1159
+ assert isinstance(self._core, LegacyProtocolCore)
1160
+ cast(LegacyProtocolCore, self._core).resume()
1161
+
1162
+ @publish(command=cmds.comment)
1163
+ @requires_version(2, 0)
1164
+ def comment(self, msg: str) -> None:
1165
+ """
1166
+ Add a user-readable message to the run log.
1167
+
1168
+ The message is visible anywhere you can view the run log, including the Opentrons App and the touchscreen on Flex.
1169
+
1170
+ .. note::
1171
+
1172
+ The value of the message is computed during protocol analysis,
1173
+ so ``comment()`` can't communicate real-time information during the
1174
+ actual protocol run.
1175
+ """
1176
+ self._core.comment(msg=msg)
1177
+
1178
+ @publish(command=cmds.delay)
1179
+ @requires_version(2, 0)
1180
+ def delay(
1181
+ self,
1182
+ seconds: float = 0,
1183
+ minutes: float = 0,
1184
+ msg: Optional[str] = None,
1185
+ ) -> None:
1186
+ """Delay protocol execution for a specific amount of time.
1187
+
1188
+ :param float seconds: The time to delay in seconds.
1189
+ :param float minutes: The time to delay in minutes.
1190
+
1191
+ If both ``seconds`` and ``minutes`` are specified, they will be added together.
1192
+ """
1193
+ delay_time = seconds + minutes * 60
1194
+ self._core.delay(seconds=delay_time, msg=msg)
1195
+
1196
+ @requires_version(2, 0)
1197
+ def home(self) -> None:
1198
+ """Home the movement system of the robot."""
1199
+ self._core.home()
1200
+
1201
+ @property
1202
+ def location_cache(self) -> Optional[Union[Location, TrashBin, WasteChute]]:
1203
+ """The cache used by the robot to determine where it last was.
1204
+
1205
+ .. versionchanged:: 2.24
1206
+ Can return a ``TrashBin`` or ``WasteChute`` object.
1207
+ """
1208
+ last_loc = self._core.get_last_location()
1209
+ if isinstance(last_loc, Location) or self._api_version >= APIVersion(2, 24):
1210
+ return last_loc
1211
+ return None
1212
+
1213
+ @location_cache.setter
1214
+ def location_cache(self, loc: Optional[Location]) -> None:
1215
+ self._core.set_last_location(loc)
1216
+
1217
+ @property
1218
+ @requires_version(2, 0)
1219
+ def deck(self) -> Deck:
1220
+ """An interface to provide information about what's currently loaded on the deck.
1221
+ This object is useful for determining if a slot on the deck is free.
1222
+
1223
+ This object behaves like a dictionary whose keys are the :ref:`deck slot <deck-slots>` names.
1224
+ For instance, ``deck[1]``, ``deck["1"]``, and ``deck["D1"]``
1225
+ will all return the object loaded in the front-left slot.
1226
+
1227
+ The value for each key depends on what is loaded in the slot:
1228
+ - A :py:obj:`~opentrons.protocol_api.Labware` if the slot contains a labware.
1229
+ - A module context if the slot contains a hardware module.
1230
+ - ``None`` if the slot doesn't contain anything.
1231
+
1232
+ A module that occupies multiple slots is set as the value for all of the
1233
+ relevant slots. Currently, the only multiple-slot module is the Thermocycler.
1234
+ When loaded, the :py:class:`ThermocyclerContext` object is the value for
1235
+ ``deck`` keys ``"A1"`` and ``"B1"`` on Flex, and ``7``, ``8``, ``10``, and
1236
+ ``11`` on OT-2. In API version 2.13 and earlier, only slot 7 keyed to the
1237
+ Thermocycler object, and slots 8, 10, and 11 keyed to ``None``.
1238
+
1239
+ Rather than filtering the objects in the deck map yourself,
1240
+ you can also use :py:attr:`loaded_labwares` to get a dict of labwares
1241
+ and :py:attr:`loaded_modules` to get a dict of modules.
1242
+
1243
+ For :ref:`advanced-control` *only*, you can delete an element of the ``deck`` dict.
1244
+ This only works for deck slots that contain labware objects. For example, if slot
1245
+ 1 contains a labware, ``del protocol.deck["1"]`` will free the slot so you can
1246
+ load another labware there.
1247
+
1248
+ .. warning::
1249
+ Deleting labware from a deck slot does not pause the protocol. Subsequent
1250
+ commands continue immediately. If you need to physically move the labware to
1251
+ reflect the new deck state, add a :py:meth:`.pause` or use
1252
+ :py:meth:`.move_labware` instead.
1253
+
1254
+ .. versionchanged:: 2.14
1255
+ Includes the Thermocycler in all of the slots it occupies.
1256
+
1257
+ .. versionchanged:: 2.15
1258
+ ``del`` sets the corresponding labware's location to ``OFF_DECK``.
1259
+ """
1260
+ return self._deck
1261
+
1262
+ @property
1263
+ @requires_version(2, 0)
1264
+ def fixed_trash(self) -> Union[Labware, TrashBin]:
1265
+ """The trash fixed to slot 12 of an OT-2's deck.
1266
+
1267
+ In API version 2.15 and earlier, the fixed trash is a :py:class:`.Labware` object with one well. Access it like labware in your protocol. For example, ``protocol.fixed_trash["A1"]``.
1268
+
1269
+ In API version 2.15 only, Flex protocols have a fixed trash in slot A3.
1270
+
1271
+ In API version 2.16 and later, the fixed trash only exists in OT-2 protocols. It is a :py:class:`.TrashBin` object, which doesn't have any wells. Trying to access ``fixed_trash`` in a Flex protocol will raise an error. See :ref:`configure-trash-bin` for details on using the movable trash in Flex protocols.
1272
+
1273
+ .. versionchanged:: 2.16
1274
+ Returns a ``TrashBin`` object.
1275
+ """
1276
+ if self._api_version >= APIVersion(2, 16):
1277
+ if self._core.robot_type == "OT-3 Standard":
1278
+ raise UnsupportedAPIError(
1279
+ api_element="Fixed Trash",
1280
+ since_version="2.16",
1281
+ current_version=f"{self._api_version}",
1282
+ extra_message="Fixed trash is no longer supported on Flex protocols.",
1283
+ )
1284
+ disposal_locations = self._core.get_disposal_locations()
1285
+ if len(disposal_locations) == 0:
1286
+ raise NoTrashDefinedError(
1287
+ "No trash container has been defined in this protocol."
1288
+ )
1289
+ if isinstance(disposal_locations[0], TrashBin):
1290
+ return disposal_locations[0]
1291
+
1292
+ fixed_trash = self._core_map.get(self._core.fixed_trash)
1293
+ if fixed_trash is None:
1294
+ raise NoTrashDefinedError(
1295
+ "No trash container has been defined in this protocol."
1296
+ )
1297
+
1298
+ return fixed_trash
1299
+
1300
+ def _load_fixed_trash(self) -> None:
1301
+ fixed_trash_core = self._core.fixed_trash
1302
+ if fixed_trash_core is not None:
1303
+ fixed_trash = Labware(
1304
+ core=fixed_trash_core,
1305
+ api_version=self._api_version,
1306
+ protocol_core=self._core,
1307
+ core_map=self._core_map,
1308
+ )
1309
+ self._core_map.add(fixed_trash_core, fixed_trash)
1310
+
1311
+ @requires_version(2, 5)
1312
+ def set_rail_lights(self, on: bool) -> None:
1313
+ """
1314
+ Controls the robot's ambient lighting (rail lights).
1315
+
1316
+ :param bool on: If ``True``, turn on the lights; otherwise, turn them off.
1317
+ """
1318
+ self._core.set_rail_lights(on=on)
1319
+
1320
+ @requires_version(2, 14)
1321
+ def define_liquid(
1322
+ self,
1323
+ name: str,
1324
+ description: Union[str, None, _Unset] = _Unset(),
1325
+ display_color: Union[str, None, _Unset] = _Unset(),
1326
+ ) -> Liquid:
1327
+ # This first line of the docstring overrides the method signature in our public
1328
+ # docs, which would otherwise have the `_Unset()`s expanded to a bunch of junk.
1329
+ """
1330
+ define_liquid(self, name: str, description: Optional[str] = None, display_color: Optional[str] = None)
1331
+
1332
+ Define a liquid within a protocol.
1333
+
1334
+ :param str name: A human-readable name for the liquid.
1335
+ :param Optional[str] description: An optional description of the liquid.
1336
+ :param Optional[str] display_color: An optional hex color code, with hash included,
1337
+ to represent the specified liquid. For example, ``"#48B1FA"``.
1338
+ Standard three-value, four-value, six-value, and eight-value syntax are all
1339
+ acceptable.
1340
+
1341
+ :return: A :py:class:`~opentrons.protocol_api.Liquid` object representing the specified liquid.
1342
+
1343
+ .. versionchanged:: 2.20
1344
+ You can now omit the ``description`` and ``display_color`` arguments.
1345
+ Formerly, when you didn't want to provide values, you had to supply
1346
+ ``description=None`` and ``display_color=None`` explicitly.
1347
+ """
1348
+ desc_and_display_color_omittable_since = APIVersion(2, 20)
1349
+ if isinstance(description, _Unset):
1350
+ if self._api_version < desc_and_display_color_omittable_since:
1351
+ raise APIVersionError(
1352
+ api_element="Calling `define_liquid()` without a `description`",
1353
+ current_version=str(self._api_version),
1354
+ until_version=str(desc_and_display_color_omittable_since),
1355
+ extra_message="Use a newer API version or explicitly supply `description=None`.",
1356
+ )
1357
+ else:
1358
+ description = None
1359
+ if isinstance(display_color, _Unset):
1360
+ if self._api_version < desc_and_display_color_omittable_since:
1361
+ raise APIVersionError(
1362
+ api_element="Calling `define_liquid()` without a `display_color`",
1363
+ current_version=str(self._api_version),
1364
+ until_version=str(desc_and_display_color_omittable_since),
1365
+ extra_message="Use a newer API version or explicitly supply `display_color=None`.",
1366
+ )
1367
+ else:
1368
+ display_color = None
1369
+
1370
+ return self._core.define_liquid(
1371
+ name=name,
1372
+ description=description,
1373
+ display_color=display_color,
1374
+ )
1375
+
1376
+ @requires_version(2, 24)
1377
+ def get_liquid_class(
1378
+ self,
1379
+ name: str,
1380
+ ) -> LiquidClass:
1381
+ """
1382
+ Get an instance of an Opentrons-verified liquid class for use in a Flex protocol.
1383
+
1384
+ :param name: Name of an Opentrons-verified liquid class. Must be one of:
1385
+
1386
+ - ``"water"``: an Opentrons-verified liquid class based on deionized water.
1387
+ - ``"glycerol_50"``: an Opentrons-verified liquid class for viscous liquid. Based on 50% glycerol.
1388
+ - ``"ethanol_80"``: an Opentrons-verified liquid class for volatile liquid. Based on 80% ethanol.
1389
+
1390
+ :raises: ``LiquidClassDefinitionDoesNotExist``: if the specified liquid class does not exist.
1391
+
1392
+ :returns: A new LiquidClass object.
1393
+ """
1394
+ return self._core.get_liquid_class(name=name, version=DEFAULT_LC_VERSION)
1395
+
1396
+ @requires_version(2, 24)
1397
+ def define_liquid_class(
1398
+ self,
1399
+ name: str,
1400
+ properties: Dict[str, Dict[str, TransferPropertiesDict]],
1401
+ base_liquid_class: Optional[LiquidClass] = None,
1402
+ display_name: Optional[str] = None,
1403
+ ) -> LiquidClass:
1404
+ """Define a custom liquid class, either based on an existing liquid class, or create a completely new one.
1405
+
1406
+ :param name: The name to give to the new liquid class. Cannot use the name of an Opentrons-verified liquid class.
1407
+ :param properties: A dict of transfer properties for pipette and tip combinations to use for liquid class transfers. The nested dictionary must have top-level keys corresponding to pipette load names and second-level keys corresponding to compatible tip rack load names. Further nested key–value pairs should be as specified in ``TransferPropertiesDict``. See the `liquid class type definitions <https://github.com/Opentrons/opentrons/blob/edge/shared-data/python/opentrons_shared_data/liquid_classes/types.py>`_.
1408
+
1409
+ :param base_liquid_class: An existing liquid class object to base the newly defined liquid class on. The specified ``transfer_properties`` will override any existing properties for the specified pipette and tip combinations. All other properties will remain the same as those in the base class.
1410
+
1411
+ :param display_name: An optional name for the liquid class. Defaults to the title-case ``name`` if a display name isn't provided.
1412
+
1413
+ :returns: A new LiquidClass object.
1414
+ """
1415
+ if definition_exists(name, DEFAULT_LC_VERSION):
1416
+ raise ValueError(
1417
+ f"Liquid class named {name} already exists. Please specify a different name."
1418
+ )
1419
+ new_liquid_class: LiquidClass
1420
+ if base_liquid_class:
1421
+ # If base liquid is provided, copy to new class
1422
+ # and replace the entries mentioned in transfer props arg
1423
+ new_liquid_class = deepcopy(base_liquid_class)
1424
+ else:
1425
+ new_liquid_class = LiquidClass.create_from(
1426
+ name=name,
1427
+ display_name=display_name or name.title(),
1428
+ by_pipette_setting={},
1429
+ )
1430
+ for pipette, by_tiprack_props in properties.items():
1431
+ for tiprack, transfer_props in by_tiprack_props.items():
1432
+ new_liquid_class.update_for(
1433
+ pipette=pipette,
1434
+ tip_rack=tiprack,
1435
+ transfer_properties=build_transfer_properties(
1436
+ transfer_properties=SharedTransferProperties.model_validate(
1437
+ transfer_props
1438
+ )
1439
+ ),
1440
+ )
1441
+ return new_liquid_class
1442
+
1443
+ @property
1444
+ @requires_version(2, 5)
1445
+ def rail_lights_on(self) -> bool:
1446
+ """Returns ``True`` if the robot's ambient lighting is on."""
1447
+ return self._core.get_rail_lights_on()
1448
+
1449
+ @property
1450
+ @requires_version(2, 5)
1451
+ def door_closed(self) -> bool:
1452
+ """Returns ``True`` if the front door of the robot is closed."""
1453
+ return self._core.door_closed()
1454
+
1455
+ @requires_version(2, 23)
1456
+ def load_lid_stack(
1457
+ self,
1458
+ load_name: str,
1459
+ location: Union[DeckLocation, Labware],
1460
+ quantity: int,
1461
+ adapter: Optional[str] = None,
1462
+ namespace: Optional[str] = None,
1463
+ version: Optional[int] = None,
1464
+ ) -> Labware:
1465
+ """
1466
+ Load a stack of Opentrons Tough Auto-Sealing Lids onto a valid deck location or adapter.
1467
+
1468
+ :param str load_name: A string to use for looking up a lid definition.
1469
+ You can find the ``load_name`` for any compatible lid on the Opentrons
1470
+ `Labware Library <https://labware.opentrons.com>`_.
1471
+ :param location: Either a :ref:`deck slot <deck-slots>`,
1472
+ like ``1``, ``"1"``, or ``"D1"``, or a valid Opentrons Adapter.
1473
+ :param int quantity: The quantity of lids to be loaded in the stack.
1474
+ :param adapter: An adapter to load the lid stack on top of. Accepts the same
1475
+ values as the ``load_name`` parameter of :py:meth:`.load_adapter`. The
1476
+ adapter will use the same namespace as the lid labware, and the API will
1477
+ choose the adapter's version automatically.
1478
+ :param str namespace: The namespace that the lid labware definition belongs to.
1479
+ If unspecified, the API will automatically search two namespaces:
1480
+
1481
+ - ``"opentrons"``, to load standard Opentrons labware definitions.
1482
+ - ``"custom_beta"``, to load custom labware definitions created with the
1483
+ `Custom Labware Creator <https://labware.opentrons.com/create>`__.
1484
+
1485
+ You might need to specify an explicit ``namespace`` if you have a custom
1486
+ definition whose ``load_name`` is the same as an Opentrons-verified
1487
+ definition, and you want to explicitly choose one or the other.
1488
+
1489
+ :param version: The version of the labware definition. You should normally
1490
+ leave this unspecified to let ``load_lid_stack()`` choose a version
1491
+ automatically.
1492
+
1493
+ :return: The initialized and loaded labware object representing the lid stack.
1494
+
1495
+ .. versionadded:: 2.23
1496
+
1497
+ """
1498
+ if self._api_version < validation.LID_STACK_VERSION_GATE:
1499
+ raise APIVersionError(
1500
+ api_element="Loading a Lid Stack",
1501
+ until_version="2.23",
1502
+ current_version=f"{self._api_version}",
1503
+ )
1504
+
1505
+ load_location: Union[DeckSlotName, StagingSlotName, LabwareCore]
1506
+ if isinstance(location, Labware):
1507
+ load_location = location._core
1508
+ else:
1509
+ load_location = validation.ensure_and_convert_deck_slot(
1510
+ location, self._api_version, self._core.robot_type
1511
+ )
1512
+
1513
+ if adapter is not None:
1514
+ if isinstance(load_location, DeckSlotName) or isinstance(
1515
+ load_location, StagingSlotName
1516
+ ):
1517
+ loaded_adapter = self.load_adapter(
1518
+ load_name=adapter,
1519
+ location=load_location.value,
1520
+ namespace=namespace,
1521
+ )
1522
+ load_location = loaded_adapter._core
1523
+ else:
1524
+ raise ValueError(
1525
+ "Location cannot be a Labware or Adapter when the 'adapter' field is not None."
1526
+ )
1527
+
1528
+ load_name = validation.ensure_lowercase_name(load_name)
1529
+
1530
+ result = self._core.load_lid_stack(
1531
+ load_name=load_name,
1532
+ location=load_location,
1533
+ quantity=quantity,
1534
+ namespace=namespace,
1535
+ version=version,
1536
+ )
1537
+
1538
+ labware = Labware(
1539
+ core=result,
1540
+ api_version=self._api_version,
1541
+ protocol_core=self._core,
1542
+ core_map=self._core_map,
1543
+ )
1544
+ return labware
1545
+
1546
+ @requires_version(2, 23)
1547
+ def move_lid(
1548
+ self,
1549
+ source_location: Union[DeckLocation, Labware],
1550
+ new_location: Union[DeckLocation, Labware, OffDeckType, WasteChute, TrashBin],
1551
+ use_gripper: bool = False,
1552
+ pick_up_offset: Optional[Mapping[str, float]] = None,
1553
+ drop_offset: Optional[Mapping[str, float]] = None,
1554
+ ) -> Labware | None:
1555
+ """Move a compatible lid from a valid source to a new location. Can return a lid stack if one is created.
1556
+
1557
+ :param source_location: The lid's starting location. This is either:
1558
+
1559
+ * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
1560
+ * A labware or adapter that's already been loaded on the deck
1561
+ with :py:meth:`load_labware` or :py:meth:`load_adapter`.
1562
+ * A lid stack that's already been loaded on the deck with
1563
+ with :py:meth:`load_lid_stack`.
1564
+
1565
+ :param new_location: Where to move the lid to. This is either:
1566
+
1567
+ * A deck slot like ``1``, ``"1"``, or ``"D1"``. See :ref:`deck-slots`.
1568
+ * A hardware module that's already been loaded on the deck
1569
+ with :py:meth:`load_module`.
1570
+ * A labware or adapter that's already been loaded on the deck
1571
+ with :py:meth:`load_labware` or :py:meth:`load_adapter`.
1572
+ * The special constant :py:obj:`OFF_DECK`.
1573
+
1574
+ :param use_gripper: Whether to use the Flex Gripper to move the lid.
1575
+
1576
+ * If ``True``, use the gripper to perform an automatic
1577
+ movement. This will raise an error in an OT-2 protocol.
1578
+ * If ``False``, pause protocol execution until the user
1579
+ performs the movement. Protocol execution remains paused until
1580
+ the user presses **Confirm and resume**.
1581
+
1582
+ Gripper-only parameters:
1583
+
1584
+ :param pick_up_offset: Optional x, y, z vector offset to use when picking up a lid.
1585
+ :param drop_offset: Optional x, y, z vector offset to use when dropping off a lid.
1586
+
1587
+ Before moving a lid to or from a labware in a hardware module, make sure that the
1588
+ labware's current and new locations are accessible, i.e., open the Thermocycler lid
1589
+ or open the Heater-Shaker's labware latch.
1590
+
1591
+ .. versionadded:: 2.23
1592
+
1593
+ """
1594
+ source: Union[LabwareCore, DeckSlotName, StagingSlotName]
1595
+ if isinstance(source_location, Labware):
1596
+ source = source_location._core
1597
+ else:
1598
+ source = validation.ensure_and_convert_deck_slot(
1599
+ source_location, self._api_version, self._core.robot_type
1600
+ )
1601
+
1602
+ destination: Union[
1603
+ ModuleCore,
1604
+ LabwareCore,
1605
+ WasteChute,
1606
+ OffDeckType,
1607
+ DeckSlotName,
1608
+ StagingSlotName,
1609
+ TrashBin,
1610
+ ]
1611
+ if isinstance(new_location, Labware):
1612
+ destination = new_location._core
1613
+ elif isinstance(new_location, (OffDeckType, WasteChute, TrashBin)):
1614
+ destination = new_location
1615
+ else:
1616
+ destination = validation.ensure_and_convert_deck_slot(
1617
+ new_location, self._api_version, self._core.robot_type
1618
+ )
1619
+
1620
+ _pick_up_offset = (
1621
+ validation.ensure_valid_labware_offset_vector(pick_up_offset)
1622
+ if pick_up_offset
1623
+ else None
1624
+ )
1625
+ _drop_offset = (
1626
+ validation.ensure_valid_labware_offset_vector(drop_offset)
1627
+ if drop_offset
1628
+ else None
1629
+ )
1630
+ with publish_context(
1631
+ broker=self.broker,
1632
+ command=cmds.move_labware(
1633
+ # This needs to be called from protocol context and not the command for import loop reasons
1634
+ text=stringify_lid_movement_command(
1635
+ source_location, new_location, use_gripper
1636
+ )
1637
+ ),
1638
+ ):
1639
+ result = self._core.move_lid(
1640
+ source_location=source,
1641
+ new_location=destination,
1642
+ use_gripper=use_gripper,
1643
+ pause_for_manual_move=True,
1644
+ pick_up_offset=_pick_up_offset,
1645
+ drop_offset=_drop_offset,
1646
+ )
1647
+ if result is not None:
1648
+ return Labware(
1649
+ core=result,
1650
+ api_version=self._api_version,
1651
+ protocol_core=self._core,
1652
+ core_map=self._core_map,
1653
+ )
1654
+ return None
1655
+
1656
+
1657
+ def _create_module_context(
1658
+ module_core: Union[ModuleCore, NonConnectedModuleCore],
1659
+ protocol_core: ProtocolCore,
1660
+ core_map: LoadedCoreMap,
1661
+ api_version: APIVersion,
1662
+ broker: LegacyBroker,
1663
+ ) -> ModuleTypes:
1664
+ module_cls: Optional[Type[ModuleTypes]] = None
1665
+ if isinstance(module_core, AbstractTemperatureModuleCore):
1666
+ module_cls = TemperatureModuleContext
1667
+ elif isinstance(module_core, AbstractMagneticModuleCore):
1668
+ module_cls = MagneticModuleContext
1669
+ elif isinstance(module_core, AbstractThermocyclerCore):
1670
+ module_cls = ThermocyclerContext
1671
+ elif isinstance(module_core, AbstractHeaterShakerCore):
1672
+ module_cls = HeaterShakerContext
1673
+ elif isinstance(module_core, AbstractMagneticBlockCore):
1674
+ module_cls = MagneticBlockContext
1675
+ elif isinstance(module_core, AbstractAbsorbanceReaderCore):
1676
+ module_cls = AbsorbanceReaderContext
1677
+ elif isinstance(module_core, AbstractFlexStackerCore):
1678
+ module_cls = FlexStackerContext
1679
+ else:
1680
+ assert False, "Unsupported module type"
1681
+
1682
+ return module_cls(
1683
+ core=module_core,
1684
+ protocol_core=protocol_core,
1685
+ core_map=core_map,
1686
+ api_version=api_version,
1687
+ broker=broker,
1688
+ )