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,768 @@
1
+ import asyncio
2
+ import re
3
+ import base64
4
+ from typing import List, Optional
5
+
6
+ from opentrons.drivers.asyncio.communication.errors import (
7
+ NoResponse,
8
+ TaskNotReady,
9
+ )
10
+ from opentrons.drivers.command_builder import CommandBuilder
11
+ from opentrons.drivers.asyncio.communication import AsyncResponseSerialConnection
12
+
13
+ from .abstract import AbstractFlexStackerDriver
14
+ from .errors import StackerErrorCodes, MotorStallDetected
15
+ from .types import (
16
+ GCODE,
17
+ ActiveRange,
18
+ LEDPattern,
19
+ MeasurementKind,
20
+ MoveResult,
21
+ SpadMapID,
22
+ StackerAxis,
23
+ PlatformStatus,
24
+ Direction,
25
+ StackerInfo,
26
+ HardwareRevision,
27
+ MoveParams,
28
+ LimitSwitchStatus,
29
+ LEDColor,
30
+ StallGuardParams,
31
+ TOFConfiguration,
32
+ TOFMeasurement,
33
+ TOFMeasurementFrame,
34
+ TOFMeasurementResult,
35
+ TOFSensor,
36
+ TOFSensorMode,
37
+ TOFSensorState,
38
+ TOFSensorStatus,
39
+ )
40
+ from .utils import (
41
+ NUMBER_OF_BINS,
42
+ validate_histogram_frame,
43
+ )
44
+
45
+
46
+ FS_BAUDRATE = 115200
47
+ DEFAULT_FS_TIMEOUT = 5
48
+ FS_MOVE_TIMEOUT = 20
49
+ FS_TOF_TIMEOUT = 20
50
+ FS_TOF_FRAME_RETRIES = 1
51
+ FS_TOF_INIT_TIMEOUT = 5
52
+ FS_ACK = "OK\n"
53
+ FS_ERROR_KEYWORD = "err"
54
+ FS_ASYNC_ERROR_ACK = "async"
55
+ DEFAULT_COMMAND_RETRIES = 2
56
+ GCODE_ROUNDING_PRECISION = 2
57
+
58
+ # LED animation range values
59
+ MIN_DURATION_MS = 25 # 25ms
60
+ MAX_DURATION_MS = 10000 # 10s
61
+ MAX_REPS = 10
62
+
63
+
64
+ class FlexStackerDriver(AbstractFlexStackerDriver):
65
+ """FLEX Stacker driver."""
66
+
67
+ @classmethod
68
+ def parse_device_info(cls, response: str) -> StackerInfo:
69
+ """Parse stacker info."""
70
+ _RE = re.compile(
71
+ f"^{GCODE.DEVICE_INFO} FW:(?P<fw>\\S+) HW:Opentrons-flex-stacker-(?P<hw>\\S+) SerialNo:(?P<sn>\\S+)$"
72
+ )
73
+ m = _RE.match(response)
74
+ if not m:
75
+ raise ValueError(f"Incorrect Response for device info: {response}")
76
+ return StackerInfo(
77
+ m.group("fw"), HardwareRevision(m.group("hw")), m.group("sn")
78
+ )
79
+
80
+ @classmethod
81
+ def parse_reset_reason(cls, response: str) -> int:
82
+ """Parse the reset reason"""
83
+ _RE = re.compile(rf"^{GCODE.GET_RESET_REASON} R:(?P<R>\d)$")
84
+ match = _RE.match(response)
85
+ if not match:
86
+ raise ValueError(f"Incorrect Response for reset reason: {response}")
87
+ return int(match.group("R"))
88
+
89
+ @classmethod
90
+ def parse_limit_switch_status(cls, response: str) -> LimitSwitchStatus:
91
+ """Parse limit switch statuses."""
92
+ field_names = LimitSwitchStatus.get_fields()
93
+ pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
94
+ _RE = re.compile(f"^{GCODE.GET_LIMIT_SWITCH} {pattern}$")
95
+ m = _RE.match(response)
96
+ if not m:
97
+ raise ValueError(f"Incorrect Response for limit switch status: {response}")
98
+ return LimitSwitchStatus(*(bool(int(m.group(name))) for name in field_names))
99
+
100
+ @classmethod
101
+ def parse_platform_sensor_status(cls, response: str) -> PlatformStatus:
102
+ """Parse platform statuses."""
103
+ field_names = PlatformStatus.get_fields()
104
+ pattern = r"\s".join([rf"{name}:(?P<{name}>\d)" for name in field_names])
105
+ _RE = re.compile(f"^{GCODE.GET_PLATFORM_SENSOR} {pattern}$")
106
+ m = _RE.match(response)
107
+ if not m:
108
+ raise ValueError(f"Incorrect Response for platform status: {response}")
109
+ return PlatformStatus(*(bool(int(m.group(name))) for name in field_names))
110
+
111
+ @classmethod
112
+ def parse_door_closed(cls, response: str) -> bool:
113
+ """Parse door closed."""
114
+ _RE = re.compile(rf"^{GCODE.GET_DOOR_SWITCH} D:(\d)$")
115
+ match = _RE.match(response)
116
+ if not match:
117
+ raise ValueError(f"Incorrect Response for door closed: {response}")
118
+ return bool(int(match.group(1)))
119
+
120
+ @classmethod
121
+ def parse_installation_detected(cls, response: str) -> bool:
122
+ """Parse install detection."""
123
+ _RE = re.compile(rf"^{GCODE.GET_INSTALL_DETECTED} I:(\d)$")
124
+ match = _RE.match(response)
125
+ if not match:
126
+ raise ValueError(
127
+ f"Incorrect Response for installation detected: {response}"
128
+ )
129
+ return bool(int(match.group(1)))
130
+
131
+ @classmethod
132
+ def parse_estop_engaged(cls, response: str) -> bool:
133
+ """Parse estop enagaged."""
134
+ _RE = re.compile(rf"^{GCODE.GET_ESTOP_ENGAGED} E:(\d)$")
135
+ match = _RE.match(response)
136
+ if not match:
137
+ raise ValueError(f"Incorrect Response for Estop engaged: {response}")
138
+ return bool(int(match.group(1)))
139
+
140
+ @classmethod
141
+ def parse_move_params(cls, response: str) -> MoveParams:
142
+ """Parse move params."""
143
+ field_names = MoveParams.get_fields()
144
+ pattern = r"\s".join(rf"{f}:(?P<{f}>(\d*\.)?\d+)" for f in field_names)
145
+ _RE = re.compile(rf"^{GCODE.GET_MOVE_PARAMS} M:([XZL]) {pattern}$")
146
+ m = _RE.match(response)
147
+ if not m:
148
+ raise ValueError(f"Incorrect Response for move params: {response}")
149
+ return MoveParams(
150
+ max_speed=float(m.group("V")),
151
+ acceleration=float(m.group("A")),
152
+ max_speed_discont=float(m.group("D")),
153
+ )
154
+
155
+ @classmethod
156
+ def parse_stallguard_params(cls, response: str) -> StallGuardParams:
157
+ """Parse stallguard params."""
158
+ pattern = r"(?P<M>[XZL]):(?P<E>\d) T:(?P<T>\d+)"
159
+ _RE = re.compile(f"^{GCODE.GET_STALLGUARD_THRESHOLD} {pattern}$")
160
+ m = _RE.match(response)
161
+ if not m:
162
+ raise ValueError(f"Incorrect Response for stallfguard params: {response}")
163
+ return StallGuardParams(
164
+ axis=StackerAxis(m.group("M")),
165
+ enabled=bool(int(m.group("E"))),
166
+ threshold=int(m.group("T")),
167
+ )
168
+
169
+ @classmethod
170
+ def parse_get_motor_register(cls, response: str) -> int:
171
+ """Parse get motor register value."""
172
+ pattern = r"(?P<M>[XZL]):(?P<R>\d+) V:(?P<V>\d+)"
173
+ _RE = re.compile(f"^{GCODE.GET_MOTOR_DRIVER_REGISTER} {pattern}$")
174
+ m = _RE.match(response)
175
+ if not m:
176
+ raise ValueError(
177
+ f"Incorrect Response for get motor driver register: {response}"
178
+ )
179
+ return int(m.group("V"))
180
+
181
+ @classmethod
182
+ def parse_get_tof_sensor_register(cls, response: str) -> int:
183
+ """Parse get tof sensor register value."""
184
+ pattern = r"(?P<S>[XZ]):(?P<R>\d+) V:(?P<V>\d+)"
185
+ _RE = re.compile(f"^{GCODE.GET_TOF_DRIVER_REGISTER} {pattern}$")
186
+ m = _RE.match(response)
187
+ if not m:
188
+ raise ValueError(
189
+ f"Incorrect Response for get tof sensor driver register: {response}"
190
+ )
191
+ return int(m.group("V"))
192
+
193
+ @classmethod
194
+ def parse_tof_sensor_status(cls, response: str) -> TOFSensorStatus:
195
+ """Parse get tof sensor status response."""
196
+ pattern = r"(?P<S>[XZ]):(?P<O>\d) T:(?P<T>\d) M:(?P<M>\d)"
197
+ _RE = re.compile(f"^{GCODE.GET_TOF_SENSOR_STATUS} {pattern}$")
198
+ m = _RE.match(response)
199
+ if not m:
200
+ raise ValueError(
201
+ f"Incorrect Response for get tof sensor status: {response}"
202
+ )
203
+ return TOFSensorStatus(
204
+ sensor=TOFSensor(m.group("S")),
205
+ state=TOFSensorState(int(m.group("T"))),
206
+ mode=TOFSensorMode(int(m.group("M"))),
207
+ ok=bool(int(m.group("O"))),
208
+ )
209
+
210
+ @classmethod
211
+ def parse_manage_tof_measurement(cls, response: str) -> TOFMeasurement:
212
+ """Parse manage tof measurement response."""
213
+ pattern = r"(?P<S>[XZ]) K:(?P<K>\d+) C:(?P<C>\d) L:(?P<L>\d+)"
214
+ _RE = re.compile(f"^{GCODE.MANAGE_TOF_MEASUREMENT} {pattern}$")
215
+ m = _RE.match(response)
216
+ if not m:
217
+ raise ValueError(
218
+ f"Incorrect Response for manage tof measurements: {response}"
219
+ )
220
+ return TOFMeasurement(
221
+ sensor=TOFSensor(m.group("S")),
222
+ kind=MeasurementKind(int(m.group("K"))),
223
+ cancelled=bool(int(m.group("C"))),
224
+ total_bytes=int(m.group("L")),
225
+ )
226
+
227
+ @classmethod
228
+ def parse_get_tof_measurement(cls, response: str) -> TOFMeasurementFrame:
229
+ """Parse get tof measurement response frame."""
230
+ pattern = r"(?P<S>[XZ]) I:(?P<I>\d+) D:(?P<D>.+)"
231
+ _RE = re.compile(f"^{GCODE.GET_TOF_MEASUREMENT} {pattern}$")
232
+ m = _RE.match(response)
233
+ if not m:
234
+ raise ValueError(
235
+ f"Incorrect Response for get tof measurement frame: {response}"
236
+ )
237
+ return TOFMeasurementFrame(
238
+ sensor=TOFSensor(m.group("S")),
239
+ frame_id=int(m.group("I")),
240
+ data=base64.b64decode(m.group("D")),
241
+ )
242
+
243
+ @classmethod
244
+ def parse_get_tof_configuration(cls, response: str) -> TOFConfiguration:
245
+ """Parse get tof sensor configuration response."""
246
+ pattern = r"(?P<S>[XZ]) I:(?P<I>\d+) A:(?P<A>\d+) K:(?P<K>\d+) P:(?P<P>\d+) H:(?P<H>\d)"
247
+ _RE = re.compile(f"^{GCODE.GET_TOF_CONFIGURATION} {pattern}$")
248
+ m = _RE.match(response)
249
+ if not m:
250
+ raise ValueError(
251
+ f"Incorrect Response for get tof sensor configuration: {response}"
252
+ )
253
+ return TOFConfiguration(
254
+ sensor=TOFSensor(m.group("S")),
255
+ spad_map_id=SpadMapID(int(m.group("I"))),
256
+ active_range=ActiveRange(int(m.group("A"))),
257
+ kilo_iterations=int(m.group("K")),
258
+ report_period_ms=int(m.group("P")),
259
+ histogram_dump=bool(m.group("H")),
260
+ )
261
+
262
+ @classmethod
263
+ def append_move_params(
264
+ cls, command: CommandBuilder, params: MoveParams | None
265
+ ) -> CommandBuilder:
266
+ """Append move params."""
267
+ if params is not None:
268
+ command.add_float("V", params.max_speed, GCODE_ROUNDING_PRECISION)
269
+ command.add_float("A", params.acceleration, GCODE_ROUNDING_PRECISION)
270
+ command.add_float("D", params.max_speed_discont, GCODE_ROUNDING_PRECISION)
271
+ return command
272
+
273
+ @classmethod
274
+ async def create(
275
+ cls, port: str, loop: Optional[asyncio.AbstractEventLoop]
276
+ ) -> "FlexStackerDriver":
277
+ """Create a FLEX Stacker driver."""
278
+ connection = await AsyncResponseSerialConnection.create(
279
+ port=port,
280
+ baud_rate=FS_BAUDRATE,
281
+ timeout=DEFAULT_FS_TIMEOUT,
282
+ number_of_retries=DEFAULT_COMMAND_RETRIES,
283
+ ack=FS_ACK,
284
+ loop=loop,
285
+ error_keyword=FS_ERROR_KEYWORD,
286
+ async_error_ack=FS_ASYNC_ERROR_ACK,
287
+ reset_buffer_before_write=True,
288
+ error_codes=StackerErrorCodes,
289
+ )
290
+ return cls(connection)
291
+
292
+ def __init__(self, connection: AsyncResponseSerialConnection) -> None:
293
+ """
294
+ Constructor
295
+
296
+ Args:
297
+ connection: Connection to the FLEX Stacker
298
+ """
299
+ self._connection = connection
300
+
301
+ async def connect(self) -> None:
302
+ """Connect to stacker."""
303
+ await self._connection.open()
304
+
305
+ async def disconnect(self) -> None:
306
+ """Disconnect from stacker."""
307
+ await self._connection.close()
308
+
309
+ async def is_connected(self) -> bool:
310
+ """Check connection to stacker."""
311
+ return await self._connection.is_open()
312
+
313
+ async def get_device_info(self) -> StackerInfo:
314
+ """Get Device Info."""
315
+ response = await self._connection.send_command(
316
+ GCODE.DEVICE_INFO.build_command()
317
+ )
318
+ device_info = self.parse_device_info(response)
319
+ reason_resp = await self._connection.send_command(
320
+ GCODE.GET_RESET_REASON.build_command()
321
+ )
322
+ reason = self.parse_reset_reason(reason_resp)
323
+ device_info.rr = reason
324
+ return device_info
325
+
326
+ async def set_serial_number(self, sn: str) -> None:
327
+ """Set Serial Number."""
328
+ if not re.match(r"^FST[\w]{1}[\d]{2}[\d]{8}[\d]+$", sn):
329
+ raise ValueError(
330
+ f"Invalid serial number: ({sn}) expected format: FSTA1020250119001"
331
+ )
332
+ resp = await self._connection.send_command(
333
+ GCODE.SET_SERIAL_NUMBER.build_command().add_element(sn)
334
+ )
335
+ if not re.match(rf"^{GCODE.SET_SERIAL_NUMBER}$", resp):
336
+ raise ValueError(f"Incorrect Response for set serial number: {resp}")
337
+
338
+ async def enable_motors(self, axis: List[StackerAxis]) -> None:
339
+ """Enables the axis motor if present, disables it otherwise."""
340
+ command = GCODE.ENABLE_MOTORS.build_command()
341
+ for a in axis:
342
+ command.add_element(a.name)
343
+ resp = await self._connection.send_command(command)
344
+ if not re.match(rf"^{GCODE.ENABLE_MOTORS}$", resp):
345
+ raise ValueError(f"Incorrect Response for enable motors: {resp}")
346
+
347
+ async def stop_motors(self) -> None:
348
+ """Stop all motor movement."""
349
+ resp = await self._connection.send_command(GCODE.STOP_MOTORS.build_command())
350
+ if not re.match(rf"^{GCODE.STOP_MOTORS}$", resp):
351
+ raise ValueError(f"Incorrect Response for stop motors: {resp}")
352
+
353
+ async def set_run_current(self, axis: StackerAxis, current: float) -> None:
354
+ """Set axis peak run current in amps."""
355
+ resp = await self._connection.send_command(
356
+ GCODE.SET_RUN_CURRENT.build_command().add_float(axis.name, current)
357
+ )
358
+ if not re.match(rf"^{GCODE.SET_RUN_CURRENT}$", resp):
359
+ raise ValueError(f"Incorrect Response for set run current: {resp}")
360
+
361
+ async def set_ihold_current(self, axis: StackerAxis, current: float) -> None:
362
+ """Set axis hold current in amps."""
363
+ resp = await self._connection.send_command(
364
+ GCODE.SET_IHOLD_CURRENT.build_command().add_float(axis.name, current)
365
+ )
366
+ if not re.match(rf"^{GCODE.SET_IHOLD_CURRENT}$", resp):
367
+ raise ValueError(f"Incorrect Response for set ihold current: {resp}")
368
+
369
+ async def set_stallguard_threshold(
370
+ self, axis: StackerAxis, enable: bool, threshold: int
371
+ ) -> None:
372
+ """Enables and sets the stallguard threshold for the given axis motor."""
373
+ assert axis != StackerAxis.L, "Stallguard not supported for L axis"
374
+ if not -64 < threshold < 63:
375
+ raise ValueError(
376
+ f"Threshold value ({threshold}) should be between -64 and 63."
377
+ )
378
+ resp = await self._connection.send_command(
379
+ GCODE.SET_STALLGUARD.build_command()
380
+ .add_int(axis.name, int(enable))
381
+ .add_int("T", threshold)
382
+ )
383
+ if not re.match(rf"^{GCODE.SET_STALLGUARD}$", resp):
384
+ raise ValueError(f"Incorrect Response for set stallguard threshold: {resp}")
385
+
386
+ async def enable_tof_sensor(self, sensor: TOFSensor, enable: bool) -> None:
387
+ """Enable or disable the TOF sensor."""
388
+ # Enabling the TOF sensor takes a while, so give extra timeout.
389
+ timeout = FS_TOF_TIMEOUT if enable else DEFAULT_FS_TIMEOUT
390
+ resp = await self._connection.send_command(
391
+ GCODE.ENABLE_TOF_SENSOR.build_command().add_int(sensor.name, int(enable)),
392
+ timeout=timeout,
393
+ )
394
+ if not re.match(rf"^{GCODE.ENABLE_TOF_SENSOR}$", resp):
395
+ raise ValueError(f"Incorrect Response for enable TOF sensor: {resp}")
396
+
397
+ async def set_tof_configuration(
398
+ self,
399
+ sensor: TOFSensor,
400
+ spad_map_id: SpadMapID,
401
+ active_range: Optional[ActiveRange] = None,
402
+ kilo_iterations: Optional[int] = None,
403
+ report_period_ms: Optional[int] = None,
404
+ histogram_dump: Optional[bool] = None,
405
+ ) -> None:
406
+ """Set the configuration of the TOF sensor.
407
+
408
+ :param sensor: The TOF sensor to configure.
409
+ :param spad_map_id: The pre-defined SPAD map which sets the fov and focus area (14 default).
410
+ :active_range: The operating mode Short-range high-accuracy (default) or long range.
411
+ :kilo_iterations: The Measurement iterations times 1024 (4000 default).
412
+ :report_period_ms: The reporting period before each measurement (500 default).
413
+ :histogram_dump: Enables/Disables histogram measurements (True default).
414
+ :return: None
415
+ """
416
+ command = (
417
+ GCODE.SET_TOF_CONFIGURATION.build_command()
418
+ .add_element(sensor.name)
419
+ .add_int("I", spad_map_id.value)
420
+ )
421
+ if active_range:
422
+ command.add_int("A", active_range.value)
423
+ if kilo_iterations:
424
+ command.add_int("K", kilo_iterations)
425
+ if report_period_ms:
426
+ command.add_int("P", report_period_ms)
427
+ if histogram_dump:
428
+ command.add_int("H", int(histogram_dump))
429
+ resp = await self._connection.send_command(command)
430
+ if not re.match(rf"^{GCODE.SET_TOF_CONFIGURATION}$", resp):
431
+ raise ValueError(f"Incorrect Response for set TOF configuration: {resp}")
432
+
433
+ async def get_tof_configuration(self, sensor: TOFSensor) -> TOFConfiguration:
434
+ """Get the configuration of the TOF sensor."""
435
+ resp = await self._connection.send_command(
436
+ GCODE.GET_TOF_CONFIGURATION.build_command().add_element(sensor.value)
437
+ )
438
+ return self.parse_get_tof_configuration(resp)
439
+
440
+ async def manage_tof_measurement(
441
+ self,
442
+ sensor: TOFSensor,
443
+ kind: MeasurementKind = MeasurementKind.HISTOGRAM,
444
+ start: bool = True,
445
+ ) -> TOFMeasurement:
446
+ """Start or stop Measurements from the TOF sensor."""
447
+ command = (
448
+ GCODE.MANAGE_TOF_MEASUREMENT.build_command()
449
+ .add_element(sensor.name)
450
+ .add_int("K", kind.value)
451
+ )
452
+ if not start:
453
+ command.add_element("C")
454
+ resp = await self._connection.send_command(command)
455
+ return self.parse_manage_tof_measurement(resp)
456
+
457
+ async def _get_tof_histogram_frame(
458
+ self, sensor: TOFSensor, resend: bool = False
459
+ ) -> TOFMeasurementFrame:
460
+ """Get the next measurement frame from TOF sensor or resend previous."""
461
+ command = GCODE.GET_TOF_MEASUREMENT.build_command().add_element(sensor.name)
462
+ if resend:
463
+ command.add_element("R")
464
+ resp = await self._connection.send_command(command)
465
+ return self.parse_get_tof_measurement(resp)
466
+
467
+ async def get_tof_histogram(self, sensor: TOFSensor) -> TOFMeasurementResult:
468
+ """Get the full histogram measurement from the TOF sensor."""
469
+ data = []
470
+ data_len = 0
471
+ next_frame_id = 0
472
+ resend = False
473
+ retries = FS_TOF_FRAME_RETRIES
474
+
475
+ # Cancel any ongoing measurements
476
+ status = await self.get_tof_sensor_status(sensor)
477
+ if status.state == TOFSensorState.MEASURING:
478
+ await self.manage_tof_measurement(sensor, start=False)
479
+
480
+ # Put the TOF sensor into histogram measurement mode
481
+ start = await self.manage_tof_measurement(
482
+ sensor, MeasurementKind.HISTOGRAM, start=True
483
+ )
484
+ # Request frames until the full histogram is transfered
485
+ while data_len < start.total_bytes:
486
+ try:
487
+ # Request next histogram frame
488
+ frame = await self._get_tof_histogram_frame(sensor, resend=resend)
489
+ # Validate histogram frame
490
+ validate_histogram_frame(frame.data, next_frame_id)
491
+ # append frame data
492
+ channel = frame.data[7:]
493
+ data.append(channel)
494
+ data_len += len(channel)
495
+ assert (
496
+ not data_len > start.total_bytes
497
+ ), f"Invalid number of bytes, expected {start.total_bytes} got {data_len}."
498
+ retries = FS_TOF_FRAME_RETRIES
499
+ next_frame_id += 1
500
+ resend = False
501
+ except (ValueError, NoResponse):
502
+ # There was a timeout or timing error, request previous frame.
503
+ if retries <= 0:
504
+ # Cancel the measurement
505
+ await self.manage_tof_measurement(sensor, start.kind, start=False)
506
+ raise RuntimeError(f"Exceded frame {next_frame_id} retries")
507
+ retries -= 1
508
+ resend = True
509
+ continue
510
+ except Exception as e:
511
+ # Cancel the histogram request
512
+ await self.manage_tof_measurement(sensor, start.kind, start=False)
513
+ raise RuntimeError(f"Could not get {sensor} Histogram", e)
514
+
515
+ # Parse channel bin measurements from data
516
+ #
517
+ # There are 10 channels (0 - 9), each channel is comprised of 3 * 128 bytes.
518
+ # The data starts with all the 128 LSB bytes, then all the 128 MID bytes, and
519
+ # finally all the 128 MSB bytes for all the channels as seen below.
520
+ #
521
+ # ch0 lsb = data[0], mid = data[10], msb = data[20]
522
+ # ch1 lsb = data[1], mid = data[11], msb = data[21]
523
+ # ch2 lsb = data[2], mid = data[12], msb = data[22]
524
+ # ...
525
+ # ch9 lsb = data[9], mid = data[19], msb = data[29]
526
+ #
527
+ # Each channel has 128 bins comprised of the lsb, mid, and msb payload from above.
528
+ # We can get the bins for each channel by combining the lsb, mid, and msb payload
529
+ # for that specific channel.
530
+ bins = {}
531
+ for ch in range(10): # 0-9 channels
532
+ # Get the lsb, mid, and msb payload for the ch
533
+ lsb = data[ch]
534
+ mid = data[ch + 10]
535
+ msb = data[ch + 20]
536
+ # combine lsb, mid, and msb bytes to generate bin count for the ch
537
+ bins[ch] = [
538
+ float((msb[b] << 16) | (mid[b] << 8) | lsb[b])
539
+ for b in range(NUMBER_OF_BINS)
540
+ ]
541
+ return TOFMeasurementResult(start.sensor, start.kind, bins)
542
+
543
+ async def set_motor_driver_register(
544
+ self, axis: StackerAxis, reg: int, value: int
545
+ ) -> None:
546
+ """Set the register of the given motor axis driver to the given value."""
547
+ resp = await self._connection.send_command(
548
+ GCODE.SET_MOTOR_DRIVER_REGISTER.build_command()
549
+ .add_int(axis.name, reg)
550
+ .add_element(str(value))
551
+ )
552
+ if not re.match(rf"^{GCODE.SET_MOTOR_DRIVER_REGISTER}$", resp):
553
+ raise ValueError(
554
+ f"Incorrect Response for set motor driver register: {resp}"
555
+ )
556
+
557
+ async def get_motor_driver_register(self, axis: StackerAxis, reg: int) -> int:
558
+ """Gets the register value of the given motor axis driver."""
559
+ response = await self._connection.send_command(
560
+ GCODE.GET_MOTOR_DRIVER_REGISTER.build_command().add_int(axis.name, reg)
561
+ )
562
+ return self.parse_get_motor_register(response)
563
+
564
+ async def set_tof_driver_register(
565
+ self, sensor: TOFSensor, reg: int, value: int
566
+ ) -> None:
567
+ """Set the register of the given tof sensor driver to the given value."""
568
+ resp = await self._connection.send_command(
569
+ GCODE.SET_TOF_DRIVER_REGISTER.build_command()
570
+ .add_int(sensor.name, reg)
571
+ .add_element(str(value))
572
+ )
573
+ if not re.match(rf"^{GCODE.SET_TOF_DRIVER_REGISTER}$", resp):
574
+ raise ValueError(
575
+ f"Incorrect Response for set tof sensor driver register: {resp}"
576
+ )
577
+
578
+ async def get_tof_driver_register(self, sensor: TOFSensor, reg: int) -> int:
579
+ """Gets the register value of the given tof sensor driver."""
580
+ response = await self._connection.send_command(
581
+ GCODE.GET_TOF_DRIVER_REGISTER.build_command().add_int(sensor.name, reg)
582
+ )
583
+ return self.parse_get_tof_sensor_register(response)
584
+
585
+ async def get_tof_sensor_status(self, sensor: TOFSensor) -> TOFSensorStatus:
586
+ """Get the status of the tof sensor."""
587
+ # This can fail early because the TOF sensor task cannot respond to messages
588
+ # while the TOF sensors are initializing.
589
+ try:
590
+ response = await self._connection.send_command(
591
+ GCODE.GET_TOF_SENSOR_STATUS.build_command().add_element(sensor.name),
592
+ timeout=FS_TOF_INIT_TIMEOUT,
593
+ )
594
+ return self.parse_tof_sensor_status(response)
595
+ except (TaskNotReady):
596
+ return TOFSensorStatus(
597
+ sensor, TOFSensorState.INITIALIZING, TOFSensorMode.UNKNOWN, False
598
+ )
599
+
600
+ async def get_motion_params(self, axis: StackerAxis) -> MoveParams:
601
+ """Get the motion parameters used by the given axis motor."""
602
+ response = await self._connection.send_command(
603
+ GCODE.GET_MOVE_PARAMS.build_command().add_element(axis.name)
604
+ )
605
+ return self.parse_move_params(response)
606
+
607
+ async def get_stallguard_threshold(self, axis: StackerAxis) -> StallGuardParams:
608
+ """Get the stallguard parameters by the given axis motor."""
609
+ response = await self._connection.send_command(
610
+ GCODE.GET_STALLGUARD_THRESHOLD.build_command().add_element(axis.name)
611
+ )
612
+ return self.parse_stallguard_params(response)
613
+
614
+ async def get_limit_switch(self, axis: StackerAxis, direction: Direction) -> bool:
615
+ """Get limit switch status.
616
+
617
+ :return: True if limit switch is triggered, False otherwise
618
+ """
619
+ response = await self.get_limit_switches_status()
620
+ return response.get(axis, direction)
621
+
622
+ async def get_limit_switches_status(self) -> LimitSwitchStatus:
623
+ """Get limit switch statuses for all axes."""
624
+ response = await self._connection.send_command(
625
+ GCODE.GET_LIMIT_SWITCH.build_command()
626
+ )
627
+ return self.parse_limit_switch_status(response)
628
+
629
+ async def get_platform_sensor(self, direction: Direction) -> bool:
630
+ """Get platform sensor at one direction."""
631
+ response = await self.get_platform_status()
632
+ return response.get(direction)
633
+
634
+ async def get_platform_status(self) -> PlatformStatus:
635
+ """Get platform sensor status.
636
+
637
+ :return: True if platform is detected, False otherwise
638
+ """
639
+ response = await self._connection.send_command(
640
+ GCODE.GET_PLATFORM_SENSOR.build_command()
641
+ )
642
+ return self.parse_platform_sensor_status(response)
643
+
644
+ async def get_hopper_door_closed(self) -> bool:
645
+ """Get whether or not door is closed.
646
+
647
+ :return: True if door is closed, False otherwise
648
+ """
649
+ response = await self._connection.send_command(
650
+ GCODE.GET_DOOR_SWITCH.build_command()
651
+ )
652
+ return self.parse_door_closed(response)
653
+
654
+ async def get_installation_detected(self) -> bool:
655
+ """Get whether or not installation is detected.
656
+
657
+ :return: True if installation is detected, False otherwise
658
+ """
659
+ response = await self._connection.send_command(
660
+ GCODE.GET_INSTALL_DETECTED.build_command()
661
+ )
662
+ return self.parse_installation_detected(response)
663
+
664
+ async def get_estop_engaged(self) -> bool:
665
+ """Get whether or not the estop was detected by this stacker.
666
+
667
+ :return: True if the estop is trigguered, False otherwise
668
+ """
669
+ response = await self._connection.send_command(
670
+ GCODE.GET_ESTOP_ENGAGED.build_command()
671
+ )
672
+ return self.parse_estop_engaged(response)
673
+
674
+ async def move_in_mm(
675
+ self, axis: StackerAxis, distance: float, params: MoveParams | None = None
676
+ ) -> MoveResult:
677
+ """Move axis by the given distance in mm."""
678
+ command = self.append_move_params(
679
+ GCODE.MOVE_TO.build_command().add_float(
680
+ axis.name, distance, GCODE_ROUNDING_PRECISION
681
+ ),
682
+ params,
683
+ )
684
+ try:
685
+ resp = await self._connection.send_command(command, timeout=FS_MOVE_TIMEOUT)
686
+ if not re.match(rf"^{GCODE.MOVE_TO}$", resp):
687
+ raise ValueError(f"Incorrect Response for move to: {resp}")
688
+ except MotorStallDetected:
689
+ self.reset_serial_buffers()
690
+ return MoveResult.STALL_ERROR
691
+ return MoveResult.NO_ERROR
692
+
693
+ async def move_to_limit_switch(
694
+ self, axis: StackerAxis, direction: Direction, params: MoveParams | None = None
695
+ ) -> MoveResult:
696
+ """Move until limit switch is triggered."""
697
+ command = self.append_move_params(
698
+ GCODE.MOVE_TO_SWITCH.build_command().add_int(axis.name, direction.value),
699
+ params,
700
+ )
701
+ try:
702
+ resp = await self._connection.send_command(command, timeout=FS_MOVE_TIMEOUT)
703
+ if not re.match(rf"^{GCODE.MOVE_TO_SWITCH}$", resp):
704
+ raise ValueError(f"Incorrect Response for move to switch: {resp}")
705
+ except MotorStallDetected:
706
+ self.reset_serial_buffers()
707
+ return MoveResult.STALL_ERROR
708
+ return MoveResult.NO_ERROR
709
+
710
+ async def home_axis(self, axis: StackerAxis, direction: Direction) -> MoveResult:
711
+ """Home axis."""
712
+ command = GCODE.HOME_AXIS.build_command().add_int(axis.name, direction.value)
713
+ try:
714
+ resp = await self._connection.send_command(command, timeout=FS_MOVE_TIMEOUT)
715
+ except MotorStallDetected:
716
+ self.reset_serial_buffers()
717
+ return MoveResult.STALL_ERROR
718
+ if not re.match(rf"^{GCODE.HOME_AXIS}$", resp):
719
+ raise ValueError(f"Incorrect Response for home axis: {resp}")
720
+ return MoveResult.NO_ERROR
721
+
722
+ async def set_led(
723
+ self,
724
+ power: float,
725
+ color: Optional[LEDColor] = None,
726
+ external: Optional[bool] = None,
727
+ pattern: Optional[LEDPattern] = None,
728
+ duration: Optional[int] = None,
729
+ reps: Optional[int] = None,
730
+ ) -> None:
731
+ """Set LED Status bar color and pattern.
732
+
733
+ :param power: Power of the LED (0-1.0), 0 is off, 1 is full power
734
+ :param color: Color of the LED
735
+ :param external: True if external LED, False if internal LED
736
+ :param pattern: Animation pattern of the LED status bar
737
+ :param duration: Animation duration in milliseconds (25-10000), 10s max
738
+ :param reps: Number of times to repeat the animation (-1 - 10), -1 is forever.
739
+ """
740
+ power = max(0, min(power, 1.0))
741
+ command = GCODE.SET_LED.build_command().add_float(
742
+ "P", power, GCODE_ROUNDING_PRECISION
743
+ )
744
+ if color is not None:
745
+ command.add_int("C", color.value)
746
+ if external is not None:
747
+ command.add_int("K", int(external))
748
+ if pattern is not None:
749
+ command.add_int("A", pattern.value)
750
+ if duration is not None:
751
+ duration = max(MIN_DURATION_MS, min(duration, MAX_DURATION_MS))
752
+ command.add_int("D", duration)
753
+ if reps is not None:
754
+ command.add_int("R", max(-1, min(reps, MAX_REPS)))
755
+ resp = await self._connection.send_command(command)
756
+ if not re.match(rf"^{GCODE.SET_LED}$", resp):
757
+ raise ValueError(f"Incorrect Response for set led: {resp}")
758
+
759
+ async def enter_programming_mode(self) -> None:
760
+ """Reboot into programming mode"""
761
+ command = GCODE.ENTER_BOOTLOADER.build_command()
762
+ await self._connection.send_dfu_command(command)
763
+ await self._connection.close()
764
+
765
+ def reset_serial_buffers(self) -> None:
766
+ """Reset the input and output serial buffers."""
767
+ self._connection._serial.reset_input_buffer()
768
+ self._connection._serial.reset_output_buffer()