bec-server 3.151.2__tar.gz → 3.152.0__tar.gz

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.
Files changed (211) hide show
  1. {bec_server-3.151.2 → bec_server-3.152.0}/PKG-INFO +1 -1
  2. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/device_server.py +5 -2
  3. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/device_serializer.py +60 -1
  4. bec_server-3.152.0/bec_server/scan_server/device_lock_registry.py +193 -0
  5. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/direct_scan_worker.py +18 -0
  6. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_assembler.py +22 -11
  7. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_queue.py +36 -5
  8. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_server.py +2 -0
  9. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/legacy_scans.py +2 -1
  10. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/move.py +2 -0
  11. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_actions.py +261 -10
  12. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_base.py +16 -0
  13. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/updated_move.py +2 -0
  14. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/scan_fixtures.py +6 -1
  15. {bec_server-3.151.2 → bec_server-3.152.0}/pyproject.toml +1 -1
  16. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_serializer.py +67 -0
  17. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_server.py +12 -2
  18. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_scan_actions.py +294 -6
  19. bec_server-3.152.0/tests/tests_scan_server/test_device_lock_registry.py +158 -0
  20. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_direct_scan_worker.py +38 -0
  21. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_assembler.py +35 -0
  22. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_queue.py +184 -2
  23. {bec_server-3.151.2 → bec_server-3.152.0}/.gitignore +0 -0
  24. {bec_server-3.151.2 → bec_server-3.152.0}/.zed/debug.json +0 -0
  25. {bec_server-3.151.2 → bec_server-3.152.0}/README.md +0 -0
  26. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/__init__.py +0 -0
  27. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/__init__.py +0 -0
  28. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/actor.py +0 -0
  29. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/builtin_actor_manager.py +0 -0
  30. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/manager.py +0 -0
  31. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/scan_interlock.py +0 -0
  32. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/worker.py +0 -0
  33. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/__init__.py +0 -0
  34. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/launch.py +0 -0
  35. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/service_handler.py +0 -0
  36. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/subprocess_launch.py +0 -0
  37. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/tmux_launch.py +0 -0
  38. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/__init__.py +0 -0
  39. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/cli/__init__.py +0 -0
  40. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/cli/launch.py +0 -0
  41. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_server.py +0 -0
  42. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_service.py +0 -0
  43. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_service_manager.py +0 -0
  44. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/lmfit1d_service.py +0 -0
  45. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/__init__.py +0 -0
  46. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/bec_message_handler.py +0 -0
  47. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/cli/__init__.py +0 -0
  48. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/cli/launch.py +0 -0
  49. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/__init__.py +0 -0
  50. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/config_update_handler.py +0 -0
  51. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/devicemanager.py +0 -0
  52. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/friendly_device_exceptions.py +0 -0
  53. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/rpc_handler.py +0 -0
  54. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/tests/__init__.py +0 -0
  55. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/tests/utils.py +0 -0
  56. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/__init__.py +0 -0
  57. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/async_writer.py +0 -0
  58. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/cli/__init__.py +0 -0
  59. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/cli/launch.py +0 -0
  60. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/default_writer.py +0 -0
  61. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/file_writer.py +0 -0
  62. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/file_writer_manager.py +0 -0
  63. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/merged_dicts.py +0 -0
  64. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer_plugins/__init__.py +0 -0
  65. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer_plugins/cSAXS.py +0 -0
  66. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.bec_linux_base +0 -0
  67. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.requirements +0 -0
  68. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.worker +0 -0
  69. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/__init__.py +0 -0
  70. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/_dev_runner.py +0 -0
  71. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/builtin_procedures.py +0 -0
  72. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/constants.py +0 -0
  73. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/container_utils.py +0 -0
  74. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/container_worker.py +0 -0
  75. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/manager.py +0 -0
  76. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/oop_worker_base.py +0 -0
  77. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/procedure_registry.py +0 -0
  78. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/protocol.py +0 -0
  79. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/subprocess_worker.py +0 -0
  80. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/worker_base.py +0 -0
  81. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/__init__.py +0 -0
  82. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/bec_emitter.py +0 -0
  83. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/cli/__init__.py +0 -0
  84. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/cli/launch.py +0 -0
  85. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/emitter.py +0 -0
  86. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/scan_bundler.py +0 -0
  87. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/__init__.py +0 -0
  88. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/beamline_state_manager.py +0 -0
  89. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/cli/__init__.py +0 -0
  90. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/cli/launch.py +0 -0
  91. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/errors.py +0 -0
  92. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/generator_scan_worker.py +0 -0
  93. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/instruction_handler.py +0 -0
  94. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/path_optimization.py +0 -0
  95. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_guard.py +0 -0
  96. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_gui_models.py +0 -0
  97. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_manager.py +0 -0
  98. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_plugins/__init__.py +0 -0
  99. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_plugins/otf_scan.py +0 -0
  100. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_stubs.py +0 -0
  101. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_worker.py +0 -0
  102. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/__init__.py +0 -0
  103. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/acquire.py +0 -0
  104. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/cont_line_scan.py +0 -0
  105. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/fermat_scan.py +0 -0
  106. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/grid_scan.py +0 -0
  107. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/hexagonal_scan.py +0 -0
  108. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/line_scan.py +0 -0
  109. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/line_sweep_scan.py +0 -0
  110. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/list_scan.py +0 -0
  111. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/log_scan.py +0 -0
  112. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/multi_region_grid_scan.py +0 -0
  113. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/multi_region_line_scan.py +0 -0
  114. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/position_generators.py +0 -0
  115. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/round_roi_scan.py +0 -0
  116. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/round_scan.py +0 -0
  117. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_argument_modifier.py +0 -0
  118. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_components.py +0 -0
  119. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_modifier.py +0 -0
  120. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/time_scan.py +0 -0
  121. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/__init__.py +0 -0
  122. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/fixtures.py +0 -0
  123. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/scan_hook_tests.py +0 -0
  124. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/utils.py +0 -0
  125. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/types.py +0 -0
  126. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/__init__.py +0 -0
  127. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/__init__.py +0 -0
  128. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_active_client_emitter.py +0 -0
  129. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_connector.py +0 -0
  130. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_forwarder.py +0 -0
  131. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_metadata_handler.py +0 -0
  132. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/config_handler.py +0 -0
  133. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/cli/__init__.py +0 -0
  134. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/cli/launch.py +0 -0
  135. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/scihub.py +0 -0
  136. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/service_handler/__init__.py +0 -0
  137. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/service_handler/service_handler.py +0 -0
  138. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/__init__.py +0 -0
  139. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/actor_test_utils.py +0 -0
  140. {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/helpers.py +0 -0
  141. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_actors/test_actors.py +0 -0
  142. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_main.py +0 -0
  143. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_service_handler.py +0 -0
  144. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_tmux_launch.py +0 -0
  145. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/conftest.py +0 -0
  146. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_cli_launch.py +0 -0
  147. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_server.py +0 -0
  148. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_service_manager.py +0 -0
  149. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_lmfit1d_service.py +0 -0
  150. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/conftest.py +0 -0
  151. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_config_handler.py +0 -0
  152. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_instructions.py +0 -0
  153. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_manager_ds.py +0 -0
  154. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_server_cli_launch.py +0 -0
  155. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_rpc_handler.py +0 -0
  156. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/conftest.py +0 -0
  157. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_async_file_writer.py +0 -0
  158. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer.py +0 -0
  159. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer_cli_launch.py +0 -0
  160. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer_manager.py +0 -0
  161. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/conftest.py +0 -0
  162. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_bec_emitter.py +0 -0
  163. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_scan_bundler.py +0 -0
  164. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_scan_bundler_cli_launch.py +0 -0
  165. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/conftest.py +0 -0
  166. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/__init__.py +0 -0
  167. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_acquire.py +0 -0
  168. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_cont_line_scan.py +0 -0
  169. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_fermat_scan.py +0 -0
  170. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_grid_scan.py +0 -0
  171. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_hexagonal_scan.py +0 -0
  172. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_line_scan.py +0 -0
  173. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_line_sweep_scan.py +0 -0
  174. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_list_scan.py +0 -0
  175. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_log_scan.py +0 -0
  176. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_move_scan.py +0 -0
  177. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_multi_region_grid_scan.py +0 -0
  178. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_multi_region_line_scan.py +0 -0
  179. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_position_generators.py +0 -0
  180. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_relative_scan_return_to_start.py +0 -0
  181. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_round_roi_scan.py +0 -0
  182. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_round_scan.py +0 -0
  183. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_scan_components.py +0 -0
  184. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_time_scan.py +0 -0
  185. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_updated_move_scan.py +0 -0
  186. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_beamline_state_manager.py +0 -0
  187. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_builtin_actor_manager.py +0 -0
  188. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_container_utils.py +0 -0
  189. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_generator_scan_worker.py +0 -0
  190. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_instruction_handler.py +0 -0
  191. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_legacy_scans.py +0 -0
  192. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_path_optimization.py +0 -0
  193. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_procedure_container_worker.py +0 -0
  194. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_procedures.py +0 -0
  195. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_argument_modifier.py +0 -0
  196. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_guard.py +0 -0
  197. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_gui_models.py +0 -0
  198. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_interlock.py +0 -0
  199. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_modifier.py +0 -0
  200. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_cli_launch.py +0 -0
  201. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_scan_manager.py +0 -0
  202. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_stub_status.py +0 -0
  203. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_stubs.py +0 -0
  204. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_worker.py +0 -0
  205. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/conftest.py +0 -0
  206. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_active_client_emitter.py +0 -0
  207. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_config_handler.py +0 -0
  208. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_connector.py +0 -0
  209. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_forwarder.py +0 -0
  210. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_metadata_handler.py +0 -0
  211. {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_scihub_cli_launch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec-server
3
- Version: 3.151.2
3
+ Version: 3.152.0
4
4
  Summary: BEC server
5
5
  Project-URL: Bug Tracker, https://github.com/bec-project/bec/issues
6
6
  Project-URL: Homepage, https://github.com/bec-project/bec
@@ -380,7 +380,7 @@ class DeviceServer(BECService):
380
380
  def on_stop_devices(self, msg: MessageObject, **_kwargs) -> None:
381
381
  """
382
382
  Callback for receiving device stop requests.
383
- Handles both stopping all devices (empty list) and stopping specific devices (list of device names).
383
+ Handles stop-all (`None`), stop-none (`[]`), and stopping specific devices (device-name list).
384
384
 
385
385
  Args:
386
386
  msg: MessageObject containing the stop request.
@@ -397,10 +397,13 @@ class DeviceServer(BECService):
397
397
  for stop_id in mvalue.metadata["stop_id"]:
398
398
  if stop_id is not None:
399
399
  self.requests_handler._stopped_requests.append(stop_id)
400
- if not mvalue.value:
400
+ if mvalue.value is None:
401
401
  self.stop_devices()
402
402
  logger.info("Received request to stop all devices.")
403
403
  return
404
+ if mvalue.value == []:
405
+ logger.info("Received request to stop no devices.")
406
+ return
404
407
  logger.info(f"Received request to stop devices: {mvalue.value}")
405
408
  self.stop_devices(mvalue.value)
406
409
 
@@ -5,11 +5,12 @@ is used to create the device interface for proxy objects on other services.
5
5
 
6
6
  import functools
7
7
  from contextlib import contextmanager
8
+ from enum import Enum
8
9
  from typing import Any, Generator
9
10
 
10
11
  import msgpack
11
12
  from ophyd import Device, Kind, PositionerBase, Signal
12
- from ophyd_devices import BECDeviceBase, ComputedSignal
13
+ from ophyd_devices import BECDeviceBase, ComputedSignal, PSIDeviceBase
13
14
  from ophyd_devices.utils.bec_signals import BECMessageSignal
14
15
 
15
16
  from bec_lib.bec_errors import DeviceConfigError
@@ -21,6 +22,26 @@ from bec_lib.signature_serializer import signature_to_dict
21
22
  logger = bec_logger.logger
22
23
 
23
24
 
25
+ class OwnershipMode(str, Enum):
26
+ """
27
+ Ownership policy used in serialized device info.
28
+
29
+ FREE: The device is free to be used by any scan or procedure without acquiring a lock.
30
+ This is typically used for read-only signals.
31
+ CLAIMABLE: The device can be locked by a scan or procedure. It can be used without a lock,
32
+ but if a scan or procedure wants to use it, it should acquire a lock first. Typical
33
+ examples are motors that are normally on readout priority "baseline". When a scan wants
34
+ to use the motor, it should acquire a lock first. If the motor is already locked by
35
+ another scan or procedure, the scan will wait until the lock is released.
36
+ PINNED: When enabled, the device has to be locked by a scan before it can be used. This is
37
+ typically used for devices that are stateful during a scan, such as detectors or flyers.
38
+ """
39
+
40
+ FREE = "free"
41
+ CLAIMABLE = "claimable"
42
+ PINNED = "pinned"
43
+
44
+
24
45
  @contextmanager
25
46
  def disable_lazy_wait_for_connection(
26
47
  device: PositionerBase | ComputedSignal | Signal | Device | BECDeviceBase,
@@ -117,6 +138,43 @@ def get_device_base_class(obj: Any) -> str:
117
138
  return "unknown"
118
139
 
119
140
 
141
+ def get_ownership_mode(
142
+ obj: PositionerBase | ComputedSignal | Signal | Device | BECDeviceBase,
143
+ ) -> OwnershipMode:
144
+ """
145
+ Resolve ownership policy for a device or signal.
146
+ The logic is as follows:
147
+ 1. If the class has an explicit ownership_mode attribute, use that.
148
+ 2. If the object is a PSIDeviceBase, it is pinned.
149
+ 3. If the object is a signal and has write_access set to False, it is free.
150
+ 4. Otherwise, it is claimable.
151
+
152
+ Args:
153
+ obj (PositionerBase | ComputedSignal | Signal | Device | BECDeviceBase): object to get the ownership mode from
154
+
155
+ Returns:
156
+ OwnershipMode: ownership mode of the object
157
+ """
158
+ explicit_mode = getattr(obj.__class__, "ownership_mode", None)
159
+ if explicit_mode is not None:
160
+ try:
161
+ return OwnershipMode(explicit_mode)
162
+ except ValueError as exc:
163
+ raise DeviceConfigError(
164
+ f"Invalid ownership_mode {explicit_mode!r} configured on "
165
+ f"{obj.__class__.__name__}. Expected one of: "
166
+ f"{', '.join(mode.value for mode in OwnershipMode)}."
167
+ ) from exc
168
+
169
+ if isinstance(obj, PSIDeviceBase):
170
+ return OwnershipMode.PINNED
171
+
172
+ if get_device_base_class(obj) == "signal" and getattr(obj, "write_access", None) is False:
173
+ return OwnershipMode.FREE
174
+
175
+ return OwnershipMode.CLAIMABLE
176
+
177
+
120
178
  def get_device_info(
121
179
  obj: PositionerBase | ComputedSignal | Signal | Device | BECDeviceBase, connect=True
122
180
  ) -> dict:
@@ -256,6 +314,7 @@ def get_device_info(
256
314
  "device_dotted_name": getattr(obj, "dotted_name", ""),
257
315
  "device_base_class": get_device_base_class(obj),
258
316
  "device_class": obj.__class__.__name__,
317
+ "ownership_mode": get_ownership_mode(obj).value,
259
318
  "read_access": getattr(obj, "read_access", None),
260
319
  "write_access": getattr(obj, "write_access", None),
261
320
  "signals": signals,
@@ -0,0 +1,193 @@
1
+ from __future__ import annotations
2
+
3
+ import threading
4
+ import time
5
+ from collections import defaultdict
6
+ from collections.abc import Callable, Iterable
7
+
8
+ from bec_lib.logger import bec_logger
9
+
10
+ logger = bec_logger.logger
11
+
12
+
13
+ class DeviceLockRegistry:
14
+ """Registry that tracks per-request device locks for the scan server."""
15
+
16
+ WAIT_INTERVAL_S: float = 0.1
17
+ WAIT_LOG_INTERVAL_S: float = 5.0
18
+
19
+ def __init__(self) -> None:
20
+ """Initialize the device lock registry."""
21
+ self._condition: threading.Condition = threading.Condition()
22
+
23
+ # Maps device names to the request ID that currently owns the lock.
24
+ self._device_owners: dict[str, str] = {}
25
+
26
+ # Maps request IDs to the set of device names they currently own.
27
+ self._owner_devices: dict[str, set[str]] = defaultdict(set)
28
+
29
+ # Maps request IDs to the set of device names they are currently waiting for.
30
+ self._pending_device_locks: dict[str, set[str]] = defaultdict(set)
31
+
32
+ def acquire_many(
33
+ self,
34
+ request_id: str,
35
+ devices: Iterable[str],
36
+ interruption_callback: Callable[[], None] | None = None,
37
+ queue_update_callback: Callable[[], None] | None = None,
38
+ ) -> list[str]:
39
+ """
40
+ Acquire locks for multiple devices on behalf of a request.
41
+
42
+ Args:
43
+ request_id (str): request identifier that will own the device locks.
44
+ devices (Iterable[str]): device names to lock.
45
+ interruption_callback (Callable[[], None] | None, optional):
46
+ callback invoked while waiting for a lock, allowing the caller
47
+ to react to interruptions. Defaults to None.
48
+ queue_update_callback (Callable[[], None] | None, optional):
49
+ callback invoked when queue-visible lock state changes, e.g.
50
+ owned or waiting devices. Defaults to None.
51
+
52
+ Returns:
53
+ list[str]: sorted device names whose locks were acquired.
54
+ """
55
+ device_names = sorted(set(devices))
56
+ if not device_names:
57
+ return []
58
+
59
+ next_log_time = 0.0
60
+ while True:
61
+ should_wait = False
62
+ should_queue_update = False
63
+ blocked_owners: dict[str, str] = {}
64
+ waiting_devices = sorted(self._pending_device_locks.get(request_id, set()))
65
+
66
+ with self._condition:
67
+ acquirable_devices: list[str] = []
68
+ blocked_devices: list[str] = []
69
+
70
+ for device in device_names:
71
+ current_owner = self._device_owners.get(device)
72
+ if current_owner == request_id:
73
+ # we already own this device, so we can skip it
74
+ continue
75
+ if current_owner is None:
76
+ # the device is not owned by anyone, so we can acquire it
77
+ acquirable_devices.append(device)
78
+ continue
79
+
80
+ # the device is owned by another request, so we need to wait for it
81
+ blocked_devices.append(device)
82
+ blocked_owners[device] = current_owner
83
+
84
+ for device in acquirable_devices:
85
+ self._device_owners[device] = request_id
86
+ self._owner_devices[request_id].add(device)
87
+
88
+ self._pending_device_locks[request_id] = set(blocked_devices)
89
+ if blocked_devices:
90
+ should_wait = True
91
+ next_log_time = self._log_waiting_for_device_lock(
92
+ request_id=request_id,
93
+ blocked_owners=blocked_owners,
94
+ next_log_time=next_log_time,
95
+ )
96
+ self._condition.wait(timeout=self.WAIT_INTERVAL_S)
97
+
98
+ next_waiting_devices = sorted(self._pending_device_locks[request_id])
99
+ should_queue_update = bool(acquirable_devices) or (
100
+ next_waiting_devices != waiting_devices
101
+ )
102
+
103
+ if should_queue_update and queue_update_callback is not None:
104
+ queue_update_callback()
105
+
106
+ if not should_wait:
107
+ return device_names
108
+
109
+ if interruption_callback is not None:
110
+ interruption_callback()
111
+
112
+ def acquire(
113
+ self,
114
+ request_id: str,
115
+ device: str,
116
+ interruption_callback: Callable[[], None] | None = None,
117
+ queue_update_callback: Callable[[], None] | None = None,
118
+ ) -> None:
119
+ """
120
+ Acquire the lock for a single device on behalf of a request.
121
+
122
+ If the device is already owned by another request, this call blocks
123
+ until the lock becomes available.
124
+
125
+ Args:
126
+ request_id (str): request identifier that will own the device lock.
127
+ device (str): device name to lock.
128
+ interruption_callback (Callable[[], None] | None, optional):
129
+ callback invoked while waiting for the lock. Defaults to None.
130
+ queue_update_callback (Callable[[], None] | None, optional):
131
+ callback invoked when queue-visible lock state changes. Defaults to None.
132
+ """
133
+ self.acquire_many(
134
+ request_id=request_id,
135
+ devices=[device],
136
+ interruption_callback=interruption_callback,
137
+ queue_update_callback=queue_update_callback,
138
+ )
139
+
140
+ def release_all(self, request_id: str) -> list[str]:
141
+ """
142
+ Release all device locks held by a request.
143
+
144
+ Args:
145
+ request_id (str): request identifier whose locks should be released.
146
+
147
+ Returns:
148
+ list[str]: sorted device names whose locks were released.
149
+ """
150
+ with self._condition:
151
+ devices = sorted(self._owner_devices.pop(request_id, set()))
152
+ for device in devices:
153
+ if self._device_owners.get(device) == request_id:
154
+ self._device_owners.pop(device, None)
155
+ self._condition.notify_all()
156
+ return devices
157
+
158
+ def get_owned_devices(self, request_id: str) -> list[str]:
159
+ """
160
+ Get the devices currently locked by a request.
161
+
162
+ Args:
163
+ request_id (str): request identifier whose locked devices should be returned.
164
+
165
+ Returns:
166
+ list[str]: sorted device names currently owned by the request.
167
+ """
168
+ with self._condition:
169
+ return sorted(self._owner_devices.get(request_id, set()))
170
+
171
+ def get_pending_devices(self, request_id: str) -> list[str]:
172
+ """
173
+ Get the devices that a request is currently waiting to acquire.
174
+
175
+ Args:
176
+ request_id (str): request identifier whose pending devices should be returned.
177
+ """
178
+ with self._condition:
179
+ return sorted(self._pending_device_locks.get(request_id, set()))
180
+
181
+ def _log_waiting_for_device_lock(
182
+ self, request_id: str, blocked_owners: dict[str, str], next_log_time: float
183
+ ) -> float:
184
+ now = time.monotonic()
185
+ if now < next_log_time:
186
+ return next_log_time
187
+
188
+ waiting_devices = ", ".join(sorted(blocked_owners))
189
+ owners = ", ".join(
190
+ f"{device} held by {owner}" for device, owner in sorted(blocked_owners.items())
191
+ )
192
+ logger.info(f"Request {request_id} waiting for device locks on {waiting_devices}; {owners}")
193
+ return now + self.WAIT_LOG_INTERVAL_S
@@ -81,6 +81,7 @@ class DirectScanWorker:
81
81
  queue = self.worker.current_instruction_queue_item
82
82
  try:
83
83
  with self.worker.device_manager._rpc_method(scan.actions.rpc_call):
84
+ scan.actions._initialize_scan()
84
85
  for step in SCAN_SEQUENCE:
85
86
  method = getattr(scan, step, None)
86
87
  if not method:
@@ -95,12 +96,27 @@ class DirectScanWorker:
95
96
  if not self._prepare_exception_cleanup(queue, exc):
96
97
  return
97
98
  self._handle_exception(exc)
99
+ finally:
100
+ self._release_scan_locks(scan)
101
+
98
102
  if queue is None:
99
103
  return
104
+
100
105
  queue.status = InstructionQueueStatus.COMPLETED
101
106
  self.worker.current_instruction_queue_item = None
102
107
  self.reset()
103
108
 
109
+ def _release_scan_locks(self, scan: ScanBase | None) -> None:
110
+ if scan is None:
111
+ return
112
+ request_id = scan.scan_info.metadata.get("RID")
113
+ if request_id is None:
114
+ return
115
+ registry = getattr(self.worker.parent, "device_lock_registry", None)
116
+ if registry is None:
117
+ return
118
+ registry.release_all(request_id)
119
+
104
120
  def _prepare_exception_cleanup(
105
121
  self, queue: DirectInstructionQueueItem | None, exc: Exception
106
122
  ) -> bool:
@@ -176,6 +192,7 @@ class DirectScanWorker:
176
192
  Update the queue info for the current instruction queue item.
177
193
  This is used to propagate the queue status to the client during the scan execution.
178
194
  """
195
+ logger.info(f"Updating queue info")
179
196
  self.worker.current_instruction_queue_item.parent.queue_manager.send_queue_status()
180
197
 
181
198
  def _propagate_error(self, content: str, exc: Exception):
@@ -253,6 +270,7 @@ class DirectScanWorker:
253
270
  else:
254
271
  self.scan.actions._send_scan_status("halted", reason=reason)
255
272
 
273
+ self._release_scan_locks(self.scan)
256
274
  queue.status = InstructionQueueStatus.STOPPED
257
275
  queue.append_to_queue_history()
258
276
  self.worker.parent.queue_manager.queues[self.worker.queue_name].abort()
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
+ from functools import partial
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from bec_lib import messages
@@ -129,18 +130,28 @@ class ScanAssembler:
129
130
  scan_cls, scan_info["signature"], resolved_args, resolved_kwargs
130
131
  )
131
132
 
132
- scan_instance = scan_cls(
133
- *resolved_args,
134
- device_manager=self.device_manager,
135
- redis_connector=self.connector,
136
- scan_modifier=get_scan_modifier(),
137
- metadata=msg.metadata,
138
- instruction_handler=self.parent.queue_manager.instruction_handler,
139
- scan_id=scan_id,
140
- request_inputs=request_inputs,
141
- **resolved_kwargs,
133
+ with self.device_manager._rpc_method(partial(self._raise_on_rpc_call, scan_cls)):
134
+ scan_instance = scan_cls(
135
+ *resolved_args,
136
+ device_manager=self.device_manager,
137
+ redis_connector=self.connector,
138
+ scan_modifier=get_scan_modifier(),
139
+ metadata=msg.metadata,
140
+ instruction_handler=self.parent.queue_manager.instruction_handler,
141
+ scan_id=scan_id,
142
+ request_inputs=request_inputs,
143
+ **resolved_kwargs,
144
+ )
145
+ return scan_instance
146
+
147
+ def _raise_on_rpc_call(self, scan_cls, device: str, func_call: str, *args, **kwargs):
148
+ # This function is used to raise an error if a runtime RPC call is made during scan initialization as it
149
+ # can lead to unpredictable behavior
150
+ raise RuntimeError(
151
+ f"The scan {scan_cls.__name__} attempted to make a runtime RPC call to `{func_call}` on device `{device}`. "
152
+ f"Please ensure that all runtime RPC calls are made within the scan's scan hooks and not during the scan's initialization."
153
+ f"If you want to set up a device before the scan starts, move the RPC call to the `prepare_scan` hook of the scan class."
142
154
  )
143
- return scan_instance
144
155
 
145
156
  def _assemble_request_inputs(self, scan_cls, args, kwargs) -> dict:
146
157
  request_inputs = {}
@@ -264,17 +264,20 @@ class QueueManager:
264
264
  return instruction_queue
265
265
  return None
266
266
 
267
- def stop_all_devices(self, stop_id: str | list[str] | None = None):
267
+ def stop_all_devices(
268
+ self, stop_id: str | list[str] | None = None, devices: list[str] | None = None
269
+ ):
268
270
  """
269
- Send a message to the device server to stop all devices.
271
+ Send a message to the device server to stop devices.
270
272
  Args:
271
273
  stop_id (str | None): An optional identifier for the stop request.
272
274
  If provided, this ID will be added to the list of stopped requests in the device server to
273
275
  prevent any instructions associated with this ID raising alarms after the stop command is issued.
274
276
  The stop_id can be a scan ID, request ID, or queue ID.
277
+ devices (list[str] | None): Optional list of devices to stop.
278
+ `None` means stop all devices, while an empty list means stop no devices.
275
279
  """
276
- # We send an empty list to indicate that all devices should be stopped
277
- msg = messages.VariableMessage(value=[], metadata={})
280
+ msg = messages.VariableMessage(value=devices, metadata={})
278
281
  if stop_id is not None:
279
282
  msg.metadata["stop_id"] = stop_id
280
283
  self.connector.send(MessageEndpoints.stop_devices(), msg)
@@ -417,7 +420,10 @@ class QueueManager:
417
420
  stop_id = instruction_queue.queue_id
418
421
  else:
419
422
  stop_id = instruction_queue.scan_id
420
- self.stop_all_devices(stop_id=stop_id)
423
+ self.stop_all_devices(
424
+ stop_id=stop_id,
425
+ devices=self._get_owned_devices_for_instruction_queue(instruction_queue),
426
+ )
421
427
 
422
428
  def _cancel_queue_item(
423
429
  self, target_queue_item: InstructionQueueItem | DirectInstructionQueueItem, queue: str
@@ -622,6 +628,27 @@ class QueueManager:
622
628
  return instr_queue.active_scan.scan_info.scan_id
623
629
  return instr_queue.active_request_block.scan_id
624
630
 
631
+ def _get_owned_devices_for_instruction_queue(
632
+ self, instruction_queue: InstructionQueueItem | DirectInstructionQueueItem
633
+ ) -> list[str] | None:
634
+ registry = getattr(self.parent, "device_lock_registry", None)
635
+ if registry is None:
636
+ return None if isinstance(instruction_queue, InstructionQueueItem) else []
637
+ if isinstance(instruction_queue, DirectInstructionQueueItem):
638
+ if instruction_queue.active_scan is None:
639
+ return []
640
+ request_id = instruction_queue.active_scan.scan_info.metadata.get("RID")
641
+ else:
642
+ if instruction_queue.active_request_block is None:
643
+ return None
644
+ request_id = instruction_queue.active_request_block.RID
645
+ if request_id is None:
646
+ return None if isinstance(instruction_queue, InstructionQueueItem) else []
647
+ devices = registry.get_owned_devices(request_id)
648
+ if isinstance(instruction_queue, InstructionQueueItem) and not devices:
649
+ return None
650
+ return devices
651
+
625
652
  def _wait_for_queue_to_appear_in_history(
626
653
  self, scan_id, queue, timeout=60
627
654
  ) -> InstructionQueueItem:
@@ -1176,6 +1203,8 @@ class RequestBlock:
1176
1203
  scan_number=self.scan_number,
1177
1204
  scan_id=self.scan_id,
1178
1205
  report_instructions=self.scan_report_instructions,
1206
+ owned_device_locks=[],
1207
+ pending_device_locks=[],
1179
1208
  )
1180
1209
 
1181
1210
 
@@ -1672,6 +1701,8 @@ class DirectInstructionQueueItem:
1672
1701
  scan_number=self._get_scan_number(scan),
1673
1702
  scan_id=scan.scan_info.scan_id,
1674
1703
  report_instructions=scan.scan_info.scan_report_instructions,
1704
+ owned_device_locks=scan.actions.get_owned_device_locks(),
1705
+ pending_device_locks=scan.actions.get_pending_device_locks(),
1675
1706
  )
1676
1707
 
1677
1708
  @property
@@ -18,6 +18,7 @@ from bec_server.procedures.manager import ProcedureManager
18
18
  from bec_server.procedures.subprocess_worker import SubProcessWorker
19
19
 
20
20
  from .beamline_state_manager import BeamlineStateManager
21
+ from .device_lock_registry import DeviceLockRegistry
21
22
  from .scan_assembler import ScanAssembler
22
23
  from .scan_guard import ScanGuard
23
24
  from .scan_manager import ScanManager
@@ -32,6 +33,7 @@ logger = bec_logger.logger
32
33
  class ScanServer(BECService):
33
34
  def __init__(self, config: ServiceConfig, connector_cls: type[RedisConnector]):
34
35
  super().__init__(config, connector_cls, unique_service=True)
36
+ self.device_lock_registry = DeviceLockRegistry()
35
37
  self._start_scan_manager()
36
38
  self._start_device_manager()
37
39
  self._start_queue_manager()
@@ -256,6 +256,7 @@ class RequestBase(ABC):
256
256
  *args,
257
257
  device_manager: DeviceManagerBase = None,
258
258
  monitored: list = None,
259
+ on_request: list = None,
259
260
  parameter: dict = None,
260
261
  metadata: dict = None,
261
262
  instruction_handler: InstructionHandler = None,
@@ -283,7 +284,7 @@ class RequestBase(ABC):
283
284
  self.readout_priority = {
284
285
  "monitored": monitored if monitored is not None else [],
285
286
  "baseline": [],
286
- "on_request": [],
287
+ "on_request": on_request if on_request is not None else [],
287
288
  "async": [],
288
289
  }
289
290
  self.update_readout_priority()
@@ -71,6 +71,8 @@ class MoveScan(ScanBase):
71
71
  self.motors = list(self.motor_args_bundles.keys())
72
72
  self.relative = relative
73
73
 
74
+ self.actions.acquire_device_lock(self.motors)
75
+
74
76
  # Update the default scan info with provided parameters.
75
77
  self.update_scan_info(relative=relative, scan_report_devices=self.motors)
76
78