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,260 @@
1
+ import asyncio
2
+ from typing import Dict, Optional, Any, List, Union
3
+ from typing_extensions import Literal
4
+ from dataclasses import dataclass, asdict, field, replace
5
+ import json
6
+ from pathlib import Path
7
+ from warnings import warn
8
+
9
+ from opentrons.config import robot_configs
10
+ from opentrons.config.types import RobotConfig, OT3Config
11
+ from opentrons.types import Mount
12
+ from opentrons.hardware_control import API, HardwareControlAPI, ThreadManager
13
+ from opentrons.hardware_control.types import OT3Mount, HardwareFeatureFlags
14
+ from opentrons.hardware_control.modules import SimulatingModule
15
+
16
+
17
+ # Name and kwargs for a module function
18
+ @dataclass(frozen=True)
19
+ class ModuleCall:
20
+ function_name: str
21
+ args: List[Any] = field(default_factory=list)
22
+ kwargs: Dict[str, Any] = field(default_factory=dict)
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class ModuleItem:
27
+ serial_number: str
28
+ model: str
29
+ calls: List[ModuleCall] = field(default_factory=list)
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class OT2SimulatorSetup:
34
+ machine: Literal["OT-2 Standard"] = "OT-2 Standard"
35
+ attached_instruments: Dict[Mount, Dict[str, Optional[str]]] = field(
36
+ default_factory=dict
37
+ )
38
+ attached_modules: Dict[str, List[ModuleItem]] = field(default_factory=dict)
39
+ config: Optional[RobotConfig] = None
40
+ strict_attached_instruments: bool = True
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class OT3SimulatorSetup:
45
+ machine: Literal["OT-3 Standard"] = "OT-3 Standard"
46
+ attached_instruments: Dict[OT3Mount, Dict[str, Optional[str]]] = field(
47
+ default_factory=dict
48
+ )
49
+ attached_modules: Dict[str, List[ModuleItem]] = field(default_factory=dict)
50
+ config: Optional[OT3Config] = None
51
+ strict_attached_instruments: bool = True
52
+
53
+
54
+ SimulatorSetup = Union[OT2SimulatorSetup, OT3SimulatorSetup]
55
+
56
+
57
+ async def _simulator_for_setup(
58
+ setup: SimulatorSetup, loop: Optional[asyncio.AbstractEventLoop]
59
+ ) -> HardwareControlAPI:
60
+ if setup.machine == "OT-2 Standard":
61
+ return await API.build_hardware_simulator(
62
+ attached_instruments=setup.attached_instruments,
63
+ attached_modules={
64
+ k: [
65
+ SimulatingModule(serial_number=m.serial_number, model=m.model)
66
+ for m in v
67
+ ]
68
+ for k, v in setup.attached_modules.items()
69
+ },
70
+ config=setup.config,
71
+ strict_attached_instruments=setup.strict_attached_instruments,
72
+ loop=loop,
73
+ feature_flags=HardwareFeatureFlags.build_from_ff(),
74
+ )
75
+ else:
76
+ from opentrons.hardware_control.ot3api import OT3API
77
+
78
+ return await OT3API.build_hardware_simulator(
79
+ attached_instruments=setup.attached_instruments,
80
+ attached_modules={
81
+ k: [
82
+ SimulatingModule(serial_number=m.serial_number, model=m.model)
83
+ for m in v
84
+ ]
85
+ for k, v in setup.attached_modules.items()
86
+ },
87
+ config=setup.config,
88
+ strict_attached_instruments=setup.strict_attached_instruments,
89
+ loop=loop,
90
+ feature_flags=HardwareFeatureFlags.build_from_ff(),
91
+ )
92
+
93
+
94
+ async def create_simulator(
95
+ setup: SimulatorSetup, loop: Optional[asyncio.AbstractEventLoop] = None
96
+ ) -> HardwareControlAPI:
97
+ """Create a simulator"""
98
+ simulator = await _simulator_for_setup(setup, loop)
99
+ for attached_module in simulator.attached_modules:
100
+ modules = setup.attached_modules[attached_module.name()]
101
+ for module in modules:
102
+ if module.serial_number == attached_module.device_info.get("serial"):
103
+ for call in module.calls:
104
+ f = getattr(attached_module, call.function_name)
105
+ await f(*call.args, **call.kwargs)
106
+
107
+ return simulator
108
+
109
+
110
+ async def load_simulator(
111
+ path: Path, loop: Optional[asyncio.AbstractEventLoop] = None
112
+ ) -> HardwareControlAPI:
113
+ """Create a simulator from a JSON file."""
114
+ return await create_simulator(setup=load_simulator_setup(path), loop=loop)
115
+
116
+
117
+ def _thread_manager_for_setup(
118
+ setup: SimulatorSetup,
119
+ ) -> ThreadManager[HardwareControlAPI]:
120
+ if setup.machine == "OT-2 Standard":
121
+ return ThreadManager(
122
+ API.build_hardware_simulator,
123
+ attached_instruments=setup.attached_instruments,
124
+ attached_modules={
125
+ k: [
126
+ SimulatingModule(serial_number=m.serial_number, model=m.model)
127
+ for m in v
128
+ ]
129
+ for k, v in setup.attached_modules.items()
130
+ },
131
+ config=setup.config,
132
+ strict_attached_instruments=setup.strict_attached_instruments,
133
+ feature_flags=HardwareFeatureFlags.build_from_ff(),
134
+ )
135
+ else:
136
+ from opentrons.hardware_control.ot3api import OT3API
137
+
138
+ return ThreadManager(
139
+ OT3API.build_hardware_simulator,
140
+ attached_instruments=setup.attached_instruments,
141
+ attached_modules={
142
+ k: [
143
+ SimulatingModule(serial_number=m.serial_number, model=m.model)
144
+ for m in v
145
+ ]
146
+ for k, v in setup.attached_modules.items()
147
+ },
148
+ config=setup.config,
149
+ strict_attached_instruments=setup.strict_attached_instruments,
150
+ feature_flags=HardwareFeatureFlags.build_from_ff(),
151
+ )
152
+
153
+
154
+ async def create_simulator_thread_manager(
155
+ setup: SimulatorSetup,
156
+ ) -> ThreadManager[HardwareControlAPI]:
157
+ """Create a simulator thread manager from a loaded config."""
158
+ thread_manager = _thread_manager_for_setup(setup)
159
+ await thread_manager.managed_thread_ready_async()
160
+
161
+ for attached_module in thread_manager.wrapped().attached_modules:
162
+ modules = setup.attached_modules[attached_module.name()]
163
+ for module in modules:
164
+ for call in module.calls:
165
+ f = getattr(attached_module, call.function_name)
166
+ await f(*call.args, **call.kwargs)
167
+
168
+ return thread_manager
169
+
170
+
171
+ async def load_simulator_thread_manager(
172
+ path: Path,
173
+ ) -> ThreadManager[HardwareControlAPI]:
174
+ """Create a simulator wrapped in a ThreadManager from a JSON file."""
175
+ return await create_simulator_thread_manager(load_simulator_setup(path))
176
+
177
+
178
+ def save_simulator_setup(simulator_setup: SimulatorSetup, path: Path) -> None:
179
+ """Write a simulator setup to a file."""
180
+ no_config = replace(simulator_setup, config=None)
181
+ as_dict = asdict(no_config)
182
+
183
+ as_dict["config"] = (
184
+ robot_configs.config_to_save(simulator_setup.config)
185
+ if simulator_setup.config
186
+ else None
187
+ )
188
+
189
+ if as_dict.get("attached_instruments", None):
190
+ as_dict["attached_instruments"] = {
191
+ mount.name.lower(): data
192
+ for mount, data in as_dict["attached_instruments"].items()
193
+ }
194
+
195
+ with path.open("w") as f:
196
+ json.dump(as_dict, f)
197
+
198
+
199
+ def load_simulator_setup(path: Path) -> SimulatorSetup:
200
+ """Load a simulator setup from a file."""
201
+ with path.open() as f:
202
+ obj = json.load(f)
203
+
204
+ if "machine" not in obj:
205
+ warn(
206
+ "Simulator configuration does not name a machine, defaulting to OT-2 Standard"
207
+ )
208
+ machine_type = obj.get("machine", "OT-2 Standard")
209
+ if machine_type == "OT-2 Standard":
210
+ return OT2SimulatorSetup(
211
+ **{k: _prepare_for_simulator_setup(k, v) for (k, v) in obj.items()}
212
+ )
213
+ else:
214
+ return OT3SimulatorSetup(
215
+ **{k: _prepare_for_ot3_simulator_setup(k, v) for (k, v) in obj.items()}
216
+ )
217
+
218
+
219
+ def _prepare_for_simulator_setup(key: str, value: Dict[str, Any]) -> Any:
220
+ """Convert value to a SimulatorSetup"""
221
+ if key == "attached_instruments" and value:
222
+ return {Mount[mount.upper()]: data for (mount, data) in value.items()}
223
+ if key == "config" and value:
224
+ return robot_configs.build_config_ot2(value)
225
+ if key == "attached_modules" and value:
226
+ attached_modules: Dict[str, List[ModuleItem]] = {}
227
+ for key, item in value.items():
228
+ for obj in item:
229
+ attached_modules.setdefault(key, []).append(
230
+ ModuleItem(
231
+ serial_number=obj["serial_number"],
232
+ model=obj["model"],
233
+ calls=[ModuleCall(**data) for data in obj["calls"]],
234
+ )
235
+ )
236
+
237
+ return attached_modules
238
+
239
+ return value
240
+
241
+
242
+ def _prepare_for_ot3_simulator_setup(key: str, value: Dict[str, Any]) -> Any:
243
+ if key == "attached_instruments" and value:
244
+ return {OT3Mount[mount.upper()]: data for (mount, data) in value.items()}
245
+ if key == "config" and value:
246
+ return robot_configs.build_config_ot3(value)
247
+ if key == "attached_modules" and value:
248
+ attached_modules: Dict[str, List[ModuleItem]] = {}
249
+ for key, item in value.items():
250
+ for obj in item:
251
+ attached_modules.setdefault(key, []).append(
252
+ ModuleItem(
253
+ serial_number=obj["serial_number"],
254
+ model=obj["model"],
255
+ calls=[ModuleCall(**data) for data in obj["calls"]],
256
+ )
257
+ )
258
+
259
+ return attached_modules
260
+ return value
@@ -0,0 +1,431 @@
1
+ """Manager for the :py:class:`.hardware_control.API` thread."""
2
+ import functools
3
+ import threading
4
+ import logging
5
+ import asyncio
6
+ import inspect
7
+ import weakref
8
+ from typing import (
9
+ Any,
10
+ Awaitable,
11
+ Callable,
12
+ Generic,
13
+ Optional,
14
+ TypeVar,
15
+ cast,
16
+ Sequence,
17
+ Mapping,
18
+ AsyncGenerator,
19
+ Union,
20
+ Type,
21
+ ParamSpec,
22
+ )
23
+ from .adapters import SynchronousAdapter
24
+ from .modules.mod_abc import AbstractModule
25
+ from .protocols import (
26
+ AsyncioConfigurable,
27
+ )
28
+
29
+ MODULE_LOG = logging.getLogger(__name__)
30
+
31
+
32
+ class ThreadManagerException(Exception):
33
+ pass
34
+
35
+
36
+ WrappedReturn = TypeVar("WrappedReturn", contravariant=True)
37
+ WrappedYield = TypeVar("WrappedYield", contravariant=True)
38
+ P = ParamSpec("P")
39
+
40
+
41
+ async def call_coroutine_threadsafe(
42
+ loop: asyncio.AbstractEventLoop,
43
+ coro: Callable[P, Awaitable[WrappedReturn]],
44
+ *args: P.args,
45
+ **kwargs: P.kwargs,
46
+ ) -> WrappedReturn:
47
+ fut = cast(
48
+ "asyncio.Future[WrappedReturn]",
49
+ asyncio.run_coroutine_threadsafe(coro(*args, **kwargs), loop),
50
+ )
51
+ wrapped = asyncio.wrap_future(fut)
52
+ return await wrapped
53
+
54
+
55
+ async def execute_asyncgen_threadsafe(
56
+ loop: asyncio.AbstractEventLoop,
57
+ agenfunc: Callable[P, AsyncGenerator[WrappedYield, None]],
58
+ *args: P.args,
59
+ **kwargs: P.kwargs,
60
+ ) -> AsyncGenerator[WrappedYield, None]:
61
+
62
+ # This function should bridge an async generator function between two asyncio
63
+ # loops running in different threads. There are several stages to this because
64
+ # there are several stages to generator execution.
65
+
66
+ # These clues will help us later
67
+ class _DoneSingleton:
68
+ pass
69
+
70
+ async def _build_queue() -> "asyncio.Queue[Union[WrappedYield, _DoneSingleton]]":
71
+ return asyncio.Queue(maxsize=1)
72
+
73
+ yield_queue = await asyncio.wrap_future(
74
+ cast(
75
+ "asyncio.Future[asyncio.Queue[Union[WrappedYield, _DoneSingleton]]]",
76
+ asyncio.run_coroutine_threadsafe(_build_queue(), loop),
77
+ )
78
+ )
79
+
80
+ # the async generator function needs to run on the target loop. it is not a coroutine
81
+ # and cannot be run with run_coroutine_threadsafe in the same way. however, we can
82
+ # make a coroutine that _can_, and that exhausts the async generator and puts what
83
+ # the generator yields into a queue. since something later will have to combine
84
+ # awaiting yield results and the function finishing, we can now use the _DoneSingleton
85
+ # to short circuit the caller waiting for a yield result when the function is done.
86
+ # in addition, the queue being 1 element max should give us similar "backpressure"
87
+ # behavior as an async for.
88
+ async def _inner_agen_wrap() -> None:
89
+ item: WrappedYield
90
+ try:
91
+ async for item in agenfunc(*args, **kwargs):
92
+ await yield_queue.put(item)
93
+ finally:
94
+ await yield_queue.put(_DoneSingleton())
95
+
96
+ # as promised, we can run our coroutine pretty easily now. note that this coroutine
97
+ # returns None, because it is handling the generator yields internally.
98
+ fut = cast(
99
+ "asyncio.Future[None]",
100
+ asyncio.run_coroutine_threadsafe(_inner_agen_wrap(), loop),
101
+ )
102
+
103
+ # now we have to bridge the output of the generator to the calling loop. this is
104
+ # fun because this uses an asyncio queue which isn't thread safe. so we also need
105
+ # to pull stuff out of the asyncio queue via the other loop
106
+ while not fut.done():
107
+ getter = cast(
108
+ "asyncio.Future[Union[WrappedYield, _DoneSingleton]]",
109
+ asyncio.run_coroutine_threadsafe(yield_queue.get(), loop),
110
+ )
111
+ asyncio_getter = asyncio.wrap_future(getter)
112
+ item = await asyncio_getter
113
+ if isinstance(item, _DoneSingleton):
114
+ break
115
+ yield item
116
+ # if there was an exception then this should re-raise it in the calling loop
117
+ _ = fut.result()
118
+
119
+
120
+ WrappedObj = TypeVar("WrappedObj", bound=AsyncioConfigurable, covariant=True)
121
+
122
+
123
+ class CallBridger(Generic[WrappedObj]):
124
+ def __init__(
125
+ self, wrapped_obj: WrappedObj, loop: asyncio.AbstractEventLoop
126
+ ) -> None:
127
+ self.wrapped_obj = wrapped_obj
128
+ self._loop = loop
129
+
130
+ def __getattribute__(self, attr_name: str) -> Any:
131
+ # Almost every attribute retrieved from us will be for people actually
132
+ # looking for an attribute of the managed object, so check there first.
133
+ managed_obj = object.__getattribute__(self, "wrapped_obj")
134
+ loop = object.__getattribute__(self, "_loop")
135
+ try:
136
+ attr = getattr(managed_obj, attr_name)
137
+ except AttributeError:
138
+ # Maybe this actually was for us? Let’s find it
139
+ return object.__getattribute__(self, attr_name)
140
+
141
+ if asyncio.iscoroutinefunction(attr):
142
+ # Return coroutine result of async function
143
+ # executed in managed thread to calling thread
144
+
145
+ @functools.wraps(attr)
146
+ async def wrapper(
147
+ *args: Sequence[Any], **kwargs: Mapping[str, Any]
148
+ ) -> WrappedReturn:
149
+ return await call_coroutine_threadsafe(loop, attr, *args, **kwargs)
150
+
151
+ return wrapper
152
+
153
+ elif asyncio.iscoroutine(attr):
154
+ # Return awaitable coroutine properties run in managed thread/loop
155
+ fut = asyncio.run_coroutine_threadsafe(attr, loop)
156
+ wrapped = asyncio.wrap_future(fut)
157
+ return wrapped
158
+
159
+ elif inspect.isasyncgenfunction(attr):
160
+ # Return a wrapper that will exectue the resulting async generator
161
+ # in managed thread loop
162
+
163
+ @functools.wraps(attr)
164
+ async def wrapper(
165
+ *args: Sequence[Any], **kwargs: Mapping[str, Any]
166
+ ) -> AsyncGenerator[WrappedYield, None]:
167
+ item: WrappedYield
168
+ async for item in execute_asyncgen_threadsafe(
169
+ loop, attr, *args, **kwargs
170
+ ):
171
+ yield item
172
+
173
+ return wrapper
174
+
175
+ return attr
176
+
177
+
178
+ # TODO: BC 2020-02-25 instead of overwriting __get_attribute__ in this class
179
+ # use inspect.getmembers to iterate over appropriate members of adapted
180
+ # instance and setattr on the outer instance with the proper threadsafe
181
+ # resolution logic injected. This approach avoids requiring calls to
182
+ # object.__get_attribute__(self,...) to opt out of the overwritten
183
+ # functionality. It is more readable and protected from
184
+ # unintentional recursion.
185
+ class ThreadManager(Generic[WrappedObj]):
186
+ """A wrapper to make every call into :py:class:`.hardware_control.API`
187
+ execute within the same thread.
188
+
189
+ This class spawns a worker thread and starts an event loop within.
190
+ It then calls the async builder parameter within that worker thread's
191
+ event loop passing thru all args and kwargs and injecting the worker
192
+ thread's loop as a kwarg to the builder. The resulting built object
193
+ is stored as a member of the class, and a synchronous interface to
194
+ the managed object's members is also exposed for convenience.
195
+
196
+ If you want to wait for the managed object's creation separately
197
+ (with managed_thread_ready_blocking or managed_thread_ready_async)
198
+ use the nonblocking_builder static method to add an attribute to the builder
199
+ function, i.e.
200
+
201
+ thread_manager = ThreadManager(ThreadManager.nonblocking_builder(builder), ...)
202
+
203
+ Example
204
+ -------
205
+ .. code-block::
206
+ >>> from opentrons.hardware_control import API, ThreadManager
207
+ >>> api_single_thread = ThreadManager(API.build_hardware_simulator)
208
+ >>> await api_single_thread.home() # call as awaitable async
209
+ >>> api_single_thread.sync.home() # call as blocking sync
210
+ """
211
+
212
+ Builder = ParamSpec("Builder")
213
+ Built = TypeVar("Built")
214
+
215
+ @staticmethod
216
+ def nonblocking_builder(
217
+ builder: Callable[Builder, Awaitable[Built]]
218
+ ) -> Callable[Builder, Awaitable[Built]]:
219
+ """Wrap an instance of a builder function to make initializes that use it nonblocking.
220
+
221
+ For instance, you can build a ThreadManager like this:
222
+
223
+ thread_manager = ThreadManager(ThreadManager.nonblocking_builder(API.build_hardware_controller), ...)
224
+
225
+ to make the initialize call return immediately so you can later wait on it via
226
+ managed_thread_ready_blocking or managed_thread_ready_async
227
+ """
228
+
229
+ @functools.wraps(builder)
230
+ async def wrapper(
231
+ *args: ThreadManager.Builder.args, **kwargs: ThreadManager.Builder.kwargs
232
+ ) -> ThreadManager.Built:
233
+ return await builder(*args, **kwargs)
234
+
235
+ setattr(wrapper, "nonblocking", True)
236
+ return wrapper
237
+
238
+ def __init__(
239
+ self,
240
+ builder: Callable[Builder, Awaitable[WrappedObj]],
241
+ *args: Builder.args,
242
+ **kwargs: Builder.kwargs,
243
+ ) -> None:
244
+ """Build the ThreadManager.
245
+
246
+ builder: The api function to use to build the instance.
247
+
248
+ The args and kwargs will be forwarded to the builder function.
249
+
250
+ Note: by default, this function will block until the managed thread is ready and the hardware controller
251
+ has been built. To make this function return immediately you can wrap its builder argument in
252
+ ThreadManager.nonblocking_builder(), like this:
253
+
254
+ thread_manager = ThreadManager(ThreadManager.nonblocking_builder(API.build_hardware_controller), ...)
255
+
256
+ Afterwards, you'll need to call ThreadManager.managed_thread_ready_blocking or its async variant before
257
+ you can actually use thei nstance.
258
+ """
259
+
260
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
261
+ self.managed_obj: Optional[WrappedObj] = None
262
+ self.bridged_obj: Optional[CallBridger[WrappedObj]] = None
263
+ self._sync_managed_obj: Optional[SynchronousAdapter[WrappedObj]] = None
264
+ is_running = threading.Event()
265
+ self._is_running = is_running
266
+ self._cached_modules: weakref.WeakKeyDictionary[
267
+ AbstractModule, CallBridger[AbstractModule]
268
+ ] = weakref.WeakKeyDictionary()
269
+ # TODO: remove this if we switch to python 3.8
270
+ # https://docs.python.org/3/library/asyncio-subprocess.html#subprocess-and-threads
271
+ # On windows, the event loop and system interface is different and
272
+ # this won't work.
273
+ try:
274
+ asyncio.get_child_watcher()
275
+ except NotImplementedError:
276
+ pass
277
+ blocking = not getattr(builder, "nonblocking", False)
278
+ target = object.__getattribute__(self, "_build_and_start_loop")
279
+ thread = threading.Thread(
280
+ target=target,
281
+ name="ManagedThread",
282
+ args=(builder, *args),
283
+ kwargs=kwargs,
284
+ daemon=True,
285
+ )
286
+ self._thread = thread
287
+ thread.start()
288
+ if blocking:
289
+ object.__getattribute__(self, "managed_thread_ready_blocking")()
290
+
291
+ def managed_thread_ready_blocking(self) -> None:
292
+ object.__getattribute__(self, "_is_running").wait()
293
+ if not object.__getattribute__(self, "managed_obj"):
294
+ raise ThreadManagerException("Failed to create Managed Object")
295
+
296
+ async def managed_thread_ready_async(self) -> None:
297
+ is_running = object.__getattribute__(self, "_is_running")
298
+ while not is_running.is_set():
299
+ await asyncio.sleep(0.1)
300
+ # Thread initialization is done.
301
+ if not object.__getattribute__(self, "managed_obj"):
302
+ raise ThreadManagerException("Failed to create Managed Object")
303
+
304
+ def _build_and_start_loop(
305
+ self,
306
+ builder: Callable[..., Awaitable[WrappedObj]],
307
+ *args: Sequence[Any],
308
+ **kwargs: Mapping[str, Any],
309
+ ) -> None:
310
+ loop = asyncio.new_event_loop()
311
+ asyncio.set_event_loop(loop)
312
+ self._loop = loop
313
+ try:
314
+ managed_obj = loop.run_until_complete(builder(*args, loop=loop, **kwargs))
315
+ self.managed_obj = managed_obj
316
+ self.bridged_obj = CallBridger(managed_obj, loop)
317
+ self._sync_managed_obj = SynchronousAdapter(managed_obj)
318
+ except Exception:
319
+ MODULE_LOG.exception("Exception in Thread Manager build")
320
+ finally:
321
+ object.__getattribute__(self, "_is_running").set()
322
+ loop.run_forever()
323
+ loop.close()
324
+
325
+ @property
326
+ def sync(self) -> SynchronousAdapter[WrappedObj]:
327
+ # Why the ignore?
328
+ # While self._sync_managed_obj is initialized None, a failure to build
329
+ # the managed_obj and _sync_managed_obj is a catastrophic failure.
330
+ # All callers of this property assume it to be valid.
331
+ return self._sync_managed_obj # type: ignore
332
+
333
+ def __repr__(self) -> str:
334
+ return "<ThreadManager>"
335
+
336
+ def clean_up_tm(self) -> None:
337
+ try:
338
+ loop = object.__getattribute__(self, "_loop")
339
+ loop.call_soon_threadsafe(loop.stop)
340
+ except Exception:
341
+ pass
342
+ object.__setattr__(self, "_cached_modules", weakref.WeakKeyDictionary({}))
343
+ object.__getattribute__(self, "_thread").join()
344
+
345
+ def wrap_module(self, module: AbstractModule) -> CallBridger[AbstractModule]:
346
+ """Return the module object wrapped in a CallBridger and cache it.
347
+
348
+ The wrapped module objects are cached in `self._cached_modules` so they can be
349
+ re-used throughout the module object's life, as creating a wrapper is expensive.
350
+ We use a WeakKeyDictionary for caching so that module objects can be
351
+ garbage collected when modules are detached (since entries in WeakKeyDictionary
352
+ get discarded when there is no longer a strong reference to the key).
353
+ """
354
+ wrapper_cache = object.__getattribute__(self, "_cached_modules")
355
+ this_module_wrapper = wrapper_cache.get(module)
356
+
357
+ if this_module_wrapper is None:
358
+ this_module_wrapper = CallBridger(
359
+ module, object.__getattribute__(self, "_loop")
360
+ )
361
+ wrapper_cache.update({module: this_module_wrapper})
362
+
363
+ return this_module_wrapper # type: ignore
364
+
365
+ def __getattribute__(self, attr_name: str) -> Any:
366
+ # hardware_control.api.API.attached_modules is the only hardware
367
+ # API method that returns something other than data. The module
368
+ # objects it returns have associated methods that can be called.
369
+ # That means they need the same wrapping treatment as the API
370
+ # itself.
371
+ if attr_name == "attached_modules":
372
+ wrap = object.__getattribute__(self, "wrap_module")
373
+ managed = object.__getattribute__(self, "managed_obj")
374
+ attr = getattr(managed, attr_name)
375
+ return [wrap(mod) for mod in attr]
376
+ elif attr_name == "clean_up":
377
+ # the wrapped object probably has this attr as well as us, and we
378
+ # want to call both, with the wrapped one first
379
+
380
+ # we only want to call cleanup once, and then only if the loop
381
+ # is running
382
+ wrapped_loop = object.__getattribute__(self, "_loop")
383
+ if not wrapped_loop.is_running():
384
+ return lambda: None
385
+
386
+ wrapped_cleanup = getattr(
387
+ object.__getattribute__(self, "bridged_obj"), "clean_up"
388
+ )
389
+ our_cleanup = object.__getattribute__(self, "clean_up_tm")
390
+
391
+ def call_both() -> None:
392
+ # the wrapped cleanup wants to happen in the managed thread,
393
+ # started from the managed loop. our cleanup wants to happen
394
+ # in the current thread, _after_ the wrapped cleanup is done
395
+ # so cancelled tasks can have a chance to complete.
396
+ async def clean_and_notify() -> None:
397
+ await wrapped_cleanup()
398
+ # this sleep allows the wrapped loop to spin to clean up the
399
+ # tasks we just cancelled.
400
+ await asyncio.sleep(0)
401
+
402
+ fut = asyncio.run_coroutine_threadsafe(clean_and_notify(), wrapped_loop)
403
+ fut.result()
404
+ our_cleanup()
405
+
406
+ return call_both
407
+
408
+ else:
409
+ try:
410
+ return getattr(object.__getattribute__(self, "bridged_obj"), attr_name)
411
+ except AttributeError:
412
+ return object.__getattribute__(self, attr_name)
413
+
414
+ def wrapped(self) -> WrappedObj:
415
+ """Expose the type of the underlying wrapped object.
416
+
417
+ This isn't a method that does anything (it just returns self again) but the cast
418
+ means that the type of self will be what the threadmanager's generic wrapped
419
+ object is. You can therefore use this to get typechecking when using
420
+ ThreadManagers.
421
+
422
+ While the generic type is what you say it is when you annotate the instance
423
+ variable containing a ThreadManager, if you restrict yourself to annotating
424
+ those instances using a protocol from hardware_api.protocols, things will more
425
+ or less work out through the rest of the system. Not perfect, but ok.
426
+ """
427
+ return cast(WrappedObj, self)
428
+
429
+ def wraps_instance(self, of_type: Type[Any]) -> bool:
430
+ """Do isinstance() on the wrapped object."""
431
+ return isinstance(object.__getattribute__(self, "managed_obj"), of_type)