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,995 @@
1
+ """Shared code for managing pipette configuration and storage."""
2
+ from dataclasses import dataclass
3
+ import logging
4
+ from typing import (
5
+ Callable,
6
+ Dict,
7
+ Generic,
8
+ Optional,
9
+ Tuple,
10
+ Any,
11
+ cast,
12
+ List,
13
+ Sequence,
14
+ Iterator,
15
+ TypeVar,
16
+ )
17
+ import numpy
18
+
19
+ from opentrons_shared_data.errors.exceptions import (
20
+ UnexpectedTipRemovalError,
21
+ UnexpectedTipAttachError,
22
+ )
23
+ from opentrons_shared_data.pipette.types import UlPerMmAction
24
+ from opentrons_shared_data.pipette.types import Quirks
25
+ from opentrons_shared_data.errors.exceptions import CommandPreconditionViolated
26
+
27
+ from opentrons import types as top_types
28
+ from opentrons.hardware_control.types import (
29
+ CriticalPoint,
30
+ HardwareAction,
31
+ Axis,
32
+ OT3Mount,
33
+ )
34
+ from opentrons.hardware_control.constants import (
35
+ SHAKE_OFF_TIPS_SPEED,
36
+ SHAKE_OFF_TIPS_PICKUP_DISTANCE,
37
+ DROP_TIP_RELEASE_DISTANCE,
38
+ SHAKE_OFF_TIPS_DROP_DISTANCE,
39
+ )
40
+
41
+ from opentrons.hardware_control.dev_types import PipetteDict
42
+ from .pipette import Pipette
43
+
44
+ # TODO both pipette handlers should be combined once the pipette configurations
45
+ # are unified AND we separate out the concept of changing pipette state versus static state
46
+
47
+ MountType = TypeVar("MountType", top_types.Mount, OT3Mount)
48
+
49
+ InstrumentsByMount = Dict[MountType, Optional[Pipette]]
50
+ PipetteHandlingData = Tuple[Pipette, MountType]
51
+
52
+ MOD_LOG = logging.getLogger(__name__)
53
+
54
+
55
+ @dataclass(frozen=True)
56
+ class LiquidActionSpec:
57
+ axis: Axis
58
+ volume: float
59
+ plunger_distance: float
60
+ speed: float
61
+ instr: Pipette
62
+ current: float
63
+
64
+
65
+ @dataclass(frozen=True)
66
+ class PickUpTipPressSpec:
67
+ relative_down: top_types.Point
68
+ relative_up: top_types.Point
69
+ current: Dict[Axis, float]
70
+ speed: float
71
+
72
+
73
+ @dataclass(frozen=True)
74
+ class PickUpTipSpec:
75
+ plunger_prep_pos: float
76
+ plunger_currents: Dict[Axis, float]
77
+ presses: List[PickUpTipPressSpec]
78
+ shake_off_list: List[Tuple[top_types.Point, Optional[float]]]
79
+ retract_target: float
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class DropTipMove:
84
+ target_position: float
85
+ current: Dict[Axis, float]
86
+ speed: Optional[float]
87
+ home_after: bool = False
88
+ home_after_safety_margin: float = 0
89
+ home_axes: Sequence[Axis] = tuple()
90
+
91
+
92
+ @dataclass(frozen=True)
93
+ class DropTipSpec:
94
+ drop_moves: List[DropTipMove]
95
+ shake_moves: List[Tuple[top_types.Point, Optional[float]]]
96
+ ending_current: Dict[Axis, float]
97
+
98
+
99
+ class PipetteHandlerProvider(Generic[MountType]):
100
+ IHP_LOG = MOD_LOG.getChild("InstrumentHandler")
101
+
102
+ def __init__(self, attached_instruments: InstrumentsByMount[MountType]):
103
+ assert attached_instruments
104
+ self._attached_instruments: InstrumentsByMount[MountType] = attached_instruments
105
+ self._ihp_log = PipetteHandlerProvider.IHP_LOG.getChild(str(id(self)))
106
+
107
+ def reset_instrument(self, mount: Optional[MountType] = None) -> None:
108
+ """
109
+ Reset the internal state of a pipette by its mount, without doing
110
+ any lower level reconfiguration. This is useful to make sure that no
111
+ settings changes from a protocol persist.
112
+
113
+ :param mount: If specified, reset that mount. If not specified,
114
+ reset both
115
+ """
116
+
117
+ def _reset(m: MountType) -> None:
118
+ if isinstance(m, top_types.Mount) and m not in top_types.Mount.ot2_mounts():
119
+ self._ihp_log.warning(
120
+ "Received a non OT2 mount for resetting. Skipping"
121
+ )
122
+ return
123
+ self._ihp_log.info(f"Resetting configuration for {m}")
124
+ p = self._attached_instruments[m]
125
+ if not p:
126
+ return
127
+ if isinstance(m, OT3Mount):
128
+ # This is to satisfy lint. Code will be cleaner once
129
+ # we can combine the pipette handler for OT2 and OT3
130
+ # pipettes again.
131
+ p.reset_pipette_offset(m.to_mount(), to_default=False)
132
+ else:
133
+ p.reset_pipette_offset(m, to_default=False)
134
+ p.reload_configurations()
135
+ p.reset_state()
136
+
137
+ if not mount:
138
+ for m in type(list(self._attached_instruments.keys())[0]):
139
+ _reset(m)
140
+ else:
141
+ _reset(mount)
142
+
143
+ def reset_instrument_offset(self, mount: MountType, to_default: bool) -> None:
144
+ """
145
+ Temporarily reset the pipette offset to default values.
146
+ :param mount: Modify the given mount.
147
+ """
148
+ if isinstance(mount, OT3Mount):
149
+ # This is to satisfy lint. Code will be cleaner once
150
+ # we can combine the pipette handler for OT2 and OT3
151
+ # pipettes again.
152
+ pipette = self.get_pipette(mount)
153
+ pipette.reset_pipette_offset(mount.to_mount(), to_default)
154
+ else:
155
+ pipette = self.get_pipette(mount)
156
+ pipette.reset_pipette_offset(mount, to_default)
157
+
158
+ def save_instrument_offset(self, mount: MountType, delta: top_types.Point) -> None:
159
+ """
160
+ Save a new instrument offset the pipette offset to a particular value.
161
+ :param mount: Modify the given mount.
162
+ :param delta: The offset to set for the pipette.
163
+ """
164
+ if isinstance(mount, OT3Mount):
165
+ # This is to satisfy lint. Code will be cleaner once
166
+ # we can combine the pipette handler for OT2 and OT3
167
+ # pipettes again.
168
+ pipette = self.get_pipette(mount)
169
+ pipette.save_pipette_offset(mount.to_mount(), delta)
170
+ else:
171
+ pipette = self.get_pipette(mount)
172
+ pipette.save_pipette_offset(mount, delta)
173
+
174
+ # TODO(mc, 2022-01-11): change returned map value type to `Optional[PipetteDict]`
175
+ # instead of potentially returning an empty dict
176
+ def get_attached_instruments(self) -> Dict[MountType, PipetteDict]:
177
+ """Get the status dicts of the cached attached instruments.
178
+
179
+ Also available as :py:meth:`get_attached_instruments`.
180
+
181
+ This returns a dictified version of the
182
+ :py:class:`hardware_control.instruments.pipette.Pipette` as a dict keyed by
183
+ the :py:class:`top_types.Mount` to which the pipette is attached.
184
+ If no pipette is attached on a given mount, the mount key will
185
+ still be present but will have the value ``None``.
186
+
187
+ Note that this is only a query of a cached value; to actively scan
188
+ for changes, use :py:meth:`cache_instruments`. This process deactivates
189
+ the motors and should be used sparingly.
190
+ """
191
+ return {
192
+ m: self.get_attached_instrument(m)
193
+ for m in self._attached_instruments.keys()
194
+ }
195
+
196
+ # TODO(mc, 2022-01-11): change return type to `Optional[PipetteDict]` instead
197
+ # of potentially returning an empty dict
198
+ def get_attached_instrument(self, mount: MountType) -> PipetteDict:
199
+ instr = self._attached_instruments[mount]
200
+ result: Dict[str, Any] = {}
201
+ if instr:
202
+ configs = [
203
+ "name",
204
+ "aspirate_flow_rate",
205
+ "dispense_flow_rate",
206
+ "pipette_id",
207
+ "current_volume",
208
+ "display_name",
209
+ "tip_length",
210
+ "model",
211
+ "blow_out_flow_rate",
212
+ "working_volume",
213
+ "tip_overlap",
214
+ "versioned_tip_overlap",
215
+ "available_volume",
216
+ "return_tip_height",
217
+ "default_aspirate_flow_rates",
218
+ "default_blow_out_flow_rates",
219
+ "default_dispense_flow_rates",
220
+ "back_compat_names",
221
+ "supported_tips",
222
+ "lld_settings",
223
+ ]
224
+
225
+ instr_dict = instr.as_dict()
226
+ # TODO (spp, 2021-08-27): Revisit this logic. Why do we need to build
227
+ # this dict newly every time? Any why only a few items are being updated?
228
+ for key in configs:
229
+ result[key] = instr_dict[key]
230
+ result["current_nozzle_map"] = instr.nozzle_manager.current_configuration
231
+ result["min_volume"] = instr.liquid_class.min_volume
232
+ result["max_volume"] = instr.liquid_class.max_volume
233
+ result["channels"] = instr._max_channels.value
234
+ result["has_tip"] = instr.has_tip
235
+ result["tip_length"] = instr.current_tip_length
236
+ result["aspirate_speed"] = self.plunger_speed(
237
+ instr, instr.aspirate_flow_rate, "aspirate"
238
+ )
239
+ result["dispense_speed"] = self.plunger_speed(
240
+ instr, instr.dispense_flow_rate, "dispense"
241
+ )
242
+ result["blow_out_speed"] = self.plunger_speed(
243
+ instr, instr.blow_out_flow_rate, "dispense"
244
+ )
245
+ result["ready_to_aspirate"] = instr.ready_to_aspirate
246
+ result["default_blow_out_speeds"] = {
247
+ alvl: self.plunger_speed(instr, fr, "blowout")
248
+ for alvl, fr in instr.blow_out_flow_rates_lookup.items()
249
+ }
250
+
251
+ result["default_dispense_speeds"] = {
252
+ alvl: self.plunger_speed(instr, fr, "dispense")
253
+ for alvl, fr in instr.dispense_flow_rates_lookup.items()
254
+ }
255
+ result["default_aspirate_speeds"] = {
256
+ alvl: self.plunger_speed(instr, fr, "aspirate")
257
+ for alvl, fr in instr.aspirate_flow_rates_lookup.items()
258
+ }
259
+ result[
260
+ "pipette_bounding_box_offsets"
261
+ ] = instr.config.pipette_bounding_box_offsets
262
+ result["lld_settings"] = instr.config.lld_settings
263
+ result["plunger_positions"] = {
264
+ "top": instr.plunger_positions.top,
265
+ "bottom": instr.plunger_positions.bottom,
266
+ "blow_out": instr.plunger_positions.blow_out,
267
+ "drop_tip": instr.plunger_positions.drop_tip,
268
+ }
269
+ result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
270
+ return cast(PipetteDict, result)
271
+
272
+ @property
273
+ def attached_instruments(self) -> Dict[MountType, PipetteDict]:
274
+ return self.get_attached_instruments()
275
+
276
+ @property
277
+ def attached_pipettes(self) -> Dict[MountType, PipetteDict]:
278
+ return self.get_attached_instruments()
279
+
280
+ @property
281
+ def get_attached_pipettes(self) -> Dict[MountType, PipetteDict]:
282
+ return self.get_attached_instruments()
283
+
284
+ @property
285
+ def hardware_instruments(self) -> InstrumentsByMount[MountType]:
286
+ """Do not write new code that uses this."""
287
+ return self._attached_instruments
288
+
289
+ def set_current_tiprack_diameter(
290
+ self, mount: MountType, tiprack_diameter: float
291
+ ) -> None:
292
+ instr = self.get_pipette(mount)
293
+ self._ihp_log.info(
294
+ "Updating tip rack diameter on pipette mount: "
295
+ f"{mount}, tip diameter: {tiprack_diameter} mm"
296
+ )
297
+ instr.current_tiprack_diameter = tiprack_diameter
298
+
299
+ def set_working_volume(self, mount: MountType, tip_volume: float) -> None:
300
+ instr = self.get_pipette(mount)
301
+ if not instr:
302
+ raise top_types.PipetteNotAttachedError(
303
+ "No pipette attached to {} mount".format(mount.name)
304
+ )
305
+ self._ihp_log.info(
306
+ "Updating working volume on pipette mount:"
307
+ f"{mount}, tip volume: {tip_volume} ul"
308
+ )
309
+ instr.working_volume = tip_volume
310
+
311
+ def calibrate_plunger(
312
+ self,
313
+ mount: MountType,
314
+ top: Optional[float] = None,
315
+ bottom: Optional[float] = None,
316
+ blow_out: Optional[float] = None,
317
+ drop_tip: Optional[float] = None,
318
+ ) -> None:
319
+ """
320
+ Set calibration values for the pipette plunger.
321
+ This can be called multiple times as the user sets each value,
322
+ or you can set them all at once.
323
+ :param top: Touching but not engaging the plunger.
324
+ :param bottom: Must be above the pipette's physical hard-stop, while
325
+ still leaving enough room for 'blow_out'
326
+ :param blow_out: Plunger is pushed down enough to expel all liquids.
327
+ :param drop_tip: Position that causes the tip to be released from the
328
+ pipette
329
+ """
330
+ instr = self.get_pipette(mount)
331
+ pos_dict: Dict[str, float] = {
332
+ "top": instr.plunger_positions.top,
333
+ "bottom": instr.plunger_positions.bottom,
334
+ "blow_out": instr.plunger_positions.blow_out,
335
+ "drop_tip": instr.plunger_positions.drop_tip,
336
+ }
337
+ if top is not None:
338
+ pos_dict["top"] = top
339
+ if bottom is not None:
340
+ pos_dict["bottom"] = bottom
341
+ if blow_out is not None:
342
+ pos_dict["blow_out"] = blow_out
343
+ if drop_tip is not None:
344
+ pos_dict["drop_tip"] = drop_tip
345
+ instr.update_config_item(pos_dict)
346
+
347
+ def set_flow_rate(
348
+ self,
349
+ mount: MountType,
350
+ aspirate: Optional[float] = None,
351
+ dispense: Optional[float] = None,
352
+ blow_out: Optional[float] = None,
353
+ ) -> None:
354
+ this_pipette = self.get_pipette(mount)
355
+ if aspirate:
356
+ this_pipette.aspirate_flow_rate = aspirate
357
+ if dispense:
358
+ this_pipette.dispense_flow_rate = dispense
359
+ if blow_out:
360
+ this_pipette.blow_out_flow_rate = blow_out
361
+
362
+ def set_pipette_speed(
363
+ self,
364
+ mount: MountType,
365
+ aspirate: Optional[float] = None,
366
+ dispense: Optional[float] = None,
367
+ blow_out: Optional[float] = None,
368
+ ) -> None:
369
+ this_pipette = self.get_pipette(mount)
370
+ if aspirate:
371
+ this_pipette.aspirate_flow_rate = self.plunger_flowrate(
372
+ this_pipette, aspirate, "aspirate"
373
+ )
374
+ if dispense:
375
+ this_pipette.dispense_flow_rate = self.plunger_flowrate(
376
+ this_pipette, dispense, "dispense"
377
+ )
378
+ if blow_out:
379
+ this_pipette.blow_out_flow_rate = self.plunger_flowrate(
380
+ this_pipette, blow_out, "dispense"
381
+ )
382
+
383
+ def instrument_max_height(
384
+ self,
385
+ mount: MountType,
386
+ retract_distance: float,
387
+ critical_point: Optional[CriticalPoint],
388
+ ) -> float:
389
+ """Return max achievable height of the attached instrument
390
+ based on the current critical point
391
+ """
392
+ pip = self.get_pipette(mount)
393
+ cp = self.critical_point_for(mount, critical_point)
394
+
395
+ max_height = (
396
+ pip.config.mount_configurations.homePosition - retract_distance + cp.z
397
+ )
398
+
399
+ return max_height
400
+
401
+ async def reset(self) -> None:
402
+ self._attached_instruments = {
403
+ k: None for k in self._attached_instruments.keys()
404
+ }
405
+
406
+ async def update_nozzle_configuration(
407
+ self,
408
+ mount: MountType,
409
+ back_left_nozzle: str,
410
+ front_right_nozzle: str,
411
+ starting_nozzle: Optional[str] = None,
412
+ ) -> None:
413
+ instr = self._attached_instruments[mount]
414
+ if instr:
415
+ instr.update_nozzle_configuration(
416
+ back_left_nozzle, front_right_nozzle, starting_nozzle
417
+ )
418
+
419
+ async def reset_nozzle_configuration(self, mount: MountType) -> None:
420
+ instr = self._attached_instruments[mount]
421
+ if instr:
422
+ instr.reset_nozzle_configuration()
423
+
424
+ def add_tip(self, mount: MountType, tip_length: float) -> None:
425
+ instr = self._attached_instruments[mount]
426
+ attached = self.attached_instruments
427
+ instr_dict = attached[mount]
428
+ if instr and not instr.has_tip:
429
+ instr.add_tip(tip_length=tip_length)
430
+ # TODO (spp, 2021-08-27): These items are being updated in a local copy
431
+ # of the PipetteDict, which gets thrown away. Fix this.
432
+ instr_dict["has_tip"] = True
433
+ instr_dict["tip_length"] = tip_length
434
+ else:
435
+ self._ihp_log.warning(
436
+ f"attach tip called while tip already attached to {instr}"
437
+ )
438
+
439
+ def cache_tip(self, mount: MountType, tip_length: float) -> None:
440
+ instrument = self.get_pipette(mount)
441
+ if instrument.has_tip:
442
+ # instrument.add_tip() would raise an AssertionError if we tried to overwrite an existing tip.
443
+ instrument.remove_tip()
444
+ instrument.add_tip(tip_length=tip_length)
445
+ instrument.set_current_volume(0)
446
+
447
+ def remove_tip(self, mount: MountType) -> None:
448
+ instr = self._attached_instruments[mount]
449
+ attached = self.attached_instruments
450
+ instr_dict = attached[mount]
451
+ if instr and instr.has_tip:
452
+ instr.remove_tip()
453
+ # TODO (spp, 2021-08-27): These items are being updated in a local copy
454
+ # of the PipetteDict, which gets thrown away. Fix this.
455
+ instr_dict["has_tip"] = False
456
+ instr_dict["tip_length"] = 0.0
457
+ else:
458
+ self._ihp_log.warning("detach tip called with no tip")
459
+
460
+ def critical_point_for(
461
+ self, mount: MountType, cp_override: Optional[CriticalPoint] = None
462
+ ) -> top_types.Point:
463
+ """Return the current critical point of the specified mount.
464
+
465
+ The mount's critical point is the position of the mount itself, if no
466
+ pipette is attached, or the pipette's critical point (which depends on
467
+ tip status).
468
+
469
+ If `cp_override` is specified, and that critical point actually exists,
470
+ it will be used instead. Invalid `cp_override`s are ignored.
471
+ """
472
+ pip = self._attached_instruments[mount]
473
+ if pip is not None and cp_override != CriticalPoint.MOUNT:
474
+ return pip.critical_point(cp_override)
475
+ else:
476
+ # This offset is required because the motor driver coordinate system is
477
+ # configured such that the end of a p300 single gen1's tip is 0.
478
+ return top_types.Point(0, 0, 30)
479
+
480
+ def ready_for_tip_action(
481
+ self, target: Pipette, action: HardwareAction, mount: MountType
482
+ ) -> None:
483
+ if not target.has_tip:
484
+ raise UnexpectedTipRemovalError(action.name, target.name, mount.name)
485
+ if (
486
+ action == HardwareAction.ASPIRATE
487
+ and target.current_volume == 0
488
+ and not target.ready_to_aspirate
489
+ ):
490
+ raise RuntimeError("Pipette not ready to aspirate")
491
+ self._ihp_log.debug(f"{action} on {target.name}")
492
+
493
+ def plunger_position(
494
+ self, instr: Pipette, ul: float, action: "UlPerMmAction"
495
+ ) -> float:
496
+ mm = ul / instr.ul_per_mm(ul, action)
497
+ position = mm + instr.plunger_positions.bottom
498
+ return round(position, 6)
499
+
500
+ def plunger_speed(
501
+ self, instr: Pipette, ul_per_s: float, action: "UlPerMmAction"
502
+ ) -> float:
503
+ mm_per_s = ul_per_s / instr.ul_per_mm(instr.liquid_class.max_volume, action)
504
+ return round(mm_per_s, 6)
505
+
506
+ def plunger_flowrate(
507
+ self, instr: Pipette, mm_per_s: float, action: "UlPerMmAction"
508
+ ) -> float:
509
+ ul_per_s = mm_per_s * instr.ul_per_mm(instr.liquid_class.max_volume, action)
510
+ return round(ul_per_s, 6)
511
+
512
+ def plan_check_aspirate(
513
+ self,
514
+ mount: MountType,
515
+ volume: Optional[float],
516
+ rate: float,
517
+ ) -> Optional[LiquidActionSpec]:
518
+ """Check preconditions for aspirate, parse args, and calculate positions.
519
+
520
+ While the mechanics of issuing an aspirate move itself are left to child
521
+ classes, determining things like aspiration volume from the allowed argument
522
+ types is invariant between machines, and this method gathers that functionality.
523
+
524
+ Coalesce
525
+ - Optional volumes
526
+
527
+ Check
528
+ - Aspiration volumes compared to max and remaining
529
+
530
+ Calculate
531
+ - Plunger distances (possibly calling an overridden plunger_volume)
532
+ """
533
+ instrument = self.get_pipette(mount)
534
+ self.ready_for_tip_action(instrument, HardwareAction.ASPIRATE, mount)
535
+ if volume is None:
536
+ self._ihp_log.debug(
537
+ "No aspirate volume defined. Aspirating up to "
538
+ "max_volume for the pipette"
539
+ )
540
+ asp_vol = instrument.available_volume
541
+ else:
542
+ asp_vol = volume
543
+
544
+ if asp_vol == 0:
545
+ return None
546
+
547
+ assert instrument.ok_to_add_volume(
548
+ asp_vol
549
+ ), "Cannot aspirate more than pipette max volume"
550
+
551
+ dist = self.plunger_position(
552
+ instrument, instrument.current_volume + asp_vol, "aspirate"
553
+ )
554
+ speed = self.plunger_speed(
555
+ instrument, instrument.aspirate_flow_rate * rate, "aspirate"
556
+ )
557
+ if isinstance(mount, OT3Mount):
558
+ return LiquidActionSpec(
559
+ axis=Axis.of_main_tool_actuator(mount),
560
+ volume=asp_vol,
561
+ plunger_distance=dist,
562
+ speed=speed,
563
+ instr=instrument,
564
+ current=instrument.plunger_motor_current.run,
565
+ )
566
+ else:
567
+ return LiquidActionSpec(
568
+ axis=Axis.of_plunger(mount),
569
+ volume=asp_vol,
570
+ plunger_distance=dist,
571
+ speed=speed,
572
+ instr=instrument,
573
+ current=instrument.plunger_motor_current.run,
574
+ )
575
+
576
+ def plan_check_dispense(
577
+ self,
578
+ mount: MountType,
579
+ volume: Optional[float],
580
+ rate: float,
581
+ push_out: Optional[float],
582
+ ) -> Optional[LiquidActionSpec]:
583
+ """Check preconditions for dispense, parse args, and calculate positions.
584
+
585
+ While the mechanics of issuing a dispense move itself are left to child
586
+ classes, determining things like dispense volume from the allowed argument
587
+ types is invariant between machines, and this method gathers that functionality.
588
+
589
+ Coalesce
590
+ - Optional volumes
591
+
592
+ Check
593
+ - Dispense volumes compared to max and remaining
594
+
595
+ Calculate
596
+ - Plunger distances (possibly calling an overridden plunger_volume)
597
+ """
598
+
599
+ instrument = self.get_pipette(mount)
600
+ self.ready_for_tip_action(instrument, HardwareAction.DISPENSE, mount)
601
+
602
+ if volume is None:
603
+ disp_vol = instrument.current_volume
604
+ self._ihp_log.debug(
605
+ "No dispense volume specified. Dispensing all "
606
+ "remaining liquid ({}uL) from pipette".format(disp_vol)
607
+ )
608
+ else:
609
+ disp_vol = volume
610
+
611
+ # Ensure we don't dispense more than the current volume.
612
+ #
613
+ # This clamping is inconsistent with plan_check_aspirate(), which asserts
614
+ # that its input is in bounds instead of clamping it. This is left to avoid
615
+ # disturbing Python protocols with apiLevel <= 2.13. In newer Python protocols,
616
+ # the Protocol Engine layer applies its own bounds checking.
617
+ disp_vol = min(instrument.current_volume, disp_vol)
618
+
619
+ if disp_vol == 0:
620
+ return None
621
+
622
+ is_full_dispense = numpy.isclose(instrument.current_volume - disp_vol, 0)
623
+
624
+ if is_full_dispense:
625
+ if push_out is None:
626
+ push_out_ul = instrument.push_out_volume
627
+ else:
628
+ push_out_ul = push_out
629
+ else:
630
+ if push_out is not None and push_out != 0:
631
+ raise CommandPreconditionViolated(
632
+ message="Cannot push_out on a dispense that does not leave the pipette empty",
633
+ detail={
634
+ "command": "dispense",
635
+ "remaining-volume": str(instrument.current_volume - disp_vol),
636
+ },
637
+ )
638
+ push_out_ul = 0
639
+
640
+ push_out_dist_mm = push_out_ul / instrument.ul_per_mm(push_out_ul, "blowout")
641
+
642
+ if not instrument.ok_to_push_out(push_out_dist_mm):
643
+ raise CommandPreconditionViolated(
644
+ message="Cannot push_out more than pipette max blowout volume.",
645
+ detail={
646
+ "command": "dispense",
647
+ },
648
+ )
649
+
650
+ dist = self.plunger_position(
651
+ instrument, instrument.current_volume - disp_vol, "dispense"
652
+ )
653
+ speed = self.plunger_speed(
654
+ instrument, instrument.dispense_flow_rate * rate, "dispense"
655
+ )
656
+ if isinstance(mount, top_types.Mount):
657
+ return LiquidActionSpec(
658
+ axis=Axis.of_plunger(mount),
659
+ volume=disp_vol,
660
+ plunger_distance=dist + push_out_dist_mm,
661
+ speed=speed,
662
+ instr=instrument,
663
+ current=instrument.plunger_motor_current.run,
664
+ )
665
+ else:
666
+ return LiquidActionSpec(
667
+ axis=Axis.of_main_tool_actuator(mount),
668
+ volume=disp_vol,
669
+ plunger_distance=dist,
670
+ speed=speed,
671
+ instr=instrument,
672
+ current=instrument.plunger_motor_current.run,
673
+ )
674
+
675
+ def plan_check_blow_out(self, mount: MountType) -> LiquidActionSpec:
676
+ """Check preconditions and calculate values for blowout."""
677
+ instrument = self.get_pipette(mount)
678
+ speed = self.plunger_speed(
679
+ instrument, instrument.blow_out_flow_rate, "dispense"
680
+ )
681
+
682
+ if isinstance(mount, top_types.Mount):
683
+ return LiquidActionSpec(
684
+ axis=Axis.of_plunger(mount),
685
+ volume=0,
686
+ plunger_distance=instrument.plunger_positions.blow_out,
687
+ speed=speed,
688
+ instr=instrument,
689
+ current=instrument.plunger_motor_current.run,
690
+ )
691
+ else:
692
+ return LiquidActionSpec(
693
+ axis=Axis.of_main_tool_actuator(mount),
694
+ volume=0,
695
+ plunger_distance=instrument.plunger_positions.blow_out,
696
+ speed=speed,
697
+ instr=instrument,
698
+ current=instrument.plunger_motor_current.run,
699
+ )
700
+
701
+ @staticmethod
702
+ def _build_pickup_shakes(
703
+ instrument: Pipette,
704
+ ) -> List[Tuple[top_types.Point, Optional[float]]]:
705
+ def build_one_shake() -> List[Tuple[top_types.Point, Optional[float]]]:
706
+ shake_dist = float(SHAKE_OFF_TIPS_PICKUP_DISTANCE)
707
+ shake_speed = float(SHAKE_OFF_TIPS_SPEED)
708
+ return [
709
+ (top_types.Point(-shake_dist, 0, 0), shake_speed), # left
710
+ (top_types.Point(2 * shake_dist, 0, 0), shake_speed), # right
711
+ (top_types.Point(-shake_dist, 0, 0), shake_speed), # center
712
+ (top_types.Point(0, -shake_dist, 0), shake_speed), # front
713
+ (top_types.Point(0, 2 * shake_dist, 0), shake_speed), # back
714
+ (top_types.Point(0, -shake_dist, 0), shake_speed), # center
715
+ (top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # up
716
+ ]
717
+
718
+ if Quirks.pickupTipShake in instrument.config.quirks:
719
+ return build_one_shake() + build_one_shake()
720
+ else:
721
+ return []
722
+
723
+ def plan_check_pick_up_tip(
724
+ self,
725
+ mount: MountType,
726
+ presses: Optional[int],
727
+ increment: Optional[float],
728
+ tip_length: float = 0,
729
+ ) -> Tuple[PickUpTipSpec, Callable[[], None]]:
730
+ # Prechecks: ready for pickup tip and press/increment are valid
731
+ instrument = self.get_pipette(mount)
732
+ if instrument.has_tip:
733
+ raise UnexpectedTipAttachError("pick_up_tip", instrument.name, mount.name)
734
+ self._ihp_log.debug(f"Picking up tip on {mount.name}")
735
+
736
+ if presses is None or presses < 0:
737
+ checked_presses = instrument.pick_up_configurations.press_fit.presses
738
+ else:
739
+ checked_presses = presses
740
+
741
+ if not increment or increment < 0:
742
+ check_incr = instrument.pick_up_configurations.press_fit.increment
743
+ else:
744
+ check_incr = increment
745
+
746
+ pick_up_speed = instrument.get_pick_up_speed_by_configuration(
747
+ instrument.pick_up_configurations.press_fit
748
+ )
749
+
750
+ pick_up_distance = instrument.get_pick_up_distance_by_configuration(
751
+ instrument.pick_up_configurations.press_fit
752
+ )
753
+
754
+ def build_presses() -> Iterator[Tuple[float, float]]:
755
+ # Press the nozzle into the tip <presses> number of times,
756
+ # moving further by <increment> mm after each press
757
+ for i in range(checked_presses):
758
+ # move nozzle down into the tip
759
+
760
+ press_dist = -1.0 * pick_up_distance + -1.0 * check_incr * i
761
+ # move nozzle back up
762
+ backup_dist = -press_dist
763
+ yield (press_dist, backup_dist)
764
+
765
+ def add_tip_to_instr() -> None:
766
+ instrument.add_tip(tip_length=tip_length)
767
+ instrument.set_current_volume(0)
768
+
769
+ if isinstance(mount, top_types.Mount):
770
+ return (
771
+ PickUpTipSpec(
772
+ plunger_prep_pos=instrument.plunger_positions.bottom,
773
+ plunger_currents={
774
+ Axis.of_plunger(mount): instrument.plunger_motor_current.run
775
+ },
776
+ presses=[
777
+ PickUpTipPressSpec(
778
+ current={
779
+ Axis.by_mount(
780
+ mount
781
+ ): instrument.get_pick_up_current_by_configuration(
782
+ instrument.pick_up_configurations.press_fit
783
+ )
784
+ },
785
+ speed=pick_up_speed,
786
+ relative_down=top_types.Point(0, 0, press_dist),
787
+ relative_up=top_types.Point(0, 0, backup_dist),
788
+ )
789
+ for press_dist, backup_dist in build_presses()
790
+ ],
791
+ shake_off_list=self._build_pickup_shakes(instrument),
792
+ retract_target=pick_up_distance + check_incr * checked_presses + 2,
793
+ ),
794
+ add_tip_to_instr,
795
+ )
796
+ else:
797
+ return (
798
+ PickUpTipSpec(
799
+ plunger_prep_pos=instrument.plunger_positions.bottom,
800
+ plunger_currents={
801
+ Axis.of_main_tool_actuator(
802
+ mount
803
+ ): instrument.plunger_motor_current.run
804
+ },
805
+ presses=[
806
+ PickUpTipPressSpec(
807
+ current={
808
+ Axis.by_mount(
809
+ mount
810
+ ): instrument.get_pick_up_current_by_configuration(
811
+ instrument.pick_up_configurations.press_fit
812
+ )
813
+ },
814
+ speed=pick_up_speed,
815
+ relative_down=top_types.Point(0, 0, press_dist),
816
+ relative_up=top_types.Point(0, 0, backup_dist),
817
+ )
818
+ for press_dist, backup_dist in build_presses()
819
+ ],
820
+ shake_off_list=self._build_pickup_shakes(instrument),
821
+ retract_target=pick_up_distance + check_incr * checked_presses + 2,
822
+ ),
823
+ add_tip_to_instr,
824
+ )
825
+
826
+ @staticmethod
827
+ def _shake_off_tips_drop(
828
+ tiprack_diameter: float,
829
+ ) -> List[Tuple[top_types.Point, Optional[float]]]:
830
+ # tips don't always fall off, especially if resting against
831
+ # tiprack or other tips below it. To ensure the tip has fallen
832
+ # first, shake the pipette to dislodge partially-sealed tips,
833
+ # then second, raise the pipette so loosened tips have room to fall
834
+ shake_off_dist = SHAKE_OFF_TIPS_DROP_DISTANCE
835
+ if tiprack_diameter > 0.0:
836
+ shake_off_dist = min(shake_off_dist, tiprack_diameter / 4)
837
+ shake_off_dist = max(shake_off_dist, 1.0)
838
+ speed = SHAKE_OFF_TIPS_SPEED
839
+ return [
840
+ (top_types.Point(-shake_off_dist, 0, 0), speed), # left
841
+ (top_types.Point(2 * shake_off_dist, 0, 0), speed), # right
842
+ (top_types.Point(-shake_off_dist, 0, 0), speed), # center
843
+ (top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # top
844
+ ]
845
+
846
+ def _droptip_sequence_builder(
847
+ self,
848
+ bottom_pos: float,
849
+ droptip_pos: float,
850
+ plunger_currents: Dict[Axis, float],
851
+ drop_tip_currents: Dict[Axis, float],
852
+ speed: float,
853
+ home_after: bool,
854
+ home_axes: Sequence[Axis],
855
+ ) -> Callable[[], List[DropTipMove]]:
856
+ def build() -> List[DropTipMove]:
857
+ base = [
858
+ DropTipMove(
859
+ target_position=bottom_pos, current=plunger_currents, speed=None
860
+ ),
861
+ DropTipMove(
862
+ target_position=droptip_pos,
863
+ current=drop_tip_currents,
864
+ speed=speed,
865
+ home_after=home_after,
866
+ home_after_safety_margin=abs(bottom_pos - droptip_pos),
867
+ home_axes=home_axes,
868
+ ),
869
+ DropTipMove( # always finish drop-tip at a known safe plunger position
870
+ target_position=bottom_pos, current=plunger_currents, speed=None
871
+ ),
872
+ ]
873
+ return base
874
+
875
+ return build
876
+
877
+ # todo(mm, 2024-10-17): The returned _remove_tips() callable is not used by anything
878
+ # anymore. Delete it.
879
+ def plan_check_drop_tip(
880
+ self,
881
+ mount: MountType,
882
+ home_after: bool,
883
+ ) -> Tuple[DropTipSpec, Callable[[], None]]:
884
+ instrument = self.get_pipette(mount)
885
+
886
+ if not instrument.drop_configurations.plunger_eject:
887
+ raise CommandPreconditionViolated(
888
+ f"Pipette {instrument.name} on {mount.name} has no plunger eject configuration"
889
+ )
890
+ bottom = instrument.plunger_positions.bottom
891
+ droptip = instrument.plunger_positions.drop_tip
892
+ speed = instrument.drop_configurations.plunger_eject.speed
893
+ shakes: List[Tuple[top_types.Point, Optional[float]]] = []
894
+ if Quirks.dropTipShake in instrument.config.quirks:
895
+ diameter = instrument.current_tiprack_diameter
896
+ shakes = self._shake_off_tips_drop(diameter)
897
+
898
+ def _remove_tips() -> None:
899
+ instrument.set_current_volume(0)
900
+ instrument.current_tiprack_diameter = 0.0
901
+ instrument.remove_tip()
902
+
903
+ if isinstance(mount, top_types.Mount):
904
+ seq_builder_ot2 = self._droptip_sequence_builder(
905
+ bottom,
906
+ droptip,
907
+ {Axis.of_plunger(mount): instrument.plunger_motor_current.run},
908
+ {
909
+ Axis.of_plunger(
910
+ mount
911
+ ): instrument.drop_configurations.plunger_eject.current
912
+ },
913
+ speed,
914
+ home_after,
915
+ (Axis.of_plunger(mount),),
916
+ )
917
+ seq_ot2 = seq_builder_ot2()
918
+ if Quirks.doubleDropTip in instrument.config.quirks:
919
+ seq_ot2 = seq_ot2 + seq_builder_ot2()
920
+ return (
921
+ DropTipSpec(
922
+ drop_moves=seq_ot2,
923
+ shake_moves=shakes,
924
+ ending_current={
925
+ Axis.of_plunger(mount): instrument.plunger_motor_current.run
926
+ },
927
+ ),
928
+ _remove_tips,
929
+ )
930
+ else:
931
+ seq_builder_ot3 = self._droptip_sequence_builder(
932
+ bottom,
933
+ droptip,
934
+ {
935
+ Axis.of_main_tool_actuator(
936
+ mount
937
+ ): instrument.plunger_motor_current.run
938
+ },
939
+ {
940
+ Axis.of_main_tool_actuator(
941
+ mount
942
+ ): instrument.drop_configurations.plunger_eject.current
943
+ },
944
+ speed,
945
+ home_after,
946
+ (Axis.of_main_tool_actuator(mount),),
947
+ )
948
+
949
+ seq_ot3 = seq_builder_ot3()
950
+ if Quirks.doubleDropTip in instrument.config.quirks:
951
+ seq_ot3 = seq_ot3 + seq_builder_ot3()
952
+ return (
953
+ DropTipSpec(
954
+ drop_moves=seq_ot3,
955
+ shake_moves=shakes,
956
+ ending_current={
957
+ Axis.of_main_tool_actuator(
958
+ mount
959
+ ): instrument.plunger_motor_current.run
960
+ },
961
+ ),
962
+ _remove_tips,
963
+ )
964
+
965
+ def get_pipette(self, mount: MountType) -> Pipette:
966
+ pip = self._attached_instruments[mount]
967
+ if not pip:
968
+ raise top_types.PipetteNotAttachedError(
969
+ f"No pipette attached to {mount.name} mount"
970
+ )
971
+ return pip
972
+
973
+ async def set_liquid_class(self, mount: MountType, liquid_class: str) -> None:
974
+ pip = self.get_pipette(mount)
975
+ pip.set_liquid_class_by_name(liquid_class)
976
+
977
+
978
+ class OT3PipetteHandler(PipetteHandlerProvider[OT3Mount]):
979
+ """Override for correct plunger_position."""
980
+
981
+ def plunger_position(
982
+ self, instr: Pipette, ul: float, action: "UlPerMmAction"
983
+ ) -> float:
984
+ mm = ul / instr.ul_per_mm(ul, action)
985
+ position = instr.plunger_positions.bottom - mm
986
+ return round(position, 6)
987
+
988
+ def critical_point_for(
989
+ self, mount: MountType, cp_override: Optional[CriticalPoint] = None
990
+ ) -> top_types.Point:
991
+ pip = self._attached_instruments[OT3Mount.from_mount(mount)]
992
+ if pip is not None and cp_override != CriticalPoint.MOUNT:
993
+ return pip.critical_point(cp_override)
994
+ else:
995
+ return top_types.Point(0, 0, 0)