opentrons 8.6.0__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 (601) 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 +557 -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 +187 -0
  45. opentrons/drivers/asyncio/communication/errors.py +88 -0
  46. opentrons/drivers/asyncio/communication/serial_connection.py +557 -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/scripts/update_module_fw.py +274 -0
  200. opentrons/hardware_control/simulator_setup.py +260 -0
  201. opentrons/hardware_control/thread_manager.py +431 -0
  202. opentrons/hardware_control/threaded_async_lock.py +97 -0
  203. opentrons/hardware_control/types.py +792 -0
  204. opentrons/hardware_control/util.py +234 -0
  205. opentrons/legacy_broker.py +53 -0
  206. opentrons/legacy_commands/__init__.py +1 -0
  207. opentrons/legacy_commands/commands.py +483 -0
  208. opentrons/legacy_commands/helpers.py +153 -0
  209. opentrons/legacy_commands/module_commands.py +276 -0
  210. opentrons/legacy_commands/protocol_commands.py +54 -0
  211. opentrons/legacy_commands/publisher.py +155 -0
  212. opentrons/legacy_commands/robot_commands.py +51 -0
  213. opentrons/legacy_commands/types.py +1186 -0
  214. opentrons/motion_planning/__init__.py +32 -0
  215. opentrons/motion_planning/adjacent_slots_getters.py +168 -0
  216. opentrons/motion_planning/deck_conflict.py +501 -0
  217. opentrons/motion_planning/errors.py +35 -0
  218. opentrons/motion_planning/types.py +42 -0
  219. opentrons/motion_planning/waypoints.py +218 -0
  220. opentrons/ordered_set.py +138 -0
  221. opentrons/protocol_api/__init__.py +105 -0
  222. opentrons/protocol_api/_liquid.py +157 -0
  223. opentrons/protocol_api/_liquid_properties.py +814 -0
  224. opentrons/protocol_api/_nozzle_layout.py +31 -0
  225. opentrons/protocol_api/_parameter_context.py +300 -0
  226. opentrons/protocol_api/_parameters.py +31 -0
  227. opentrons/protocol_api/_transfer_liquid_validation.py +108 -0
  228. opentrons/protocol_api/_types.py +43 -0
  229. opentrons/protocol_api/config.py +23 -0
  230. opentrons/protocol_api/core/__init__.py +23 -0
  231. opentrons/protocol_api/core/common.py +33 -0
  232. opentrons/protocol_api/core/core_map.py +74 -0
  233. opentrons/protocol_api/core/engine/__init__.py +22 -0
  234. opentrons/protocol_api/core/engine/_default_labware_versions.py +179 -0
  235. opentrons/protocol_api/core/engine/deck_conflict.py +400 -0
  236. opentrons/protocol_api/core/engine/exceptions.py +19 -0
  237. opentrons/protocol_api/core/engine/instrument.py +2391 -0
  238. opentrons/protocol_api/core/engine/labware.py +238 -0
  239. opentrons/protocol_api/core/engine/load_labware_params.py +73 -0
  240. opentrons/protocol_api/core/engine/module_core.py +1027 -0
  241. opentrons/protocol_api/core/engine/overlap_versions.py +20 -0
  242. opentrons/protocol_api/core/engine/pipette_movement_conflict.py +358 -0
  243. opentrons/protocol_api/core/engine/point_calculations.py +64 -0
  244. opentrons/protocol_api/core/engine/protocol.py +1153 -0
  245. opentrons/protocol_api/core/engine/robot.py +139 -0
  246. opentrons/protocol_api/core/engine/stringify.py +74 -0
  247. opentrons/protocol_api/core/engine/transfer_components_executor.py +1006 -0
  248. opentrons/protocol_api/core/engine/well.py +241 -0
  249. opentrons/protocol_api/core/instrument.py +459 -0
  250. opentrons/protocol_api/core/labware.py +151 -0
  251. opentrons/protocol_api/core/legacy/__init__.py +11 -0
  252. opentrons/protocol_api/core/legacy/_labware_geometry.py +37 -0
  253. opentrons/protocol_api/core/legacy/deck.py +369 -0
  254. opentrons/protocol_api/core/legacy/labware_offset_provider.py +108 -0
  255. opentrons/protocol_api/core/legacy/legacy_instrument_core.py +709 -0
  256. opentrons/protocol_api/core/legacy/legacy_labware_core.py +235 -0
  257. opentrons/protocol_api/core/legacy/legacy_module_core.py +592 -0
  258. opentrons/protocol_api/core/legacy/legacy_protocol_core.py +612 -0
  259. opentrons/protocol_api/core/legacy/legacy_well_core.py +162 -0
  260. opentrons/protocol_api/core/legacy/load_info.py +67 -0
  261. opentrons/protocol_api/core/legacy/module_geometry.py +547 -0
  262. opentrons/protocol_api/core/legacy/well_geometry.py +148 -0
  263. opentrons/protocol_api/core/legacy_simulator/__init__.py +16 -0
  264. opentrons/protocol_api/core/legacy_simulator/legacy_instrument_core.py +624 -0
  265. opentrons/protocol_api/core/legacy_simulator/legacy_protocol_core.py +85 -0
  266. opentrons/protocol_api/core/module.py +484 -0
  267. opentrons/protocol_api/core/protocol.py +311 -0
  268. opentrons/protocol_api/core/robot.py +51 -0
  269. opentrons/protocol_api/core/well.py +116 -0
  270. opentrons/protocol_api/core/well_grid.py +45 -0
  271. opentrons/protocol_api/create_protocol_context.py +177 -0
  272. opentrons/protocol_api/deck.py +223 -0
  273. opentrons/protocol_api/disposal_locations.py +244 -0
  274. opentrons/protocol_api/instrument_context.py +3272 -0
  275. opentrons/protocol_api/labware.py +1579 -0
  276. opentrons/protocol_api/module_contexts.py +1447 -0
  277. opentrons/protocol_api/module_validation_and_errors.py +61 -0
  278. opentrons/protocol_api/protocol_context.py +1688 -0
  279. opentrons/protocol_api/robot_context.py +303 -0
  280. opentrons/protocol_api/validation.py +761 -0
  281. opentrons/protocol_engine/__init__.py +155 -0
  282. opentrons/protocol_engine/actions/__init__.py +65 -0
  283. opentrons/protocol_engine/actions/action_dispatcher.py +30 -0
  284. opentrons/protocol_engine/actions/action_handler.py +13 -0
  285. opentrons/protocol_engine/actions/actions.py +302 -0
  286. opentrons/protocol_engine/actions/get_state_update.py +38 -0
  287. opentrons/protocol_engine/clients/__init__.py +5 -0
  288. opentrons/protocol_engine/clients/sync_client.py +174 -0
  289. opentrons/protocol_engine/clients/transports.py +197 -0
  290. opentrons/protocol_engine/commands/__init__.py +757 -0
  291. opentrons/protocol_engine/commands/absorbance_reader/__init__.py +61 -0
  292. opentrons/protocol_engine/commands/absorbance_reader/close_lid.py +154 -0
  293. opentrons/protocol_engine/commands/absorbance_reader/common.py +6 -0
  294. opentrons/protocol_engine/commands/absorbance_reader/initialize.py +151 -0
  295. opentrons/protocol_engine/commands/absorbance_reader/open_lid.py +154 -0
  296. opentrons/protocol_engine/commands/absorbance_reader/read.py +226 -0
  297. opentrons/protocol_engine/commands/air_gap_in_place.py +162 -0
  298. opentrons/protocol_engine/commands/aspirate.py +244 -0
  299. opentrons/protocol_engine/commands/aspirate_in_place.py +184 -0
  300. opentrons/protocol_engine/commands/aspirate_while_tracking.py +211 -0
  301. opentrons/protocol_engine/commands/blow_out.py +146 -0
  302. opentrons/protocol_engine/commands/blow_out_in_place.py +119 -0
  303. opentrons/protocol_engine/commands/calibration/__init__.py +60 -0
  304. opentrons/protocol_engine/commands/calibration/calibrate_gripper.py +166 -0
  305. opentrons/protocol_engine/commands/calibration/calibrate_module.py +117 -0
  306. opentrons/protocol_engine/commands/calibration/calibrate_pipette.py +96 -0
  307. opentrons/protocol_engine/commands/calibration/move_to_maintenance_position.py +156 -0
  308. opentrons/protocol_engine/commands/command.py +308 -0
  309. opentrons/protocol_engine/commands/command_unions.py +974 -0
  310. opentrons/protocol_engine/commands/comment.py +57 -0
  311. opentrons/protocol_engine/commands/configure_for_volume.py +108 -0
  312. opentrons/protocol_engine/commands/configure_nozzle_layout.py +115 -0
  313. opentrons/protocol_engine/commands/custom.py +67 -0
  314. opentrons/protocol_engine/commands/dispense.py +194 -0
  315. opentrons/protocol_engine/commands/dispense_in_place.py +179 -0
  316. opentrons/protocol_engine/commands/dispense_while_tracking.py +204 -0
  317. opentrons/protocol_engine/commands/drop_tip.py +232 -0
  318. opentrons/protocol_engine/commands/drop_tip_in_place.py +205 -0
  319. opentrons/protocol_engine/commands/flex_stacker/__init__.py +64 -0
  320. opentrons/protocol_engine/commands/flex_stacker/common.py +900 -0
  321. opentrons/protocol_engine/commands/flex_stacker/empty.py +293 -0
  322. opentrons/protocol_engine/commands/flex_stacker/fill.py +281 -0
  323. opentrons/protocol_engine/commands/flex_stacker/retrieve.py +339 -0
  324. opentrons/protocol_engine/commands/flex_stacker/set_stored_labware.py +328 -0
  325. opentrons/protocol_engine/commands/flex_stacker/store.py +339 -0
  326. opentrons/protocol_engine/commands/generate_command_schema.py +61 -0
  327. opentrons/protocol_engine/commands/get_next_tip.py +134 -0
  328. opentrons/protocol_engine/commands/get_tip_presence.py +87 -0
  329. opentrons/protocol_engine/commands/hash_command_params.py +38 -0
  330. opentrons/protocol_engine/commands/heater_shaker/__init__.py +102 -0
  331. opentrons/protocol_engine/commands/heater_shaker/close_labware_latch.py +83 -0
  332. opentrons/protocol_engine/commands/heater_shaker/deactivate_heater.py +82 -0
  333. opentrons/protocol_engine/commands/heater_shaker/deactivate_shaker.py +84 -0
  334. opentrons/protocol_engine/commands/heater_shaker/open_labware_latch.py +110 -0
  335. opentrons/protocol_engine/commands/heater_shaker/set_and_wait_for_shake_speed.py +125 -0
  336. opentrons/protocol_engine/commands/heater_shaker/set_target_temperature.py +90 -0
  337. opentrons/protocol_engine/commands/heater_shaker/wait_for_temperature.py +102 -0
  338. opentrons/protocol_engine/commands/home.py +100 -0
  339. opentrons/protocol_engine/commands/identify_module.py +86 -0
  340. opentrons/protocol_engine/commands/labware_handling_common.py +29 -0
  341. opentrons/protocol_engine/commands/liquid_probe.py +464 -0
  342. opentrons/protocol_engine/commands/load_labware.py +210 -0
  343. opentrons/protocol_engine/commands/load_lid.py +154 -0
  344. opentrons/protocol_engine/commands/load_lid_stack.py +272 -0
  345. opentrons/protocol_engine/commands/load_liquid.py +95 -0
  346. opentrons/protocol_engine/commands/load_liquid_class.py +144 -0
  347. opentrons/protocol_engine/commands/load_module.py +223 -0
  348. opentrons/protocol_engine/commands/load_pipette.py +167 -0
  349. opentrons/protocol_engine/commands/magnetic_module/__init__.py +32 -0
  350. opentrons/protocol_engine/commands/magnetic_module/disengage.py +97 -0
  351. opentrons/protocol_engine/commands/magnetic_module/engage.py +119 -0
  352. opentrons/protocol_engine/commands/move_labware.py +546 -0
  353. opentrons/protocol_engine/commands/move_relative.py +102 -0
  354. opentrons/protocol_engine/commands/move_to_addressable_area.py +176 -0
  355. opentrons/protocol_engine/commands/move_to_addressable_area_for_drop_tip.py +198 -0
  356. opentrons/protocol_engine/commands/move_to_coordinates.py +107 -0
  357. opentrons/protocol_engine/commands/move_to_well.py +119 -0
  358. opentrons/protocol_engine/commands/movement_common.py +338 -0
  359. opentrons/protocol_engine/commands/pick_up_tip.py +241 -0
  360. opentrons/protocol_engine/commands/pipetting_common.py +443 -0
  361. opentrons/protocol_engine/commands/prepare_to_aspirate.py +121 -0
  362. opentrons/protocol_engine/commands/pressure_dispense.py +155 -0
  363. opentrons/protocol_engine/commands/reload_labware.py +90 -0
  364. opentrons/protocol_engine/commands/retract_axis.py +75 -0
  365. opentrons/protocol_engine/commands/robot/__init__.py +70 -0
  366. opentrons/protocol_engine/commands/robot/close_gripper_jaw.py +96 -0
  367. opentrons/protocol_engine/commands/robot/common.py +18 -0
  368. opentrons/protocol_engine/commands/robot/move_axes_relative.py +101 -0
  369. opentrons/protocol_engine/commands/robot/move_axes_to.py +100 -0
  370. opentrons/protocol_engine/commands/robot/move_to.py +94 -0
  371. opentrons/protocol_engine/commands/robot/open_gripper_jaw.py +86 -0
  372. opentrons/protocol_engine/commands/save_position.py +109 -0
  373. opentrons/protocol_engine/commands/seal_pipette_to_tip.py +353 -0
  374. opentrons/protocol_engine/commands/set_rail_lights.py +67 -0
  375. opentrons/protocol_engine/commands/set_status_bar.py +89 -0
  376. opentrons/protocol_engine/commands/temperature_module/__init__.py +46 -0
  377. opentrons/protocol_engine/commands/temperature_module/deactivate.py +86 -0
  378. opentrons/protocol_engine/commands/temperature_module/set_target_temperature.py +97 -0
  379. opentrons/protocol_engine/commands/temperature_module/wait_for_temperature.py +104 -0
  380. opentrons/protocol_engine/commands/thermocycler/__init__.py +152 -0
  381. opentrons/protocol_engine/commands/thermocycler/close_lid.py +87 -0
  382. opentrons/protocol_engine/commands/thermocycler/deactivate_block.py +80 -0
  383. opentrons/protocol_engine/commands/thermocycler/deactivate_lid.py +80 -0
  384. opentrons/protocol_engine/commands/thermocycler/open_lid.py +87 -0
  385. opentrons/protocol_engine/commands/thermocycler/run_extended_profile.py +171 -0
  386. opentrons/protocol_engine/commands/thermocycler/run_profile.py +124 -0
  387. opentrons/protocol_engine/commands/thermocycler/set_target_block_temperature.py +140 -0
  388. opentrons/protocol_engine/commands/thermocycler/set_target_lid_temperature.py +100 -0
  389. opentrons/protocol_engine/commands/thermocycler/wait_for_block_temperature.py +93 -0
  390. opentrons/protocol_engine/commands/thermocycler/wait_for_lid_temperature.py +89 -0
  391. opentrons/protocol_engine/commands/touch_tip.py +189 -0
  392. opentrons/protocol_engine/commands/unsafe/__init__.py +161 -0
  393. opentrons/protocol_engine/commands/unsafe/unsafe_blow_out_in_place.py +100 -0
  394. opentrons/protocol_engine/commands/unsafe/unsafe_drop_tip_in_place.py +121 -0
  395. opentrons/protocol_engine/commands/unsafe/unsafe_engage_axes.py +82 -0
  396. opentrons/protocol_engine/commands/unsafe/unsafe_place_labware.py +208 -0
  397. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_close_latch.py +94 -0
  398. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_manual_retrieve.py +295 -0
  399. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_open_latch.py +91 -0
  400. opentrons/protocol_engine/commands/unsafe/unsafe_stacker_prepare_shuttle.py +136 -0
  401. opentrons/protocol_engine/commands/unsafe/unsafe_ungrip_labware.py +77 -0
  402. opentrons/protocol_engine/commands/unsafe/update_position_estimators.py +90 -0
  403. opentrons/protocol_engine/commands/unseal_pipette_from_tip.py +153 -0
  404. opentrons/protocol_engine/commands/verify_tip_presence.py +100 -0
  405. opentrons/protocol_engine/commands/wait_for_duration.py +76 -0
  406. opentrons/protocol_engine/commands/wait_for_resume.py +75 -0
  407. opentrons/protocol_engine/create_protocol_engine.py +193 -0
  408. opentrons/protocol_engine/engine_support.py +28 -0
  409. opentrons/protocol_engine/error_recovery_policy.py +81 -0
  410. opentrons/protocol_engine/errors/__init__.py +191 -0
  411. opentrons/protocol_engine/errors/error_occurrence.py +182 -0
  412. opentrons/protocol_engine/errors/exceptions.py +1308 -0
  413. opentrons/protocol_engine/execution/__init__.py +50 -0
  414. opentrons/protocol_engine/execution/command_executor.py +216 -0
  415. opentrons/protocol_engine/execution/create_queue_worker.py +102 -0
  416. opentrons/protocol_engine/execution/door_watcher.py +119 -0
  417. opentrons/protocol_engine/execution/equipment.py +819 -0
  418. opentrons/protocol_engine/execution/error_recovery_hardware_state_synchronizer.py +101 -0
  419. opentrons/protocol_engine/execution/gantry_mover.py +686 -0
  420. opentrons/protocol_engine/execution/hardware_stopper.py +147 -0
  421. opentrons/protocol_engine/execution/heater_shaker_movement_flagger.py +207 -0
  422. opentrons/protocol_engine/execution/labware_movement.py +297 -0
  423. opentrons/protocol_engine/execution/movement.py +350 -0
  424. opentrons/protocol_engine/execution/pipetting.py +607 -0
  425. opentrons/protocol_engine/execution/queue_worker.py +86 -0
  426. opentrons/protocol_engine/execution/rail_lights.py +25 -0
  427. opentrons/protocol_engine/execution/run_control.py +33 -0
  428. opentrons/protocol_engine/execution/status_bar.py +34 -0
  429. opentrons/protocol_engine/execution/thermocycler_movement_flagger.py +188 -0
  430. opentrons/protocol_engine/execution/thermocycler_plate_lifter.py +81 -0
  431. opentrons/protocol_engine/execution/tip_handler.py +550 -0
  432. opentrons/protocol_engine/labware_offset_standardization.py +194 -0
  433. opentrons/protocol_engine/notes/__init__.py +17 -0
  434. opentrons/protocol_engine/notes/notes.py +59 -0
  435. opentrons/protocol_engine/plugins.py +104 -0
  436. opentrons/protocol_engine/protocol_engine.py +683 -0
  437. opentrons/protocol_engine/resources/__init__.py +26 -0
  438. opentrons/protocol_engine/resources/deck_configuration_provider.py +232 -0
  439. opentrons/protocol_engine/resources/deck_data_provider.py +94 -0
  440. opentrons/protocol_engine/resources/file_provider.py +161 -0
  441. opentrons/protocol_engine/resources/fixture_validation.py +68 -0
  442. opentrons/protocol_engine/resources/labware_data_provider.py +106 -0
  443. opentrons/protocol_engine/resources/labware_validation.py +73 -0
  444. opentrons/protocol_engine/resources/model_utils.py +32 -0
  445. opentrons/protocol_engine/resources/module_data_provider.py +44 -0
  446. opentrons/protocol_engine/resources/ot3_validation.py +21 -0
  447. opentrons/protocol_engine/resources/pipette_data_provider.py +379 -0
  448. opentrons/protocol_engine/slot_standardization.py +128 -0
  449. opentrons/protocol_engine/state/__init__.py +1 -0
  450. opentrons/protocol_engine/state/_abstract_store.py +27 -0
  451. opentrons/protocol_engine/state/_axis_aligned_bounding_box.py +50 -0
  452. opentrons/protocol_engine/state/_labware_origin_math.py +636 -0
  453. opentrons/protocol_engine/state/_move_types.py +83 -0
  454. opentrons/protocol_engine/state/_well_math.py +193 -0
  455. opentrons/protocol_engine/state/addressable_areas.py +699 -0
  456. opentrons/protocol_engine/state/command_history.py +309 -0
  457. opentrons/protocol_engine/state/commands.py +1164 -0
  458. opentrons/protocol_engine/state/config.py +39 -0
  459. opentrons/protocol_engine/state/files.py +57 -0
  460. opentrons/protocol_engine/state/fluid_stack.py +138 -0
  461. opentrons/protocol_engine/state/geometry.py +2408 -0
  462. opentrons/protocol_engine/state/inner_well_math_utils.py +548 -0
  463. opentrons/protocol_engine/state/labware.py +1432 -0
  464. opentrons/protocol_engine/state/liquid_classes.py +82 -0
  465. opentrons/protocol_engine/state/liquids.py +73 -0
  466. opentrons/protocol_engine/state/module_substates/__init__.py +45 -0
  467. opentrons/protocol_engine/state/module_substates/absorbance_reader_substate.py +35 -0
  468. opentrons/protocol_engine/state/module_substates/flex_stacker_substate.py +112 -0
  469. opentrons/protocol_engine/state/module_substates/heater_shaker_module_substate.py +115 -0
  470. opentrons/protocol_engine/state/module_substates/magnetic_block_substate.py +17 -0
  471. opentrons/protocol_engine/state/module_substates/magnetic_module_substate.py +65 -0
  472. opentrons/protocol_engine/state/module_substates/temperature_module_substate.py +67 -0
  473. opentrons/protocol_engine/state/module_substates/thermocycler_module_substate.py +163 -0
  474. opentrons/protocol_engine/state/modules.py +1515 -0
  475. opentrons/protocol_engine/state/motion.py +373 -0
  476. opentrons/protocol_engine/state/pipettes.py +905 -0
  477. opentrons/protocol_engine/state/state.py +421 -0
  478. opentrons/protocol_engine/state/state_summary.py +36 -0
  479. opentrons/protocol_engine/state/tips.py +420 -0
  480. opentrons/protocol_engine/state/update_types.py +904 -0
  481. opentrons/protocol_engine/state/wells.py +290 -0
  482. opentrons/protocol_engine/types/__init__.py +310 -0
  483. opentrons/protocol_engine/types/automatic_tip_selection.py +39 -0
  484. opentrons/protocol_engine/types/command_annotations.py +53 -0
  485. opentrons/protocol_engine/types/deck_configuration.py +81 -0
  486. opentrons/protocol_engine/types/execution.py +96 -0
  487. opentrons/protocol_engine/types/hardware_passthrough.py +25 -0
  488. opentrons/protocol_engine/types/instrument.py +47 -0
  489. opentrons/protocol_engine/types/instrument_sensors.py +47 -0
  490. opentrons/protocol_engine/types/labware.py +131 -0
  491. opentrons/protocol_engine/types/labware_movement.py +22 -0
  492. opentrons/protocol_engine/types/labware_offset_location.py +111 -0
  493. opentrons/protocol_engine/types/labware_offset_vector.py +16 -0
  494. opentrons/protocol_engine/types/liquid.py +40 -0
  495. opentrons/protocol_engine/types/liquid_class.py +59 -0
  496. opentrons/protocol_engine/types/liquid_handling.py +13 -0
  497. opentrons/protocol_engine/types/liquid_level_detection.py +191 -0
  498. opentrons/protocol_engine/types/location.py +194 -0
  499. opentrons/protocol_engine/types/module.py +310 -0
  500. opentrons/protocol_engine/types/partial_tip_configuration.py +76 -0
  501. opentrons/protocol_engine/types/run_time_parameters.py +133 -0
  502. opentrons/protocol_engine/types/tip.py +18 -0
  503. opentrons/protocol_engine/types/util.py +21 -0
  504. opentrons/protocol_engine/types/well_position.py +124 -0
  505. opentrons/protocol_reader/__init__.py +37 -0
  506. opentrons/protocol_reader/extract_labware_definitions.py +66 -0
  507. opentrons/protocol_reader/file_format_validator.py +152 -0
  508. opentrons/protocol_reader/file_hasher.py +27 -0
  509. opentrons/protocol_reader/file_identifier.py +284 -0
  510. opentrons/protocol_reader/file_reader_writer.py +90 -0
  511. opentrons/protocol_reader/input_file.py +16 -0
  512. opentrons/protocol_reader/protocol_files_invalid_error.py +6 -0
  513. opentrons/protocol_reader/protocol_reader.py +188 -0
  514. opentrons/protocol_reader/protocol_source.py +124 -0
  515. opentrons/protocol_reader/role_analyzer.py +86 -0
  516. opentrons/protocol_runner/__init__.py +26 -0
  517. opentrons/protocol_runner/create_simulating_orchestrator.py +118 -0
  518. opentrons/protocol_runner/json_file_reader.py +55 -0
  519. opentrons/protocol_runner/json_translator.py +314 -0
  520. opentrons/protocol_runner/legacy_command_mapper.py +852 -0
  521. opentrons/protocol_runner/legacy_context_plugin.py +116 -0
  522. opentrons/protocol_runner/protocol_runner.py +530 -0
  523. opentrons/protocol_runner/python_protocol_wrappers.py +179 -0
  524. opentrons/protocol_runner/run_orchestrator.py +496 -0
  525. opentrons/protocol_runner/task_queue.py +95 -0
  526. opentrons/protocols/__init__.py +6 -0
  527. opentrons/protocols/advanced_control/__init__.py +0 -0
  528. opentrons/protocols/advanced_control/common.py +38 -0
  529. opentrons/protocols/advanced_control/mix.py +60 -0
  530. opentrons/protocols/advanced_control/transfers/__init__.py +0 -0
  531. opentrons/protocols/advanced_control/transfers/common.py +180 -0
  532. opentrons/protocols/advanced_control/transfers/transfer.py +972 -0
  533. opentrons/protocols/advanced_control/transfers/transfer_liquid_utils.py +231 -0
  534. opentrons/protocols/api_support/__init__.py +0 -0
  535. opentrons/protocols/api_support/constants.py +8 -0
  536. opentrons/protocols/api_support/deck_type.py +110 -0
  537. opentrons/protocols/api_support/definitions.py +18 -0
  538. opentrons/protocols/api_support/instrument.py +151 -0
  539. opentrons/protocols/api_support/labware_like.py +233 -0
  540. opentrons/protocols/api_support/tip_tracker.py +175 -0
  541. opentrons/protocols/api_support/types.py +32 -0
  542. opentrons/protocols/api_support/util.py +403 -0
  543. opentrons/protocols/bundle.py +89 -0
  544. opentrons/protocols/duration/__init__.py +4 -0
  545. opentrons/protocols/duration/errors.py +5 -0
  546. opentrons/protocols/duration/estimator.py +628 -0
  547. opentrons/protocols/execution/__init__.py +0 -0
  548. opentrons/protocols/execution/dev_types.py +181 -0
  549. opentrons/protocols/execution/errors.py +40 -0
  550. opentrons/protocols/execution/execute.py +84 -0
  551. opentrons/protocols/execution/execute_json_v3.py +275 -0
  552. opentrons/protocols/execution/execute_json_v4.py +359 -0
  553. opentrons/protocols/execution/execute_json_v5.py +28 -0
  554. opentrons/protocols/execution/execute_python.py +169 -0
  555. opentrons/protocols/execution/json_dispatchers.py +87 -0
  556. opentrons/protocols/execution/types.py +7 -0
  557. opentrons/protocols/geometry/__init__.py +0 -0
  558. opentrons/protocols/geometry/planning.py +297 -0
  559. opentrons/protocols/labware.py +312 -0
  560. opentrons/protocols/models/__init__.py +0 -0
  561. opentrons/protocols/models/json_protocol.py +679 -0
  562. opentrons/protocols/parameters/__init__.py +0 -0
  563. opentrons/protocols/parameters/csv_parameter_definition.py +77 -0
  564. opentrons/protocols/parameters/csv_parameter_interface.py +96 -0
  565. opentrons/protocols/parameters/exceptions.py +34 -0
  566. opentrons/protocols/parameters/parameter_definition.py +272 -0
  567. opentrons/protocols/parameters/types.py +17 -0
  568. opentrons/protocols/parameters/validation.py +267 -0
  569. opentrons/protocols/parse.py +671 -0
  570. opentrons/protocols/types.py +159 -0
  571. opentrons/py.typed +0 -0
  572. opentrons/resources/scripts/lpc21isp +0 -0
  573. opentrons/resources/smoothie-edge-8414642.hex +23010 -0
  574. opentrons/simulate.py +1065 -0
  575. opentrons/system/__init__.py +6 -0
  576. opentrons/system/camera.py +51 -0
  577. opentrons/system/log_control.py +59 -0
  578. opentrons/system/nmcli.py +856 -0
  579. opentrons/system/resin.py +24 -0
  580. opentrons/system/smoothie_update.py +15 -0
  581. opentrons/system/wifi.py +204 -0
  582. opentrons/tools/__init__.py +0 -0
  583. opentrons/tools/args_handler.py +22 -0
  584. opentrons/tools/write_pipette_memory.py +157 -0
  585. opentrons/types.py +618 -0
  586. opentrons/util/__init__.py +1 -0
  587. opentrons/util/async_helpers.py +166 -0
  588. opentrons/util/broker.py +84 -0
  589. opentrons/util/change_notifier.py +47 -0
  590. opentrons/util/entrypoint_util.py +278 -0
  591. opentrons/util/get_union_elements.py +26 -0
  592. opentrons/util/helpers.py +6 -0
  593. opentrons/util/linal.py +178 -0
  594. opentrons/util/logging_config.py +265 -0
  595. opentrons/util/logging_queue_handler.py +61 -0
  596. opentrons/util/performance_helpers.py +157 -0
  597. opentrons-8.6.0.dist-info/METADATA +37 -0
  598. opentrons-8.6.0.dist-info/RECORD +601 -0
  599. opentrons-8.6.0.dist-info/WHEEL +4 -0
  600. opentrons-8.6.0.dist-info/entry_points.txt +3 -0
  601. opentrons-8.6.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,1030 @@
1
+ """Shared code for managing pipette configuration and storage."""
2
+ from dataclasses import dataclass
3
+ import logging
4
+ from typing import (
5
+ Dict,
6
+ Optional,
7
+ Tuple,
8
+ Any,
9
+ cast,
10
+ List,
11
+ TypeVar,
12
+ )
13
+ from typing_extensions import Final
14
+ import numpy
15
+ from opentrons_shared_data.pipette.types import UlPerMmAction
16
+
17
+ from opentrons_shared_data.errors.exceptions import (
18
+ CommandPreconditionViolated,
19
+ CommandParameterLimitViolated,
20
+ UnexpectedTipRemovalError,
21
+ UnexpectedTipAttachError,
22
+ )
23
+ from opentrons_shared_data.pipette.pipette_definition import (
24
+ liquid_class_for_volume_between_default_and_defaultlowvolume,
25
+ PressFitPickUpTipConfiguration,
26
+ CamActionPickUpTipConfiguration,
27
+ )
28
+
29
+ from opentrons import types as top_types
30
+ from opentrons.hardware_control.types import (
31
+ CriticalPoint,
32
+ HardwareAction,
33
+ Axis,
34
+ OT3Mount,
35
+ TipScrapeType,
36
+ )
37
+ from opentrons.hardware_control.constants import (
38
+ SHAKE_OFF_TIPS_SPEED,
39
+ SHAKE_OFF_TIPS_PICKUP_DISTANCE,
40
+ DROP_TIP_RELEASE_DISTANCE,
41
+ SHAKE_OFF_TIPS_DROP_DISTANCE,
42
+ )
43
+
44
+ from opentrons.hardware_control.dev_types import PipetteDict
45
+ from .pipette import Pipette
46
+ from .instrument_calibration import (
47
+ PipetteOffsetSummary,
48
+ PipetteOffsetByPipetteMount,
49
+ check_instrument_offset_reasonability,
50
+ )
51
+
52
+
53
+ MOD_LOG = logging.getLogger(__name__)
54
+
55
+ # TODO both pipette handlers should be combined once the pipette configurations
56
+ # are unified AND we separate out the concept of changing pipette state versus static state
57
+ HOME_POSITION: Final[float] = 230.15
58
+
59
+ MountType = TypeVar("MountType", top_types.Mount, OT3Mount)
60
+ InstrumentsByMount = Dict[MountType, Optional[Pipette]]
61
+ PipetteHandlingData = Tuple[Pipette, OT3Mount]
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class LiquidActionSpec:
66
+ axis: Axis
67
+ volume: float
68
+ plunger_distance: float
69
+ speed: float
70
+ acceleration: float
71
+ instr: Pipette
72
+ current: float
73
+
74
+
75
+ @dataclass(frozen=True)
76
+ class TipActionMoveSpec:
77
+ distance: float
78
+ currents: Optional[Dict[Axis, float]]
79
+ speed: Optional[
80
+ float
81
+ ] # allow speed for a movement to default to its axes' speed settings
82
+ scrape_axis: Optional[Axis] = None
83
+ # add a scrape motion in the middle of a tip drop
84
+
85
+
86
+ @dataclass(frozen=True)
87
+ class TipActionSpec:
88
+ tip_action_moves: List[TipActionMoveSpec]
89
+ shake_off_moves: List[Tuple[top_types.Point, Optional[float]]]
90
+ z_distance_to_tiprack: Optional[float] = None
91
+ ending_z_retract_distance: Optional[float] = None
92
+
93
+
94
+ class OT3PipetteHandler:
95
+ IHP_LOG = MOD_LOG.getChild("InstrumentHandler")
96
+
97
+ def __init__(self, attached_instruments: InstrumentsByMount[OT3Mount]):
98
+ assert attached_instruments
99
+ self._attached_instruments: InstrumentsByMount[OT3Mount] = attached_instruments
100
+ self._ihp_log = self.__class__.IHP_LOG
101
+
102
+ def reset_instrument(self, mount: Optional[OT3Mount] = None) -> None:
103
+ """
104
+ Reset the internal state of a pipette by its mount, without doing
105
+ any lower level reconfiguration. This is useful to make sure that no
106
+ settings changes from a protocol persist.
107
+
108
+ :param mount: If specified, reset that mount. If not specified,
109
+ reset both
110
+ """
111
+
112
+ # need to have a reset function on the pipette
113
+ def _reset(m: OT3Mount) -> None:
114
+ self._ihp_log.info(f"Resetting configuration for {m}")
115
+ p = self._attached_instruments[m]
116
+ if not p:
117
+ return
118
+ p.reset_pipette_offset(OT3Mount.from_mount(m), to_default=False)
119
+ p.reload_configurations()
120
+ p.reset_state()
121
+
122
+ if not mount:
123
+ for m in type(list(self._attached_instruments.keys())[0]):
124
+ _reset(m)
125
+ else:
126
+ _reset(mount)
127
+
128
+ def get_instrument_offset(self, mount: OT3Mount) -> Optional[PipetteOffsetSummary]:
129
+ """Get the specified pipette's offset."""
130
+ assert mount != OT3Mount.GRIPPER, "Wrong mount type to fetch pipette offset"
131
+ try:
132
+ pipette = self.get_pipette(mount)
133
+ except top_types.PipetteNotAttachedError:
134
+ return None
135
+ return self._return_augmented_offset_data(
136
+ pipette, mount, pipette.pipette_offset
137
+ )
138
+
139
+ def reset_instrument_offset(self, mount: OT3Mount, to_default: bool) -> None:
140
+ """
141
+ Temporarily reset the pipette offset to default values.
142
+ :param mount: Modify the given mount.
143
+ """
144
+ pipette = self.get_pipette(mount)
145
+ pipette.reset_pipette_offset(mount, to_default)
146
+
147
+ def save_instrument_offset(
148
+ self, mount: OT3Mount, delta: top_types.Point
149
+ ) -> PipetteOffsetSummary:
150
+ """
151
+ Save a new instrument offset the pipette offset to a particular value.
152
+ :param mount: Modify the given mount.
153
+ :param delta: The offset to set for the pipette.
154
+ """
155
+ pipette = self.get_pipette(mount)
156
+ offset_data = pipette.save_pipette_offset(mount, delta)
157
+ return self._return_augmented_offset_data(pipette, mount, offset_data)
158
+
159
+ def _return_augmented_offset_data(
160
+ self,
161
+ pipette: Pipette,
162
+ mount: OT3Mount,
163
+ offset_data: PipetteOffsetByPipetteMount,
164
+ ) -> PipetteOffsetSummary:
165
+ if mount == OT3Mount.LEFT:
166
+ other_pipette = self._attached_instruments.get(OT3Mount.RIGHT, None)
167
+ if other_pipette:
168
+ other_offset = other_pipette.pipette_offset.offset
169
+ else:
170
+ other_offset = top_types.Point(0, 0, 0)
171
+ reasonability = check_instrument_offset_reasonability(
172
+ offset_data.offset, other_offset
173
+ )
174
+ else:
175
+ other_pipette = self._attached_instruments.get(OT3Mount.LEFT, None)
176
+ if other_pipette:
177
+ other_offset = other_pipette.pipette_offset.offset
178
+ else:
179
+ other_offset = top_types.Point(0, 0, 0)
180
+ reasonability = check_instrument_offset_reasonability(
181
+ other_offset, offset_data.offset
182
+ )
183
+ return PipetteOffsetSummary(
184
+ offset=offset_data.offset,
185
+ source=offset_data.source,
186
+ status=offset_data.status,
187
+ last_modified=offset_data.last_modified,
188
+ reasonability_check_failures=reasonability,
189
+ )
190
+
191
+ # TODO(mc, 2022-01-11): change returned map value type to `Optional[PipetteDict]`
192
+ # instead of potentially returning an empty dict
193
+ # For compatibility purposes only right now. We should change this
194
+ # as soon as we can modify the /pipettes endpoint.
195
+ def get_attached_instruments(self) -> Dict[OT3Mount, PipetteDict]:
196
+ """Get the status dicts of the cached attached instruments.
197
+
198
+ Also available as :py:meth:`get_attached_instruments`.
199
+
200
+ This returns a dictified version of the
201
+ :py:class:`hardware_control.instruments.pipette.Pipette` as a dict keyed by
202
+ the :py:class:`top_types.Mount` to which the pipette is attached.
203
+ If no pipette is attached on a given mount, the mount key will
204
+ still be present but will have the value ``None``.
205
+
206
+ Note that this is only a query of a cached value; to actively scan
207
+ for changes, use :py:meth:`cache_instruments`. This process deactivates
208
+ the motors and should be used sparingly.
209
+ """
210
+ return {
211
+ m: self.get_attached_instrument(m)
212
+ for m in self._attached_instruments.keys()
213
+ }
214
+
215
+ # TODO(mc, 2022-01-11): change return type to `Optional[PipetteDict]` instead
216
+ # of potentially returning an empty dict
217
+ def get_attached_instrument(self, mount: OT3Mount) -> PipetteDict:
218
+ # TODO (lc 12-05-2022) Kill this code ASAP
219
+ instr = self._attached_instruments[mount]
220
+ result: Dict[str, Any] = {}
221
+ if instr:
222
+ configs = [
223
+ "name",
224
+ "aspirate_flow_rate",
225
+ "dispense_flow_rate",
226
+ "pipette_id",
227
+ "current_volume",
228
+ "display_name",
229
+ "tip_length",
230
+ "model",
231
+ "blow_out_flow_rate",
232
+ "working_volume",
233
+ "tip_overlap",
234
+ "versioned_tip_overlap",
235
+ "available_volume",
236
+ "return_tip_height",
237
+ "default_aspirate_flow_rates",
238
+ "default_blow_out_flow_rates",
239
+ "default_dispense_flow_rates",
240
+ "back_compat_names",
241
+ "supported_tips",
242
+ "lld_settings",
243
+ "available_sensors",
244
+ ]
245
+
246
+ instr_dict = instr.as_dict()
247
+ # TODO (spp, 2021-08-27): Revisit this logic. Why do we need to build
248
+ # this dict newly every time? Any why only a few items are being updated?
249
+ for key in configs:
250
+ result[key] = instr_dict[key]
251
+
252
+ result["current_nozzle_map"] = instr.nozzle_manager.current_configuration
253
+ result["min_volume"] = instr.liquid_class.min_volume
254
+ result["max_volume"] = instr.liquid_class.max_volume
255
+ result["channels"] = instr._max_channels.value
256
+ result["has_tip"] = instr.has_tip
257
+ result["tip_length"] = instr.current_tip_length
258
+ result["aspirate_speed"] = self.plunger_speed(
259
+ instr, instr.aspirate_flow_rate, "aspirate"
260
+ )
261
+ result["dispense_speed"] = self.plunger_speed(
262
+ instr, instr.dispense_flow_rate, "dispense"
263
+ )
264
+ result["blow_out_speed"] = self.plunger_speed(
265
+ instr, instr.blow_out_flow_rate, "dispense"
266
+ )
267
+ result["ready_to_aspirate"] = instr.ready_to_aspirate
268
+
269
+ result["default_blow_out_speeds"] = {
270
+ alvl: self.plunger_speed(instr, fr, "blowout")
271
+ for alvl, fr in instr.blow_out_flow_rates_lookup.items()
272
+ }
273
+
274
+ result["default_dispense_speeds"] = {
275
+ alvl: self.plunger_speed(instr, fr, "dispense")
276
+ for alvl, fr in instr.dispense_flow_rates_lookup.items()
277
+ }
278
+ result["default_aspirate_speeds"] = {
279
+ alvl: self.plunger_speed(instr, fr, "aspirate")
280
+ for alvl, fr in instr.aspirate_flow_rates_lookup.items()
281
+ }
282
+ result[
283
+ "default_push_out_volume"
284
+ ] = instr.active_tip_settings.default_push_out_volume
285
+ result[
286
+ "pipette_bounding_box_offsets"
287
+ ] = instr.config.pipette_bounding_box_offsets
288
+ result["lld_settings"] = instr.config.lld_settings
289
+ result["plunger_positions"] = {
290
+ "top": instr.plunger_positions.top,
291
+ "bottom": instr.plunger_positions.bottom,
292
+ "blow_out": instr.plunger_positions.blow_out,
293
+ "drop_tip": instr.plunger_positions.drop_tip,
294
+ }
295
+ result["shaft_ul_per_mm"] = instr.config.shaft_ul_per_mm
296
+ result["available_sensors"] = instr.config.available_sensors
297
+ return cast(PipetteDict, result)
298
+
299
+ @property
300
+ def attached_instruments(self) -> Dict[OT3Mount, PipetteDict]:
301
+ return self.get_attached_instruments()
302
+
303
+ @property
304
+ def hardware_instruments(self) -> InstrumentsByMount[OT3Mount]:
305
+ """Do not write new code that uses this."""
306
+ return self._attached_instruments
307
+
308
+ def set_current_tiprack_diameter(
309
+ self, mount: OT3Mount, tiprack_diameter: float
310
+ ) -> None:
311
+ instr = self.get_pipette(mount)
312
+ self._ihp_log.info(
313
+ "Updating tip rack diameter on pipette mount: "
314
+ f"{mount}, tip diameter: {tiprack_diameter} mm"
315
+ )
316
+ instr.current_tiprack_diameter = tiprack_diameter
317
+
318
+ def set_working_volume(self, mount: OT3Mount, tip_volume: float) -> None:
319
+ instr = self.get_pipette(mount)
320
+ if not instr:
321
+ raise top_types.PipetteNotAttachedError(
322
+ "No pipette attached to {} mount".format(mount.name)
323
+ )
324
+ self._ihp_log.info(
325
+ "Updating working volume on pipette mount:"
326
+ f"{mount}, tip volume: {tip_volume} ul"
327
+ )
328
+ instr.working_volume = tip_volume
329
+
330
+ def calibrate_plunger(
331
+ self,
332
+ mount: OT3Mount,
333
+ top: Optional[float] = None,
334
+ bottom: Optional[float] = None,
335
+ blow_out: Optional[float] = None,
336
+ drop_tip: Optional[float] = None,
337
+ ) -> None:
338
+ """
339
+ Set calibration values for the pipette plunger.
340
+ This can be called multiple times as the user sets each value,
341
+ or you can set them all at once.
342
+ :param top: Touching but not engaging the plunger.
343
+ :param bottom: Must be above the pipette's physical hard-stop, while
344
+ still leaving enough room for 'blow_out'
345
+ :param blow_out: Plunger is pushed down enough to expel all liquids.
346
+ :param drop_tip: Position that causes the tip to be released from the
347
+ pipette
348
+ """
349
+ instr = self.get_pipette(mount)
350
+ pos_dict: Dict[str, float] = {
351
+ "top": instr.plunger_positions.top,
352
+ "bottom": instr.plunger_positions.bottom,
353
+ "blow_out": instr.plunger_positions.blow_out,
354
+ "drop_tip": instr.plunger_positions.drop_tip,
355
+ }
356
+ if top is not None:
357
+ pos_dict["top"] = top
358
+ if bottom is not None:
359
+ pos_dict["bottom"] = bottom
360
+ if blow_out is not None:
361
+ pos_dict["blow_out"] = blow_out
362
+ if drop_tip is not None:
363
+ pos_dict["drop_tip"] = drop_tip
364
+ instr.update_config_item(pos_dict)
365
+
366
+ def set_flow_rate(
367
+ self,
368
+ mount: OT3Mount,
369
+ aspirate: Optional[float] = None,
370
+ dispense: Optional[float] = None,
371
+ blow_out: Optional[float] = None,
372
+ ) -> None:
373
+ this_pipette = self.get_pipette(mount)
374
+ if aspirate:
375
+ this_pipette.aspirate_flow_rate = aspirate
376
+ if dispense:
377
+ this_pipette.dispense_flow_rate = dispense
378
+ if blow_out:
379
+ this_pipette.blow_out_flow_rate = blow_out
380
+
381
+ def set_pipette_speed(
382
+ self,
383
+ mount: OT3Mount,
384
+ aspirate: Optional[float] = None,
385
+ dispense: Optional[float] = None,
386
+ blow_out: Optional[float] = None,
387
+ ) -> None:
388
+ this_pipette = self.get_pipette(mount)
389
+ if aspirate:
390
+ this_pipette.aspirate_flow_rate = self.plunger_flowrate(
391
+ this_pipette, aspirate, "aspirate"
392
+ )
393
+ if dispense:
394
+ this_pipette.dispense_flow_rate = self.plunger_flowrate(
395
+ this_pipette, dispense, "dispense"
396
+ )
397
+ if blow_out:
398
+ this_pipette.blow_out_flow_rate = self.plunger_flowrate(
399
+ this_pipette, blow_out, "blowout"
400
+ )
401
+
402
+ def instrument_max_height(
403
+ self,
404
+ mount: OT3Mount,
405
+ retract_distance: float,
406
+ critical_point: Optional[CriticalPoint],
407
+ ) -> float:
408
+ """Return max achievable height of the attached instrument
409
+ based on the current critical point
410
+ """
411
+ cp = self.critical_point_for(mount, critical_point)
412
+
413
+ max_height = HOME_POSITION - retract_distance + cp.z
414
+
415
+ return max_height
416
+
417
+ async def reset(self) -> None:
418
+ self._attached_instruments = {
419
+ k: None for k in self._attached_instruments.keys()
420
+ }
421
+
422
+ async def update_nozzle_configuration(
423
+ self,
424
+ mount: MountType,
425
+ back_left_nozzle: str,
426
+ front_right_nozzle: str,
427
+ starting_nozzle: Optional[str] = None,
428
+ ) -> None:
429
+ instr = self._attached_instruments[OT3Mount.from_mount(mount)]
430
+ if instr:
431
+ instr.update_nozzle_configuration(
432
+ back_left_nozzle, front_right_nozzle, starting_nozzle
433
+ )
434
+
435
+ async def reset_nozzle_configuration(self, mount: OT3Mount) -> None:
436
+ instr = self._attached_instruments[OT3Mount.from_mount(mount)]
437
+ if instr:
438
+ instr.reset_nozzle_configuration()
439
+
440
+ def add_tip(self, mount: OT3Mount, tip_length: float) -> None:
441
+ instr = self._attached_instruments[mount]
442
+ attached = self.attached_instruments
443
+ instr_dict = attached[mount]
444
+ if instr and not instr.has_tip:
445
+ instr.add_tip(tip_length=tip_length)
446
+ # TODO (spp, 2021-08-27): These items are being updated in a local copy
447
+ # of the PipetteDict, which gets thrown away. Fix this.
448
+ instr_dict["has_tip"] = True
449
+ instr_dict["tip_length"] = tip_length
450
+ else:
451
+ self._ihp_log.warning(
452
+ "attach tip called while tip already attached to {instr}"
453
+ )
454
+
455
+ def cache_tip(self, mount: OT3Mount, tip_length: float) -> None:
456
+ instrument = self.get_pipette(mount)
457
+ if instrument.has_tip:
458
+ # instrument.add_tip() would raise an AssertionError if we tried to overwrite an existing tip.
459
+ instrument.remove_tip()
460
+ instrument.add_tip(tip_length=tip_length)
461
+ instrument.set_current_volume(0)
462
+
463
+ def remove_tip(self, mount: OT3Mount) -> None:
464
+ instr = self._attached_instruments[mount]
465
+ attached = self.attached_instruments
466
+ instr_dict = attached[mount]
467
+ if instr and instr.has_tip:
468
+ instr.remove_tip()
469
+ # TODO (spp, 2021-08-27): These items are being updated in a local copy
470
+ # of the PipetteDict, which gets thrown away. Fix this.
471
+ instr_dict["has_tip"] = False
472
+ instr_dict["tip_length"] = 0.0
473
+ else:
474
+ self._ihp_log.warning("detach tip called with no tip")
475
+
476
+ def critical_point_for(
477
+ self, mount: OT3Mount, cp_override: Optional[CriticalPoint] = None
478
+ ) -> top_types.Point:
479
+ """Return the current critical point of the specified mount.
480
+
481
+ The mount's critical point is the position of the mount itself, if no
482
+ pipette is attached, or the pipette's critical point (which depends on
483
+ tip status).
484
+
485
+ If `cp_override` is specified, and that critical point actually exists,
486
+ it will be used instead. Invalid `cp_override`s are ignored.
487
+ """
488
+ pip = self._attached_instruments[OT3Mount.from_mount(mount)]
489
+ if pip is not None and cp_override != CriticalPoint.MOUNT:
490
+ return pip.critical_point(cp_override)
491
+ else:
492
+ return top_types.Point(0, 0, 0)
493
+
494
+ def ready_for_tip_action(
495
+ self, target: Pipette, action: HardwareAction, mount: OT3Mount
496
+ ) -> None:
497
+ if not target.has_tip:
498
+ raise UnexpectedTipRemovalError(str(action), target.name, mount.name)
499
+ if (
500
+ action == HardwareAction.ASPIRATE
501
+ and target.current_volume == 0
502
+ and not target.ready_to_aspirate
503
+ ):
504
+ raise RuntimeError("Pipette not ready to aspirate")
505
+ self._ihp_log.debug(f"{action} on {target.name}")
506
+
507
+ def plunger_position(
508
+ self,
509
+ instr: Pipette,
510
+ ul: float,
511
+ action: "UlPerMmAction",
512
+ correction_volume: float = 0.0,
513
+ ) -> float:
514
+ if ul == 0:
515
+ position = instr.plunger_positions.bottom
516
+ else:
517
+ multiplier = 1.0 + (correction_volume / ul)
518
+ mm_dist_from_bottom = ul / instr.ul_per_mm(ul, action)
519
+ mm_dist_from_bottom_corrected = mm_dist_from_bottom * multiplier
520
+ position = instr.plunger_positions.bottom - mm_dist_from_bottom_corrected
521
+ return round(position, 6)
522
+
523
+ def plunger_speed(
524
+ self, instr: Pipette, ul_per_s: float, action: "UlPerMmAction"
525
+ ) -> float:
526
+ mm_per_s = ul_per_s / instr.ul_per_mm(instr.working_volume, action)
527
+ return round(mm_per_s, 6)
528
+
529
+ def plunger_flowrate(
530
+ self, instr: Pipette, mm_per_s: float, action: "UlPerMmAction"
531
+ ) -> float:
532
+ ul_per_s = mm_per_s * instr.ul_per_mm(instr.working_volume, action)
533
+ return round(ul_per_s, 6)
534
+
535
+ def plunger_acceleration(self, instr: Pipette, ul_per_s_per_s: float) -> float:
536
+ # using nominal ul/mm, to make sure accelerations are always the same
537
+ # regardless of volume being aspirated/dispensed
538
+ mm_per_s_per_s = ul_per_s_per_s / instr.config.shaft_ul_per_mm
539
+ return round(mm_per_s_per_s, 6)
540
+
541
+ def plan_check_aspirate(
542
+ self,
543
+ mount: OT3Mount,
544
+ volume: Optional[float],
545
+ rate: float,
546
+ correction_volume: float = 0.0,
547
+ ) -> Optional[LiquidActionSpec]:
548
+ """Check preconditions for aspirate, parse args, and calculate positions.
549
+
550
+ While the mechanics of issuing an aspirate move itself are left to child
551
+ classes, determining things like aspiration volume from the allowed argument
552
+ types is invariant between machines, and this method gathers that functionality.
553
+
554
+ Coalesce
555
+ - Optional volumes
556
+
557
+ Check
558
+ - Aspiration volumes compared to max and remaining
559
+
560
+ Calculate
561
+ - Plunger distances (possibly calling an overridden plunger_volume)
562
+ """
563
+ instrument = self.get_pipette(mount)
564
+ self.ready_for_tip_action(instrument, HardwareAction.ASPIRATE, mount)
565
+ if volume is None:
566
+ self._ihp_log.debug(
567
+ "No aspirate volume defined. Aspirating up to "
568
+ "max_volume for the pipette"
569
+ )
570
+ asp_vol = instrument.available_volume
571
+ else:
572
+ asp_vol = volume
573
+
574
+ if asp_vol == 0:
575
+ return None
576
+
577
+ assert instrument.ok_to_add_volume(
578
+ asp_vol
579
+ ), "Cannot aspirate more than pipette max volume"
580
+
581
+ dist = self.plunger_position(
582
+ instr=instrument,
583
+ ul=instrument.current_volume + asp_vol,
584
+ action="aspirate",
585
+ correction_volume=correction_volume,
586
+ )
587
+ speed = self.plunger_speed(
588
+ instrument, instrument.aspirate_flow_rate * rate, "aspirate"
589
+ )
590
+ acceleration = self.plunger_acceleration(
591
+ instrument, instrument.flow_acceleration
592
+ )
593
+
594
+ return LiquidActionSpec(
595
+ axis=Axis.of_main_tool_actuator(mount),
596
+ volume=asp_vol,
597
+ plunger_distance=dist,
598
+ speed=speed,
599
+ acceleration=acceleration,
600
+ instr=instrument,
601
+ current=instrument.plunger_motor_current.run,
602
+ )
603
+
604
+ def plan_check_dispense(
605
+ self,
606
+ mount: OT3Mount,
607
+ volume: Optional[float],
608
+ rate: float,
609
+ push_out: Optional[float],
610
+ is_full_dispense: bool,
611
+ correction_volume: float = 0.0,
612
+ ) -> Optional[LiquidActionSpec]:
613
+ """Check preconditions for dispense, parse args, and calculate positions.
614
+
615
+ While the mechanics of issuing a dispense move itself are left to child
616
+ classes, determining things like dispense volume from the allowed argument
617
+ types is invariant between machines, and this method gathers that functionality.
618
+
619
+ Coalesce
620
+ - Optional volumes
621
+
622
+ Check
623
+ - Dispense volumes compared to max and remaining
624
+
625
+ Calculate
626
+ - Plunger distances (possibly calling an overridden plunger_volume)
627
+ """
628
+
629
+ instrument = self.get_pipette(mount)
630
+ self.ready_for_tip_action(instrument, HardwareAction.DISPENSE, mount)
631
+
632
+ if volume is None:
633
+ disp_vol = instrument.current_volume
634
+ self._ihp_log.debug(
635
+ "No dispense volume specified. Dispensing all "
636
+ "remaining liquid ({}uL) from pipette".format(disp_vol)
637
+ )
638
+ else:
639
+ disp_vol = volume
640
+
641
+ # Ensure we don't dispense more than the current volume.
642
+ #
643
+ # This clamping is inconsistent with plan_check_aspirate(), which asserts
644
+ # that its input is in bounds instead of clamping it. This is to match a quirk
645
+ # of the OT-2 version of this class. Protocol Engine does its own clamping,
646
+ # so we don't expect this to trigger in practice.
647
+ disp_vol = min(instrument.current_volume, disp_vol)
648
+
649
+ # TODO (Ryan): Remove this check in the future.
650
+ # we moved this logic up to protocol_engine but replacing with this check to make sure
651
+ # we don't accidentally call this incorrectly from somewhere else.
652
+ if not is_full_dispense and numpy.isclose(
653
+ instrument.current_volume - disp_vol, 0
654
+ ):
655
+ raise CommandPreconditionViolated(
656
+ message="Command created a full-dispense without the full dispense argument",
657
+ detail={
658
+ "command": "dispense",
659
+ "current-volume": str(instrument.current_volume),
660
+ "dispense-volume": str(disp_vol),
661
+ },
662
+ )
663
+
664
+ if disp_vol == 0:
665
+ return None
666
+
667
+ if is_full_dispense:
668
+ disp_vol = instrument.current_volume
669
+ if push_out is None:
670
+ push_out_ul = instrument.push_out_volume
671
+ else:
672
+ push_out_ul = push_out
673
+ else:
674
+ if push_out is not None and push_out != 0:
675
+ raise CommandPreconditionViolated(
676
+ message="Cannot push_out on a dispense that does not leave the pipette empty",
677
+ detail={
678
+ "command": "dispense",
679
+ "remaining-volume": str(instrument.current_volume - disp_vol),
680
+ },
681
+ )
682
+ push_out_ul = 0
683
+
684
+ push_out_dist_mm = push_out_ul / instrument.ul_per_mm(push_out_ul, "blowout")
685
+
686
+ if not instrument.ok_to_push_out(push_out_dist_mm):
687
+ raise CommandParameterLimitViolated(
688
+ command_name="dispense",
689
+ parameter_name="push_out",
690
+ limit_statement="less than pipette max blowout volume",
691
+ actual_value=str(push_out_ul),
692
+ )
693
+
694
+ dist = self.plunger_position(
695
+ instr=instrument,
696
+ ul=instrument.current_volume - disp_vol,
697
+ action="dispense",
698
+ correction_volume=correction_volume,
699
+ )
700
+ speed = self.plunger_speed(
701
+ instrument, instrument.dispense_flow_rate * rate, "dispense"
702
+ )
703
+ acceleration = self.plunger_acceleration(
704
+ instrument, instrument.flow_acceleration
705
+ )
706
+ return LiquidActionSpec(
707
+ axis=Axis.of_main_tool_actuator(mount),
708
+ volume=disp_vol,
709
+ plunger_distance=dist + push_out_dist_mm,
710
+ speed=speed,
711
+ acceleration=acceleration,
712
+ instr=instrument,
713
+ current=instrument.plunger_motor_current.run,
714
+ )
715
+
716
+ def plan_check_blow_out(
717
+ self, mount: OT3Mount, volume: Optional[float] = None
718
+ ) -> LiquidActionSpec:
719
+ """Check preconditions and calculate values for blowout."""
720
+ instrument = self.get_pipette(mount)
721
+ speed = self.plunger_speed(instrument, instrument.blow_out_flow_rate, "blowout")
722
+ acceleration = self.plunger_acceleration(
723
+ instrument, instrument.flow_acceleration
724
+ )
725
+ max_distance = (
726
+ instrument.plunger_positions.blow_out - instrument.plunger_positions.bottom
727
+ )
728
+ if volume is None:
729
+ distance_mm = max_distance
730
+ else:
731
+ ul = volume
732
+ distance_mm = ul / instrument.ul_per_mm(ul, "blowout")
733
+ if distance_mm > max_distance:
734
+ raise CommandParameterLimitViolated(
735
+ command_name="blow_out",
736
+ parameter_name="volume",
737
+ limit_statement="less than the available distance for the plunger to move",
738
+ actual_value=str(volume),
739
+ )
740
+
741
+ return LiquidActionSpec(
742
+ axis=Axis.of_main_tool_actuator(mount),
743
+ volume=0,
744
+ plunger_distance=distance_mm,
745
+ speed=speed,
746
+ acceleration=acceleration,
747
+ instr=instrument,
748
+ current=instrument.plunger_motor_current.run,
749
+ )
750
+
751
+ @staticmethod
752
+ def _build_tip_motor_moves(
753
+ prep_move_dist: float,
754
+ clamp_move_dist: float,
755
+ prep_move_speed: float,
756
+ clamp_move_speed: float,
757
+ tip_motor_current: float,
758
+ plunger_current: float,
759
+ ) -> List[TipActionMoveSpec]:
760
+ return [
761
+ TipActionMoveSpec(
762
+ distance=prep_move_dist,
763
+ speed=prep_move_speed,
764
+ currents={
765
+ Axis.P_L: plunger_current,
766
+ Axis.Q: tip_motor_current,
767
+ },
768
+ ),
769
+ TipActionMoveSpec(
770
+ distance=prep_move_dist + clamp_move_dist,
771
+ speed=clamp_move_speed,
772
+ currents={
773
+ Axis.P_L: plunger_current,
774
+ Axis.Q: tip_motor_current,
775
+ },
776
+ ),
777
+ ]
778
+
779
+ @staticmethod
780
+ def _build_pickup_shakes(
781
+ instrument: Pipette,
782
+ ) -> List[Tuple[top_types.Point, Optional[float]]]:
783
+ def build_one_shake() -> List[Tuple[top_types.Point, Optional[float]]]:
784
+ shake_dist = float(SHAKE_OFF_TIPS_PICKUP_DISTANCE)
785
+ shake_speed = float(SHAKE_OFF_TIPS_SPEED)
786
+ return [
787
+ (top_types.Point(-shake_dist, 0, 0), shake_speed), # left
788
+ (top_types.Point(2 * shake_dist, 0, 0), shake_speed), # right
789
+ (top_types.Point(-shake_dist, 0, 0), shake_speed), # center
790
+ (top_types.Point(0, -shake_dist, 0), shake_speed), # front
791
+ (top_types.Point(0, 2 * shake_dist, 0), shake_speed), # back
792
+ (top_types.Point(0, -shake_dist, 0), shake_speed), # center
793
+ (top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # up
794
+ ]
795
+
796
+ return []
797
+
798
+ def plan_ht_pick_up_tip(self, tip_count: int) -> TipActionSpec:
799
+ # Prechecks: ready for pickup tip and press/increment are valid
800
+ mount = OT3Mount.LEFT
801
+ instrument = self.get_pipette(mount)
802
+ if instrument.has_tip:
803
+ raise UnexpectedTipAttachError("pick_up_tip", instrument.name, mount.name)
804
+ self._ihp_log.debug(f"Picking up tip on {mount.name}")
805
+
806
+ pick_up_config = instrument.get_pick_up_configuration()
807
+ if not isinstance(pick_up_config, CamActionPickUpTipConfiguration):
808
+ raise CommandPreconditionViolated(
809
+ f"Low-throughput pick up tip got wrong config for {instrument.name} on {mount.name}"
810
+ )
811
+
812
+ tip_motor_moves = self._build_tip_motor_moves(
813
+ prep_move_dist=pick_up_config.prep_move_distance,
814
+ clamp_move_dist=instrument.get_pick_up_distance_by_configuration(
815
+ pick_up_config
816
+ ),
817
+ prep_move_speed=pick_up_config.prep_move_speed,
818
+ clamp_move_speed=instrument.get_pick_up_speed_by_configuration(
819
+ pick_up_config
820
+ ),
821
+ plunger_current=instrument.plunger_motor_current.run,
822
+ tip_motor_current=instrument.get_pick_up_current_by_configuration(
823
+ pick_up_config
824
+ ),
825
+ )
826
+
827
+ return TipActionSpec(
828
+ tip_action_moves=tip_motor_moves,
829
+ shake_off_moves=[],
830
+ z_distance_to_tiprack=(-1 * pick_up_config.connect_tiprack_distance_mm),
831
+ ending_z_retract_distance=instrument.config.end_tip_action_retract_distance_mm,
832
+ )
833
+
834
+ def plan_lt_pick_up_tip(
835
+ self,
836
+ mount: OT3Mount,
837
+ tip_count: int,
838
+ presses: Optional[int],
839
+ increment: Optional[float],
840
+ ) -> TipActionSpec:
841
+ # Prechecks: ready for pickup tip and press/increment are valid
842
+ instrument = self.get_pipette(mount)
843
+ if instrument.has_tip:
844
+ raise UnexpectedTipAttachError("pick_up_tip", instrument.name, mount.name)
845
+ self._ihp_log.debug(f"Picking up tip on {mount.name}")
846
+
847
+ pick_up_config = instrument.get_pick_up_configuration()
848
+ if not isinstance(pick_up_config, PressFitPickUpTipConfiguration):
849
+ raise CommandPreconditionViolated(
850
+ f"Low-throughput pick up tip got wrong config for {instrument.name} on {mount.name}"
851
+ )
852
+ if presses is None or presses < 0:
853
+ checked_presses = pick_up_config.presses
854
+ else:
855
+ checked_presses = presses
856
+
857
+ if not increment or increment < 0:
858
+ check_incr = pick_up_config.increment
859
+ else:
860
+ check_incr = increment
861
+
862
+ pick_up_speed = instrument.get_pick_up_speed_by_configuration(pick_up_config)
863
+
864
+ def build_presses() -> List[TipActionMoveSpec]:
865
+ # Press the nozzle into the tip <presses> number of times,
866
+ # moving further by <increment> mm after each press
867
+ press_moves = []
868
+ for i in range(checked_presses):
869
+ # move nozzle down into the tip
870
+ press_dist = (
871
+ -1.0
872
+ * instrument.get_pick_up_distance_by_configuration(pick_up_config)
873
+ + -1.0 * check_incr * i
874
+ )
875
+ press_moves.append(
876
+ TipActionMoveSpec(
877
+ distance=press_dist,
878
+ speed=pick_up_speed,
879
+ currents={
880
+ Axis.by_mount(
881
+ mount
882
+ ): instrument.get_pick_up_current_by_configuration(
883
+ pick_up_config
884
+ )
885
+ },
886
+ )
887
+ )
888
+ # move nozzle back up
889
+ backup_dist = -press_dist
890
+ press_moves.append(
891
+ TipActionMoveSpec(
892
+ distance=backup_dist,
893
+ speed=pick_up_speed,
894
+ currents=None,
895
+ )
896
+ )
897
+ return press_moves
898
+
899
+ return TipActionSpec(
900
+ tip_action_moves=build_presses(),
901
+ shake_off_moves=self._build_pickup_shakes(instrument),
902
+ ending_z_retract_distance=instrument.config.end_tip_action_retract_distance_mm,
903
+ )
904
+
905
+ @staticmethod
906
+ def _shake_off_tips_drop(
907
+ tiprack_diameter: float,
908
+ ) -> List[Tuple[top_types.Point, Optional[float]]]:
909
+ # tips don't always fall off, especially if resting against
910
+ # tiprack or other tips below it. To ensure the tip has fallen
911
+ # first, shake the pipette to dislodge partially-sealed tips,
912
+ # then second, raise the pipette so loosened tips have room to fall
913
+ shake_off_dist = SHAKE_OFF_TIPS_DROP_DISTANCE
914
+ if tiprack_diameter > 0.0:
915
+ shake_off_dist = min(shake_off_dist, tiprack_diameter / 4)
916
+ shake_off_dist = max(shake_off_dist, 1.0)
917
+ speed = SHAKE_OFF_TIPS_SPEED
918
+ return [
919
+ (top_types.Point(-shake_off_dist, 0, 0), speed), # left
920
+ (top_types.Point(2 * shake_off_dist, 0, 0), speed), # right
921
+ (top_types.Point(-shake_off_dist, 0, 0), speed), # center
922
+ (top_types.Point(0, 0, DROP_TIP_RELEASE_DISTANCE), None), # top
923
+ ]
924
+
925
+ def plan_lt_drop_tip(
926
+ self,
927
+ mount: OT3Mount,
928
+ scrape_tips: TipScrapeType = TipScrapeType.NONE,
929
+ ) -> TipActionSpec:
930
+ instrument = self.get_pipette(mount)
931
+ config = instrument.drop_configurations.plunger_eject
932
+ if not config:
933
+ raise CommandPreconditionViolated(
934
+ f"No plunger-eject drop tip configurations for {instrument.name} on {mount.name}"
935
+ )
936
+ scrape_move: Optional[TipActionMoveSpec] = None
937
+ match scrape_tips:
938
+ case TipScrapeType.LEFT_ONE_COL:
939
+ scrape_move = TipActionMoveSpec(
940
+ distance=-11, currents=None, speed=None, scrape_axis=Axis.X
941
+ )
942
+ case TipScrapeType.RIGHT_ONE_COL:
943
+ scrape_move = TipActionMoveSpec(
944
+ distance=11, currents=None, speed=None, scrape_axis=Axis.X
945
+ )
946
+ case TipScrapeType.NONE:
947
+ scrape_move = None
948
+ case _:
949
+ scrape_move = None
950
+ drop_seq = [
951
+ TipActionMoveSpec(
952
+ distance=instrument.plunger_positions.drop_tip,
953
+ speed=config.speed,
954
+ currents={
955
+ Axis.of_main_tool_actuator(mount): config.current,
956
+ },
957
+ ),
958
+ TipActionMoveSpec(
959
+ distance=instrument.plunger_positions.bottom,
960
+ speed=None,
961
+ currents={
962
+ Axis.of_main_tool_actuator(
963
+ mount
964
+ ): instrument.plunger_motor_current.run,
965
+ },
966
+ ),
967
+ ]
968
+ if scrape_move:
969
+ # Add the scrape move before the plunger moves back up
970
+ drop_seq.insert(1, scrape_move)
971
+
972
+ return TipActionSpec(
973
+ tip_action_moves=drop_seq,
974
+ shake_off_moves=[],
975
+ )
976
+
977
+ def plan_ht_drop_tip(self) -> TipActionSpec:
978
+ mount = OT3Mount.LEFT
979
+ instrument = self.get_pipette(mount)
980
+ config = instrument.drop_configurations.cam_action
981
+ if not config:
982
+ raise CommandPreconditionViolated(
983
+ f"No cam-action drop tip configurations for {instrument.name} on {mount.name}"
984
+ )
985
+
986
+ drop_seq = self._build_tip_motor_moves(
987
+ prep_move_dist=config.prep_move_distance,
988
+ clamp_move_dist=config.distance,
989
+ prep_move_speed=config.prep_move_speed,
990
+ clamp_move_speed=config.speed,
991
+ plunger_current=instrument.plunger_motor_current.run,
992
+ tip_motor_current=config.current,
993
+ )
994
+
995
+ return TipActionSpec(
996
+ tip_action_moves=drop_seq,
997
+ shake_off_moves=[],
998
+ )
999
+
1000
+ def has_pipette(self, mount: OT3Mount) -> bool:
1001
+ return bool(self._attached_instruments[mount])
1002
+
1003
+ def get_pipette(self, mount: OT3Mount) -> Pipette:
1004
+ pip = self._attached_instruments[mount]
1005
+ if not pip:
1006
+ raise top_types.PipetteNotAttachedError(
1007
+ f"No pipette attached to {mount.name} mount"
1008
+ )
1009
+ return pip
1010
+
1011
+ async def set_liquid_class(self, mount: OT3Mount, liquid_class: str) -> None:
1012
+ pip = self.get_pipette(mount)
1013
+ pip.set_liquid_class_by_name(liquid_class)
1014
+
1015
+ async def configure_for_volume(self, mount: OT3Mount, volume: float) -> None:
1016
+ pip = self.get_pipette(mount)
1017
+ if pip.current_volume > 0:
1018
+ # Switching liquid classes can't happen when there's already liquid
1019
+ return
1020
+ new_class_name = liquid_class_for_volume_between_default_and_defaultlowvolume(
1021
+ volume,
1022
+ pip.liquid_class_name,
1023
+ pip.config.liquid_properties,
1024
+ )
1025
+ pip.set_liquid_class_by_name(new_class_name.name)
1026
+
1027
+ def get_tip_sensor_count(self, mount: OT3Mount) -> int:
1028
+ if not self.has_pipette(mount):
1029
+ return 0
1030
+ return self.get_pipette(mount).tip_presence_responses