bec-server 4.4.2__tar.gz → 4.5.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 (215) hide show
  1. {bec_server-4.4.2 → bec_server-4.5.0}/PKG-INFO +1 -1
  2. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/direct_scan_worker.py +10 -3
  3. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_guard.py +14 -22
  4. bec_server-4.5.0/bec_server/scan_server/scan_hook_broadcaster.py +114 -0
  5. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_queue.py +25 -5
  6. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_actions.py +95 -5
  7. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_modifier.py +7 -2
  8. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_status.py +131 -29
  9. {bec_server-4.4.2 → bec_server-4.5.0}/pyproject.toml +1 -1
  10. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_scan_actions.py +399 -0
  11. bec_server-4.5.0/tests/tests_scan_server/scans/test_scan_status.py +501 -0
  12. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_direct_scan_worker.py +113 -0
  13. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_guard.py +103 -6
  14. bec_server-4.5.0/tests/tests_scan_server/test_scan_hook_broadcaster.py +216 -0
  15. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_modifier.py +98 -1
  16. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_server_queue.py +80 -0
  17. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_worker.py +2 -1
  18. bec_server-4.4.2/tests/tests_scan_server/scans/test_scan_status.py +0 -146
  19. {bec_server-4.4.2 → bec_server-4.5.0}/.gitignore +0 -0
  20. {bec_server-4.4.2 → bec_server-4.5.0}/.zed/debug.json +0 -0
  21. {bec_server-4.4.2 → bec_server-4.5.0}/README.md +0 -0
  22. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/__init__.py +0 -0
  23. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/__init__.py +0 -0
  24. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/actor.py +0 -0
  25. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/builtin_actor_manager.py +0 -0
  26. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/manager.py +0 -0
  27. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/scan_interlock.py +0 -0
  28. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/actors/worker.py +0 -0
  29. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/bec_server_utils/__init__.py +0 -0
  30. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/bec_server_utils/launch.py +0 -0
  31. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/bec_server_utils/service_handler.py +0 -0
  32. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/bec_server_utils/subprocess_launch.py +0 -0
  33. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/bec_server_utils/tmux_launch.py +0 -0
  34. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/__init__.py +0 -0
  35. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/cli/__init__.py +0 -0
  36. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/cli/launch.py +0 -0
  37. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/dap_server.py +0 -0
  38. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/dap_service.py +0 -0
  39. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/dap_service_manager.py +0 -0
  40. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/data_processing/lmfit1d_service.py +0 -0
  41. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/__init__.py +0 -0
  42. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/bec_message_handler.py +0 -0
  43. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/cli/__init__.py +0 -0
  44. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/cli/launch.py +0 -0
  45. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/device_server.py +0 -0
  46. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/devices/__init__.py +0 -0
  47. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/devices/config_update_handler.py +0 -0
  48. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/devices/device_serializer.py +0 -0
  49. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/devices/devicemanager.py +0 -0
  50. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/friendly_device_exceptions.py +0 -0
  51. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/ophyd_callback_monitor.py +0 -0
  52. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/rpc_handler.py +0 -0
  53. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/tests/__init__.py +0 -0
  54. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/device_server/tests/utils.py +0 -0
  55. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/__init__.py +0 -0
  56. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/async_writer.py +0 -0
  57. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/cli/__init__.py +0 -0
  58. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/cli/launch.py +0 -0
  59. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/default_writer.py +0 -0
  60. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/file_writer.py +0 -0
  61. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/file_writer_manager.py +0 -0
  62. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/file_writer/merged_dicts.py +0 -0
  63. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/Containerfile.bec_linux_base +0 -0
  64. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/Containerfile.requirements +0 -0
  65. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/Containerfile.worker +0 -0
  66. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/__init__.py +0 -0
  67. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/_dev_runner.py +0 -0
  68. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/builtin_procedures.py +0 -0
  69. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/constants.py +0 -0
  70. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/container_utils.py +0 -0
  71. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/container_worker.py +0 -0
  72. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/manager.py +0 -0
  73. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/oop_worker_base.py +0 -0
  74. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/procedure_registry.py +0 -0
  75. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/protocol.py +0 -0
  76. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/queue_manager.py +0 -0
  77. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/subprocess_worker.py +0 -0
  78. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/procedures/worker_base.py +0 -0
  79. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/__init__.py +0 -0
  80. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/bec_emitter.py +0 -0
  81. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/cli/__init__.py +0 -0
  82. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/cli/launch.py +0 -0
  83. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/emitter.py +0 -0
  84. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_bundler/scan_bundler.py +0 -0
  85. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/__init__.py +0 -0
  86. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/beamline_state_manager.py +0 -0
  87. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/cli/__init__.py +0 -0
  88. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/cli/launch.py +0 -0
  89. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/device_lock_registry.py +0 -0
  90. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/errors.py +0 -0
  91. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/instruction_handler.py +0 -0
  92. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_assembler.py +0 -0
  93. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_manager.py +0 -0
  94. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_server.py +0 -0
  95. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scan_worker.py +0 -0
  96. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/__init__.py +0 -0
  97. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/acquire.py +0 -0
  98. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/cont_line_scan.py +0 -0
  99. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/device_rpc.py +0 -0
  100. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/fermat_scan.py +0 -0
  101. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/grid_scan.py +0 -0
  102. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/hexagonal_scan.py +0 -0
  103. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/line_scan.py +0 -0
  104. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/line_sweep_scan.py +0 -0
  105. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/list_scan.py +0 -0
  106. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/log_scan.py +0 -0
  107. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/move.py +0 -0
  108. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/multi_region_grid_scan.py +0 -0
  109. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/multi_region_line_scan.py +0 -0
  110. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/path_optimization.py +0 -0
  111. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/position_generators.py +0 -0
  112. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/round_roi_scan.py +0 -0
  113. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/round_scan.py +0 -0
  114. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_argument_modifier.py +0 -0
  115. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_base.py +0 -0
  116. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/scan_components.py +0 -0
  117. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/time_scan.py +0 -0
  118. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/scans/updated_move.py +0 -0
  119. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/tests/__init__.py +0 -0
  120. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/tests/fixtures.py +0 -0
  121. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/tests/scan_fixtures.py +0 -0
  122. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/tests/scan_hook_tests.py +0 -0
  123. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scan_server/tests/utils.py +0 -0
  124. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/__init__.py +0 -0
  125. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/__init__.py +0 -0
  126. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/atlas_active_client_emitter.py +0 -0
  127. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/atlas_connector.py +0 -0
  128. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/atlas_forwarder.py +0 -0
  129. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/atlas_metadata_handler.py +0 -0
  130. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/atlas_plugin_repo_emitter.py +0 -0
  131. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/atlas/config_handler.py +0 -0
  132. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/cli/__init__.py +0 -0
  133. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/cli/launch.py +0 -0
  134. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/scihub.py +0 -0
  135. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/service_handler/__init__.py +0 -0
  136. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/scihub/service_handler/service_handler.py +0 -0
  137. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/test/__init__.py +0 -0
  138. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/test/actor_test_utils.py +0 -0
  139. {bec_server-4.4.2 → bec_server-4.5.0}/bec_server/test/helpers.py +0 -0
  140. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_actors/test_actors.py +0 -0
  141. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_bec_server_utils/test_main.py +0 -0
  142. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_bec_server_utils/test_service_handler.py +0 -0
  143. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_bec_server_utils/test_subprocess_launch.py +0 -0
  144. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_bec_server_utils/test_tmux_launch.py +0 -0
  145. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_data_processing/conftest.py +0 -0
  146. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_data_processing/test_dap_cli_launch.py +0 -0
  147. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_data_processing/test_dap_server.py +0 -0
  148. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_data_processing/test_dap_service_manager.py +0 -0
  149. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_data_processing/test_lmfit1d_service.py +0 -0
  150. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/conftest.py +0 -0
  151. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_config_handler.py +0 -0
  152. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_config_metadata_refresh.py +0 -0
  153. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_device_instructions.py +0 -0
  154. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_device_manager_ds.py +0 -0
  155. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_device_serializer.py +0 -0
  156. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_device_server.py +0 -0
  157. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_device_server_cli_launch.py +0 -0
  158. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_ophyd_callback_monitor.py +0 -0
  159. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_device_server/test_rpc_handler.py +0 -0
  160. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_file_writer/conftest.py +0 -0
  161. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_file_writer/test_async_file_writer.py +0 -0
  162. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_file_writer/test_file_writer.py +0 -0
  163. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_file_writer/test_file_writer_cli_launch.py +0 -0
  164. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_file_writer/test_file_writer_manager.py +0 -0
  165. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_bundler/conftest.py +0 -0
  166. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_bundler/test_bec_emitter.py +0 -0
  167. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_bundler/test_scan_bundler.py +0 -0
  168. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_bundler/test_scan_bundler_cli_launch.py +0 -0
  169. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/conftest.py +0 -0
  170. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/__init__.py +0 -0
  171. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_acquire.py +0 -0
  172. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_cont_line_scan.py +0 -0
  173. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_device_rpc_scan.py +0 -0
  174. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_fermat_scan.py +0 -0
  175. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_grid_scan.py +0 -0
  176. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_hexagonal_scan.py +0 -0
  177. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_line_scan.py +0 -0
  178. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_line_sweep_scan.py +0 -0
  179. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_list_scan.py +0 -0
  180. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_log_scan.py +0 -0
  181. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_move_scan.py +0 -0
  182. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_multi_region_grid_scan.py +0 -0
  183. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_multi_region_line_scan.py +0 -0
  184. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_position_preview.py +0 -0
  185. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_relative_scan_return_to_start.py +0 -0
  186. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_round_roi_scan.py +0 -0
  187. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_round_scan.py +0 -0
  188. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_scan_components.py +0 -0
  189. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_time_scan.py +0 -0
  190. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/scans/test_updated_move_scan.py +0 -0
  191. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_beamline_state_manager.py +0 -0
  192. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_builtin_actor_manager.py +0 -0
  193. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_container_utils.py +0 -0
  194. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_device_lock_registry.py +0 -0
  195. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_instruction_handler.py +0 -0
  196. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_path_optimization.py +0 -0
  197. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_procedure_container_worker.py +0 -0
  198. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_procedure_queue_pool.py +0 -0
  199. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_procedure_submission.py +0 -0
  200. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_procedure_subprocess_worker.py +0 -0
  201. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_procedures.py +0 -0
  202. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_argument_modifier.py +0 -0
  203. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_assembler.py +0 -0
  204. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_fixture_imports.py +0 -0
  205. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_interlock.py +0 -0
  206. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_server_cli_launch.py +0 -0
  207. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scan_server/test_scan_server_scan_manager.py +0 -0
  208. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/conftest.py +0 -0
  209. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_active_client_emitter.py +0 -0
  210. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_config_handler.py +0 -0
  211. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_connector.py +0 -0
  212. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_forwarder.py +0 -0
  213. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_metadata_handler.py +0 -0
  214. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_atlas_plugin_repo_emitter.py +0 -0
  215. {bec_server-4.4.2 → bec_server-4.5.0}/tests/tests_scihub/test_scihub_cli_launch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: bec-server
3
- Version: 4.4.2
3
+ Version: 4.5.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
@@ -79,6 +79,7 @@ class DirectScanWorker:
79
79
  scan.actions._update_queue_info_callback = self.update_queue_info
80
80
  queue = self.worker.current_instruction_queue_item
81
81
  try:
82
+ scan.actions._hook_broadcaster.start()
82
83
  with self.worker.device_manager._rpc_method(scan.actions.rpc_call):
83
84
  scan.actions._initialize_scan()
84
85
  for step in SCAN_SEQUENCE:
@@ -86,7 +87,8 @@ class DirectScanWorker:
86
87
  if not method:
87
88
  raise ScanAbortion(f"Scan is missing required method: {step}")
88
89
  self.check_for_interruption()
89
- method()
90
+ with scan.actions._scan_hook_context(step):
91
+ method()
90
92
  self.run_post_scan_step(step, scan)
91
93
  except ScanAbortion as exc:
92
94
  if not self._prepare_exception_cleanup(queue, exc):
@@ -97,7 +99,10 @@ class DirectScanWorker:
97
99
  return
98
100
  self._handle_exception(exc)
99
101
  finally:
100
- self._release_scan_locks(scan)
102
+ try:
103
+ scan.actions._hook_broadcaster.stop()
104
+ finally:
105
+ self._release_scan_locks(scan)
101
106
 
102
107
  if queue is None:
103
108
  return
@@ -245,7 +250,9 @@ class DirectScanWorker:
245
250
  try:
246
251
  scan._shutdown_event.clear()
247
252
  with self.worker.device_manager._rpc_method(scan.actions.rpc_call):
248
- scan.on_exception(hook_exc)
253
+ # pylint: disable=protected-access
254
+ with scan.actions._scan_hook_context("on_exception"):
255
+ scan.on_exception(hook_exc)
249
256
 
250
257
  except Exception:
251
258
  scan.actions.send_client_info("")
@@ -8,7 +8,6 @@ from typing import TYPE_CHECKING
8
8
  from bec_lib import messages
9
9
  from bec_lib.endpoints import MessageEndpoints
10
10
  from bec_lib.logger import bec_logger
11
- from bec_server.scan_server.scan_queue import ScanQueueStatus
12
11
 
13
12
  logger = bec_logger.logger
14
13
 
@@ -308,28 +307,21 @@ class ScanGuard:
308
307
  logger.error(f"Invalid queue: {target_queue}")
309
308
  self._send_scan_queue_order_change_response(False, f"Invalid queue: {target_queue}")
310
309
  return
311
- if self.parent.queue_manager.queues[target_queue].status != ScanQueueStatus.PAUSED:
312
- logger.error(f"Queue {target_queue} is not paused.")
313
- self._send_scan_queue_order_change_response(
314
- False, f"Queue {target_queue} is not paused. Cannot move scans."
315
- )
316
- return
317
- if msg.action == "move_to" and msg.target_position is None:
318
- logger.error("Missing target_position")
319
- self._send_scan_queue_order_change_response(
320
- False, "Missing target_position for move_to"
321
- )
322
- return
323
-
324
310
  queue = self.parent.queue_manager.queues[target_queue]
325
- for scan in queue.queue:
326
- if msg.scan_id in scan.scan_id:
327
- break
328
- else:
329
- logger.error(f"Scan {msg.scan_id} not found in queue {target_queue}")
330
- self._send_scan_queue_order_change_response(
331
- False, f"Scan {msg.scan_id} not found in queue {target_queue}"
332
- )
311
+ with queue._lock: # pylint: disable=protected-access
312
+ active = queue.active_instruction_queue
313
+ if active is not None and msg.scan_id in active.scan_id:
314
+ rejection = "The running scan cannot be moved."
315
+ elif msg.action == "move_to" and msg.target_position is None:
316
+ rejection = "Missing target_position for move_to"
317
+ elif not any(msg.scan_id in scan.scan_id for scan in queue.queue):
318
+ rejection = f"Scan {msg.scan_id} not found in queue {target_queue}"
319
+ else:
320
+ rejection = None
321
+
322
+ if rejection is not None:
323
+ logger.error(rejection)
324
+ self._send_scan_queue_order_change_response(False, rejection)
333
325
  return
334
326
  self.device_manager.connector.send(sqoc, msg)
335
327
  self._send_scan_queue_order_change_response(True, "Order change accepted")
@@ -0,0 +1,114 @@
1
+ """Broadcast and store the latest scan lifecycle or instruction update for clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from typing import TYPE_CHECKING
7
+
8
+ from bec_lib import messages
9
+ from bec_lib.endpoints import MessageEndpoints
10
+ from bec_lib.logger import bec_logger
11
+
12
+ if TYPE_CHECKING:
13
+ from bec_lib.redis_connector import RedisConnector
14
+
15
+ logger = bec_logger.logger
16
+
17
+
18
+ class ScanHookBroadcaster:
19
+ """Send only the latest update in each 0.1 second broadcast window, unless flushed at once."""
20
+
21
+ def __init__(self, connector: RedisConnector) -> None:
22
+ self._connector = connector
23
+ self._lock = threading.Lock()
24
+ self._publish_lock = threading.Lock()
25
+ self._latest_update: messages.ScanHookMessage | None = None
26
+ self._timer: threading.Timer | None = None
27
+ self._active = False
28
+ self._stopped = False
29
+
30
+ @property
31
+ def active(self) -> bool:
32
+ """Whether events are collected, i.e. the scan worker is executing the scan."""
33
+ with self._lock:
34
+ return self._active
35
+
36
+ def start(self) -> None:
37
+ """Enable event collection when the scan worker begins execution."""
38
+ with self._lock:
39
+ if self._stopped:
40
+ raise RuntimeError("A stopped scan hook broadcaster cannot be restarted.")
41
+ self._active = True
42
+
43
+ def stop(self) -> None:
44
+ """Stop collection, join the pending timer, and attempt final delivery."""
45
+ with self._lock:
46
+ if self._stopped:
47
+ return
48
+ self._stopped = True
49
+ self._active = False
50
+ timer = self._timer
51
+ if timer is not None:
52
+ timer.cancel()
53
+ if timer is not None and timer is not threading.current_thread():
54
+ timer.join()
55
+ self._flush_scheduled()
56
+
57
+ def add(self, event: messages.ScanHookMessage) -> None:
58
+ """Replace the pending update and schedule a flush when the first event arrives.
59
+
60
+ Args:
61
+ event (messages.ScanHookMessage): Lifecycle or instruction event to broadcast.
62
+ """
63
+ with self._lock:
64
+ if not self._active:
65
+ return
66
+ self._latest_update = event
67
+ if self._timer is None:
68
+ self._timer = threading.Timer(0.1, self._flush_scheduled)
69
+ self._timer.daemon = True
70
+ self._timer.start()
71
+
72
+ def _flush_scheduled(self) -> None:
73
+ try:
74
+ self.flush()
75
+ except Exception: # pylint: disable=broad-except
76
+ logger.exception("Failed to broadcast scan hook events")
77
+
78
+ def flush_now(self) -> None:
79
+ """Send the pending update without waiting for the window, e.g. for a step change.
80
+
81
+ A failed publication is logged and does not interrupt the caller.
82
+ """
83
+ self._flush_scheduled()
84
+
85
+ def flush(self) -> None:
86
+ """Send the latest pending update and cancel the pending timer.
87
+
88
+ Scans call this on every exit path to deliver their final update.
89
+ Failed publications retain the update for retry unless a newer one arrives.
90
+ """
91
+ timer = None
92
+ try:
93
+ # Serialize publications without blocking producers on network I/O.
94
+ with self._publish_lock:
95
+ with self._lock:
96
+ timer = self._timer
97
+ self._timer = None
98
+ if timer is not None:
99
+ timer.cancel()
100
+ event = self._latest_update
101
+ self._latest_update = None
102
+ if event is None:
103
+ return
104
+ try:
105
+ self._connector.set_and_publish(MessageEndpoints.scan_hook(), event)
106
+ except Exception:
107
+ with self._lock:
108
+ if not self._stopped and self._latest_update is None:
109
+ self._latest_update = event
110
+ raise
111
+ finally:
112
+ # A timer may itself be waiting to publish, so release both locks first.
113
+ if timer is not None and timer is not threading.current_thread():
114
+ timer.join()
@@ -237,14 +237,21 @@ class QueueManager:
237
237
  msg (messages.ScanQueueOrderMessage): ScanQueueOrderMessage
238
238
 
239
239
  """
240
- with self._lock:
240
+ target_queue = msg.queue
241
+ # take the worker's queue lock first (same order as the worker) so the running item
242
+ # cannot be popped while we decide what may move
243
+ with self.queues[target_queue]._lock, self._lock:
241
244
  logger.info(f"Handling scan queue order change: {msg}")
242
- target_queue = msg.queue
243
245
  queue = self.queues[target_queue].queue
244
246
  queue_item = self._get_queue_item_by_scan_id(msg)
245
247
  if not queue_item:
246
248
  logger.error(f"Scan {msg.scan_id} not found in queue {target_queue}")
247
249
  return
250
+ # in a running queue, the active item stays first and nothing moves in front of it
251
+ first = self._first_movable_index(target_queue)
252
+ if queue.index(queue_item) < first:
253
+ logger.error(f"Scan {msg.scan_id} is running and cannot be moved")
254
+ return
248
255
 
249
256
  if msg.action == "move_to":
250
257
  # move the scan to the target position
@@ -252,7 +259,7 @@ class QueueManager:
252
259
  logger.error("Missing target_position")
253
260
  return
254
261
 
255
- position = max(0, min(msg.target_position, len(queue) - 1))
262
+ position = max(first, min(msg.target_position, len(queue) - 1))
256
263
 
257
264
  queue.remove(queue_item)
258
265
  queue.insert(position, queue_item)
@@ -260,7 +267,7 @@ class QueueManager:
260
267
  if msg.action == "move_up":
261
268
  # move the scan up by one position
262
269
  idx = queue.index(queue_item)
263
- if idx == 0:
270
+ if idx <= first:
264
271
  return
265
272
  queue.remove(queue_item)
266
273
  queue.insert(idx - 1, queue_item)
@@ -276,7 +283,7 @@ class QueueManager:
276
283
  if msg.action == "move_top":
277
284
  # move the scan to the top of the queue
278
285
  queue.remove(queue_item)
279
- queue.insert(0, queue_item)
286
+ queue.insert(first, queue_item)
280
287
 
281
288
  if msg.action == "move_bottom":
282
289
  # move the scan to the bottom of the queue
@@ -285,6 +292,19 @@ class QueueManager:
285
292
 
286
293
  self.send_queue_status()
287
294
 
295
+ def _first_movable_index(self, queue_name: str) -> int:
296
+ """
297
+ Index of the first queue item that may be moved: 1 while the active item is at the head,
298
+ paused or not, else 0. Nothing may move in front of the active item: the worker pops the
299
+ head once the active item is done, so anything placed before it would leave the finished
300
+ item in the queue to be started again.
301
+ """
302
+ scan_queue = self.queues[queue_name]
303
+ active = scan_queue.active_instruction_queue
304
+ if active is not None and scan_queue.queue and scan_queue.queue[0] is active:
305
+ return 1
306
+ return 0
307
+
288
308
  def _get_queue_item_by_scan_id(
289
309
  self, msg: messages.ScanQueueOrderMessage
290
310
  ) -> DirectInstructionQueueItem | None:
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import functools
4
4
  import os
5
+ import time
5
6
  import uuid
6
- from collections.abc import Iterable
7
+ from collections.abc import Iterable, Iterator
8
+ from contextlib import contextmanager
7
9
  from string import Template
8
10
  from typing import TYPE_CHECKING, Any, Callable, Literal, TypeAlias
9
11
 
@@ -18,6 +20,8 @@ from bec_lib.logger import bec_logger
18
20
  from bec_lib.messaging_hooks import MessagingEvent
19
21
  from bec_lib.messaging_services import NotificationMessageObject
20
22
  from bec_lib.utils.scan_utils import compose_cli_input_from_scan_info
23
+ from bec_server.scan_server.errors import UserScanInterruption
24
+ from bec_server.scan_server.scan_hook_broadcaster import ScanHookBroadcaster
21
25
  from bec_server.scan_server.scans.scan_status import ScanStatus
22
26
 
23
27
  if TYPE_CHECKING:
@@ -67,6 +71,84 @@ class ScanActions:
67
71
  self._metadata_suffix = ""
68
72
  self._queued_device_locks: set[str] = set()
69
73
  self._scan_running = False
74
+ self._scan_hook_stack: list[str] = []
75
+ self._step_started_at: float | None = None
76
+ self._hook_broadcaster = ScanHookBroadcaster(self._connector)
77
+
78
+ @contextmanager
79
+ def _scan_hook_context(self, hook: str) -> Iterator[None]:
80
+ """
81
+ Broadcast lifecycle hooks, reporting per-point execution as scan_core. The start, end and
82
+ failure of a top-level step are sent at once, so no step or error is lost to coalescing.
83
+ A step ended by a user or alarm interruption (finish now, abort, halt) is reported as
84
+ interrupted, any other exception as an error.
85
+ """
86
+ if hook == "at_each_point":
87
+ hook = "scan_core"
88
+ if self._scan_hook_stack and self._scan_hook_stack[-1] == hook:
89
+ yield
90
+ return
91
+ is_step = not self._scan_hook_stack
92
+ if is_step and self._hook_broadcaster.active:
93
+ self._step_started_at = time.time()
94
+ self._scan_hook_stack.append(hook)
95
+ try:
96
+ self._broadcast_scan_hook(flush=is_step)
97
+ yield
98
+ except UserScanInterruption:
99
+ self._broadcast_scan_hook(status="interrupted", flush=is_step)
100
+ raise
101
+ except BaseException:
102
+ self._broadcast_scan_hook(status="error", flush=is_step)
103
+ raise
104
+ else:
105
+ self._broadcast_scan_hook(status="completed", flush=is_step)
106
+ finally:
107
+ self._scan_hook_stack.pop()
108
+ if self._scan_hook_stack:
109
+ self._broadcast_scan_hook()
110
+ else:
111
+ self._step_started_at = None
112
+
113
+ def _broadcast_scan_hook(
114
+ self,
115
+ instructions: list[messages.DeviceInstructionMessage] | None = None,
116
+ status: Literal["running", "completed", "interrupted", "error"] = "running",
117
+ waiting: bool = False,
118
+ wait_started_at: float | None = None,
119
+ flush: bool = False,
120
+ ) -> None:
121
+ """
122
+ Publish the current hook with the scan's original correlation metadata.
123
+
124
+ Args:
125
+ flush (bool): Send at once instead of at the end of the 0.1 second window.
126
+ """
127
+ scan_info = self._scan.scan_info
128
+ if scan_info.scan_id is None:
129
+ return
130
+ self._hook_broadcaster.add(
131
+ messages.ScanHookMessage(
132
+ scan_id=scan_info.scan_id,
133
+ hook=self._scan_hook_stack[-1] if self._scan_hook_stack else None,
134
+ status=status,
135
+ instructions=instructions or [],
136
+ waiting=waiting,
137
+ wait_started_at=wait_started_at,
138
+ hook_started_at=self._step_started_at,
139
+ metadata={**scan_info.metadata, "scan_id": scan_info.scan_id},
140
+ )
141
+ )
142
+ if flush:
143
+ self._hook_broadcaster.flush_now()
144
+
145
+ def _on_status_wait(self, status: ScanStatus) -> None:
146
+ """Report pending instructions; the next lifecycle or instruction update replaces the wait."""
147
+ self._broadcast_scan_hook(
148
+ instructions=status.pending_instructions,
149
+ waiting=True,
150
+ wait_started_at=status.wait_started_at,
151
+ )
70
152
 
71
153
  @property
72
154
  def readout_priority(self) -> dict:
@@ -649,10 +731,13 @@ class ScanActions:
649
731
  metadata={"device_instr_id": status._device_instr_id},
650
732
  )
651
733
  self._send(instr)
652
- if min_wait is not None:
653
- self._shutdown_event.wait(min_wait)
654
- if wait:
655
- status.wait()
734
+ # The exposure delay blocks execution even if the trigger has already completed.
735
+ # pylint: disable=protected-access
736
+ with status._wait_context(waiting=bool(min_wait and min_wait > 0)):
737
+ if min_wait is not None:
738
+ self._shutdown_event.wait(min_wait)
739
+ if wait:
740
+ status.wait()
656
741
  return status
657
742
 
658
743
  @requires_scan_is_running
@@ -1045,6 +1130,7 @@ class ScanActions:
1045
1130
  registry=self._status_registry,
1046
1131
  is_container=is_container,
1047
1132
  name=name,
1133
+ wait_callback=self._on_status_wait,
1048
1134
  )
1049
1135
  self._status_registry[status._device_instr_id] = status
1050
1136
  return status
@@ -1076,7 +1162,11 @@ class ScanActions:
1076
1162
  instr_devices = msg.device if isinstance(msg.device, list) else [msg.device]
1077
1163
  if set(instr_devices) & self._devices_with_required_response:
1078
1164
  msg.metadata["response"] = True
1165
+ status = self._status_registry.get(msg.metadata.get("device_instr_id"))
1166
+ if status is not None:
1167
+ status.instruction = msg
1079
1168
  self._connector.send(MessageEndpoints.device_instructions(), msg)
1169
+ self._broadcast_scan_hook(instructions=[msg])
1080
1170
 
1081
1171
  def _get_message_metadata(self) -> dict:
1082
1172
  metadata = {}
@@ -101,8 +101,8 @@ def scan_hook(func):
101
101
  in the appropriate order.
102
102
  """
103
103
 
104
- @wraps(func)
105
- def wrapper(self, *args, **kwargs):
104
+ # pylint: disable=protected-access
105
+ def dispatch(self, *args, **kwargs):
106
106
  if func.__name__ not in self._scan_modifier_hooks:
107
107
  return func(self, *args, **kwargs)
108
108
 
@@ -131,6 +131,11 @@ def scan_hook(func):
131
131
 
132
132
  return
133
133
 
134
+ @wraps(func)
135
+ def wrapper(self, *args, **kwargs):
136
+ with self.actions._scan_hook_context(func.__name__):
137
+ return dispatch(self, *args, **kwargs)
138
+
134
139
  # pylint: disable=protected-access
135
140
  wrapper._scan_hook_info = {"method_name": func.__name__} # type: ignore
136
141
  wrapper._scan_hook_original = func # type: ignore[attr-defined]