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.
- {bec_server-3.151.2 → bec_server-3.152.0}/PKG-INFO +1 -1
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/device_server.py +5 -2
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/device_serializer.py +60 -1
- bec_server-3.152.0/bec_server/scan_server/device_lock_registry.py +193 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/direct_scan_worker.py +18 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_assembler.py +22 -11
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_queue.py +36 -5
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_server.py +2 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/legacy_scans.py +2 -1
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/move.py +2 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_actions.py +261 -10
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_base.py +16 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/updated_move.py +2 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/scan_fixtures.py +6 -1
- {bec_server-3.151.2 → bec_server-3.152.0}/pyproject.toml +1 -1
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_serializer.py +67 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_server.py +12 -2
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_scan_actions.py +294 -6
- bec_server-3.152.0/tests/tests_scan_server/test_device_lock_registry.py +158 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_direct_scan_worker.py +38 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_assembler.py +35 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_queue.py +184 -2
- {bec_server-3.151.2 → bec_server-3.152.0}/.gitignore +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/.zed/debug.json +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/README.md +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/actor.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/builtin_actor_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/scan_interlock.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/actors/worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/service_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/subprocess_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/bec_server_utils/tmux_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_server.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_service.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/dap_service_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/data_processing/lmfit1d_service.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/bec_message_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/config_update_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/devicemanager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/friendly_device_exceptions.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/rpc_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/tests/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/tests/utils.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/async_writer.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/default_writer.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/file_writer.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/file_writer_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer/merged_dicts.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer_plugins/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/file_writer_plugins/cSAXS.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.bec_linux_base +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.requirements +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/Containerfile.worker +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/_dev_runner.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/builtin_procedures.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/constants.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/container_utils.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/container_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/oop_worker_base.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/procedure_registry.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/protocol.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/subprocess_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/procedures/worker_base.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/bec_emitter.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/emitter.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_bundler/scan_bundler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/beamline_state_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/errors.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/generator_scan_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/instruction_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/path_optimization.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_guard.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_gui_models.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_plugins/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_plugins/otf_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_stubs.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scan_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/acquire.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/cont_line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/fermat_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/grid_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/hexagonal_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/line_sweep_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/list_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/log_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/multi_region_grid_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/multi_region_line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/position_generators.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/round_roi_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/round_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_argument_modifier.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_components.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/scan_modifier.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/scans/time_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/fixtures.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/scan_hook_tests.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/tests/utils.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scan_server/types.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_active_client_emitter.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_connector.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_forwarder.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/atlas_metadata_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/atlas/config_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/cli/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/cli/launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/scihub.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/service_handler/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/scihub/service_handler/service_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/actor_test_utils.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/bec_server/test/helpers.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_actors/test_actors.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_main.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_service_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_bec_server_utils/test_tmux_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_cli_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_server.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_dap_service_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_data_processing/test_lmfit1d_service.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_config_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_instructions.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_manager_ds.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_device_server_cli_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_device_server/test_rpc_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_async_file_writer.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer_cli_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_file_writer/test_file_writer_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_bec_emitter.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_scan_bundler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_bundler/test_scan_bundler_cli_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/__init__.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_acquire.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_cont_line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_fermat_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_grid_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_hexagonal_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_line_sweep_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_list_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_log_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_move_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_multi_region_grid_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_multi_region_line_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_position_generators.py +0 -0
- {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
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_round_roi_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_round_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_scan_components.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_time_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/scans_v4/test_updated_move_scan.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_beamline_state_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_builtin_actor_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_container_utils.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_generator_scan_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_instruction_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_legacy_scans.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_path_optimization.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_procedure_container_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_procedures.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_argument_modifier.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_guard.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_gui_models.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_interlock.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_modifier.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_cli_launch.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_server_scan_manager.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_stub_status.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_stubs.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scan_server/test_scan_worker.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/conftest.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_active_client_emitter.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_config_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_connector.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_forwarder.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_atlas_metadata_handler.py +0 -0
- {bec_server-3.151.2 → bec_server-3.152.0}/tests/tests_scihub/test_scihub_cli_launch.py +0 -0
|
@@ -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
|
|
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
|
|
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
|
|
{bec_server-3.151.2 → bec_server-3.152.0}/bec_server/device_server/devices/device_serializer.py
RENAMED
|
@@ -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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|