bec-server 3.148.0__tar.gz → 3.149.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.148.0 → bec_server-3.149.0}/PKG-INFO +1 -1
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/path_optimization.py +65 -20
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_assembler.py +4 -2
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_queue.py +4 -2
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/fermat_scan.py +13 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/legacy_scans.py +11 -20
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/position_generators.py +6 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/scan_actions.py +72 -36
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/scan_base.py +2 -2
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/scan_components.py +20 -25
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/tests/scan_fixtures.py +8 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/pyproject.toml +2 -1
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_fermat_scan.py +51 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_scan_actions.py +126 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_scan_components.py +44 -6
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_path_optimization.py +17 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_server_queue.py +59 -2
- {bec_server-3.148.0 → bec_server-3.149.0}/.gitignore +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/.zed/debug.json +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/README.md +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/actor.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/builtin_actor_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/scan_interlock.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/actors/worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/bec_server_utils/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/bec_server_utils/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/bec_server_utils/service_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/bec_server_utils/subprocess_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/bec_server_utils/tmux_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/dap_server.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/dap_service.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/dap_service_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/image_analysis_service.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/data_processing/lmfit1d_service.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/bec_message_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/device_server.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/devices/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/devices/config_update_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/devices/device_serializer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/devices/devicemanager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/friendly_device_exceptions.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/rpc_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/tests/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/device_server/tests/utils.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/async_writer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/default_writer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/file_writer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/file_writer_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer/merged_dicts.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer_plugins/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/file_writer_plugins/cSAXS.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/Containerfile.bec_linux_base +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/Containerfile.requirements +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/Containerfile.worker +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/_dev_runner.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/builtin_procedures.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/constants.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/container_utils.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/container_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/oop_worker_base.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/procedure_registry.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/protocol.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/subprocess_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/procedures/worker_base.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/bec_emitter.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/emitter.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_bundler/scan_bundler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/beamline_state_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/direct_scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/errors.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/generator_scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/instruction_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_guard.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_gui_models.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_plugins/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_plugins/otf_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_server.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_stubs.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/acquire.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/cont_line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/grid_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/hexagonal_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/line_sweep_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/list_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/log_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/move.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/multi_region_grid_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/multi_region_line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/round_roi_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/round_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/scan_argument_modifier.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/scan_modifier.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/time_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/updated_move.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/tests/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/tests/fixtures.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/tests/scan_hook_tests.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/tests/utils.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/types.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/atlas_active_client_emitter.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/atlas_connector.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/atlas_forwarder.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/atlas_metadata_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/atlas/config_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/cli/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/cli/launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/scihub.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/service_handler/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scihub/service_handler/service_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/test/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/test/actor_test_utils.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/bec_server/test/helpers.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_actors/test_actors.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_bec_server_utils/test_main.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_bec_server_utils/test_service_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_bec_server_utils/test_tmux_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/test_dap_cli_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/test_dap_server.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/test_dap_service_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/test_image_analysis_service.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_data_processing/test_lmfit1d_service.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_config_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_device_instructions.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_device_manager_ds.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_device_serializer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_device_server.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_device_server_cli_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_device_server/test_rpc_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_file_writer/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_file_writer/test_async_file_writer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_file_writer/test_file_writer.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_file_writer/test_file_writer_cli_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_file_writer/test_file_writer_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_bundler/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_bundler/test_bec_emitter.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_bundler/test_scan_bundler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_bundler/test_scan_bundler_cli_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/__init__.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_acquire.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_cont_line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_grid_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_hexagonal_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_line_sweep_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_list_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_log_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_move_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_multi_region_grid_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_multi_region_line_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_position_generators.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_relative_scan_return_to_start.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_round_roi_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_round_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_time_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/scans_v4/test_updated_move_scan.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_beamline_state_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_builtin_actor_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_container_utils.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_direct_scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_generator_scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_instruction_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_legacy_scans.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_procedure_container_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_procedures.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_argument_modifier.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_assembler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_guard.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_gui_models.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_interlock.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_modifier.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_server_cli_launch.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_server_scan_manager.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_stub_status.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_stubs.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scan_server/test_scan_worker.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/conftest.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_atlas_active_client_emitter.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_atlas_config_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_atlas_connector.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_atlas_forwarder.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_atlas_metadata_handler.py +0 -0
- {bec_server-3.148.0 → bec_server-3.149.0}/tests/tests_scihub/test_scihub_cli_launch.py +0 -0
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
from time import perf_counter
|
|
1
2
|
from typing import Literal
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
from pydantic import BaseModel
|
|
5
6
|
from scipy.spatial import cKDTree # type: ignore
|
|
6
7
|
|
|
8
|
+
from bec_lib.logger import bec_logger
|
|
9
|
+
from bec_server.scan_server.scans.position_generators import Direction
|
|
10
|
+
|
|
11
|
+
logger = bec_logger.logger
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
class PathQualityStats(BaseModel):
|
|
9
15
|
max_jump: float
|
|
@@ -15,6 +21,19 @@ class PathQualityStats(BaseModel):
|
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
class PathOptimizerMixin:
|
|
24
|
+
def _log_optimization_result(
|
|
25
|
+
self, method: str, original_path: np.ndarray, optimized_path: np.ndarray, duration_s: float
|
|
26
|
+
) -> None:
|
|
27
|
+
original_length = self.get_path_length(original_path)
|
|
28
|
+
optimized_length = self.get_path_length(optimized_path)
|
|
29
|
+
reduced_by = original_length - optimized_length
|
|
30
|
+
reduction_pct = (reduced_by / original_length * 100) if original_length > 0 else 0.0
|
|
31
|
+
|
|
32
|
+
logger.info(
|
|
33
|
+
f"Path optimizer ({method}) reduced path length from {original_length:.4f} to {optimized_length:.4f} "
|
|
34
|
+
f"({reduced_by:.4f} reduction, {reduction_pct:.2f}% decrease) in {duration_s:.4f} seconds."
|
|
35
|
+
)
|
|
36
|
+
|
|
18
37
|
def get_radius(self, pos: np.ndarray) -> np.ndarray:
|
|
19
38
|
"""
|
|
20
39
|
Calculate the radial distance from the origin for each position
|
|
@@ -31,9 +50,10 @@ class PathOptimizerMixin:
|
|
|
31
50
|
self,
|
|
32
51
|
positions: np.ndarray,
|
|
33
52
|
corridor_size: float | None = None,
|
|
34
|
-
|
|
53
|
+
fast_axis: Literal[0, 1] = 0,
|
|
35
54
|
num_iterations: int = 1,
|
|
36
|
-
|
|
55
|
+
first_corridor_direction: Direction | Literal[-1, 1] = Direction.ASCENDING,
|
|
56
|
+
snaked: bool = True,
|
|
37
57
|
corridor_estimation: Literal["density", "median_distance"] = "median_distance",
|
|
38
58
|
):
|
|
39
59
|
"""
|
|
@@ -45,12 +65,15 @@ class PathOptimizerMixin:
|
|
|
45
65
|
Args:
|
|
46
66
|
positions (np.ndarray): Array of positions
|
|
47
67
|
corridor_size (float, optional): Width of each corridor. Defaults to None (auto-estimated).
|
|
48
|
-
|
|
68
|
+
fast_axis (Literal[0, 1], optional): Axis traversed within each corridor (0 or 1). Defaults to 0.
|
|
49
69
|
num_iterations (int, optional): Number of corridor sizes to try. Defaults to 1.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
70
|
+
first_corridor_direction (Direction | Literal[-1, 1], optional): Traversal direction of the first
|
|
71
|
+
corridor along the fast axis. Positive means ascending, negative means
|
|
72
|
+
descending. Defaults to Direction.ASCENDING.
|
|
73
|
+
snaked (bool, optional): If True, alternate the traversal direction between
|
|
74
|
+
successive corridors. If False, keep the same direction in every corridor.
|
|
75
|
+
corridor_estimation (Literal["density", "median_distance"], optional): Method for estimating corridor size if not provided.
|
|
76
|
+
Options are "density" or "median_distance". Defaults to "median_distance".
|
|
54
77
|
|
|
55
78
|
Returns:
|
|
56
79
|
np.ndarray: Optimized positions
|
|
@@ -58,6 +81,9 @@ class PathOptimizerMixin:
|
|
|
58
81
|
if positions.ndim != 2 or positions.shape[1] < 2:
|
|
59
82
|
return positions
|
|
60
83
|
|
|
84
|
+
start_time = perf_counter()
|
|
85
|
+
original_path = positions
|
|
86
|
+
|
|
61
87
|
# Estimate corridor size if needed
|
|
62
88
|
if corridor_size is None:
|
|
63
89
|
if corridor_estimation == "density":
|
|
@@ -70,8 +96,14 @@ class PathOptimizerMixin:
|
|
|
70
96
|
'Choose "density" or "median_distance".'
|
|
71
97
|
)
|
|
72
98
|
|
|
73
|
-
|
|
74
|
-
|
|
99
|
+
if not isinstance(first_corridor_direction, Direction):
|
|
100
|
+
first_corridor_direction = Direction(first_corridor_direction)
|
|
101
|
+
|
|
102
|
+
if fast_axis not in (0, 1):
|
|
103
|
+
raise ValueError("fast_axis must be 0 or 1")
|
|
104
|
+
|
|
105
|
+
slow_axis = int(not fast_axis)
|
|
106
|
+
axis_vals = positions[:, slow_axis]
|
|
75
107
|
|
|
76
108
|
best_length = np.inf
|
|
77
109
|
best_path = positions
|
|
@@ -97,17 +129,15 @@ class PathOptimizerMixin:
|
|
|
97
129
|
if block.size == 0:
|
|
98
130
|
continue
|
|
99
131
|
|
|
100
|
-
# Sort within corridor along
|
|
101
|
-
block = block[np.argsort(positions[block,
|
|
132
|
+
# Sort within corridor along fast axis
|
|
133
|
+
block = block[np.argsort(positions[block, fast_axis])]
|
|
102
134
|
|
|
103
135
|
# Direction handling
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if step % 2 == 0:
|
|
110
|
-
block = block[::-1]
|
|
136
|
+
direction = first_corridor_direction
|
|
137
|
+
if snaked and step % 2 == 1:
|
|
138
|
+
direction = Direction(-direction)
|
|
139
|
+
if direction == Direction.DESCENDING:
|
|
140
|
+
block = block[::-1]
|
|
111
141
|
|
|
112
142
|
index_sorted.append(block)
|
|
113
143
|
|
|
@@ -122,6 +152,9 @@ class PathOptimizerMixin:
|
|
|
122
152
|
best_length = path_length
|
|
123
153
|
best_path = path
|
|
124
154
|
|
|
155
|
+
self._log_optimization_result(
|
|
156
|
+
"corridor", original_path, best_path, perf_counter() - start_time
|
|
157
|
+
)
|
|
125
158
|
return best_path
|
|
126
159
|
|
|
127
160
|
def _corridor_estimate_density(self, positions: np.ndarray) -> float:
|
|
@@ -182,6 +215,9 @@ class PathOptimizerMixin:
|
|
|
182
215
|
if pos.ndim != 2 or pos.shape[1] < 2 or len(pos) < 2:
|
|
183
216
|
return pos
|
|
184
217
|
|
|
218
|
+
start_time = perf_counter()
|
|
219
|
+
original_path = pos
|
|
220
|
+
|
|
185
221
|
# Calculate radii once
|
|
186
222
|
radii = self.get_radius(pos)
|
|
187
223
|
max_rad = np.max(radii)
|
|
@@ -227,7 +263,9 @@ class PathOptimizerMixin:
|
|
|
227
263
|
best_score = score
|
|
228
264
|
best_path = optimized_path
|
|
229
265
|
|
|
230
|
-
|
|
266
|
+
result = best_path if best_path is not None else pos
|
|
267
|
+
self._log_optimization_result("shell", original_path, result, perf_counter() - start_time)
|
|
268
|
+
return result
|
|
231
269
|
|
|
232
270
|
def _estimate_shell_width(self, pos: np.ndarray, radii: np.ndarray, max_rad: float) -> float:
|
|
233
271
|
"""
|
|
@@ -413,6 +451,9 @@ class PathOptimizerMixin:
|
|
|
413
451
|
if len(positions) <= 1:
|
|
414
452
|
return positions
|
|
415
453
|
|
|
454
|
+
start_time = perf_counter()
|
|
455
|
+
original_path = positions
|
|
456
|
+
|
|
416
457
|
n_points = len(positions)
|
|
417
458
|
|
|
418
459
|
# Use a copy to avoid modifying the original
|
|
@@ -455,4 +496,8 @@ class PathOptimizerMixin:
|
|
|
455
496
|
path.append(current_pos)
|
|
456
497
|
remaining_indices.remove(original_idx)
|
|
457
498
|
|
|
458
|
-
|
|
499
|
+
result = np.array(path)
|
|
500
|
+
self._log_optimization_result(
|
|
501
|
+
"nearest_neighbor", original_path, result, perf_counter() - start_time
|
|
502
|
+
)
|
|
503
|
+
return result
|
|
@@ -97,13 +97,15 @@ class ScanAssembler:
|
|
|
97
97
|
)
|
|
98
98
|
return scan_instance
|
|
99
99
|
|
|
100
|
-
def assemble_direct_scan(
|
|
100
|
+
def assemble_direct_scan(
|
|
101
|
+
self, msg: messages.ScanQueueMessage, scan_id: str | None
|
|
102
|
+
) -> ScanBaseV4:
|
|
101
103
|
"""Assemble the device instructions for a given ScanQueueMessage.
|
|
102
104
|
This will be achieved by calling the specified class (must be a derived class of ScanBaseV4)
|
|
103
105
|
|
|
104
106
|
Args:
|
|
105
107
|
msg (messages.ScanQueueMessage): scan queue message for which the instruction should be assembled
|
|
106
|
-
scan_id (str): scan id of the scan
|
|
108
|
+
scan_id (str | None): scan id of the scan
|
|
107
109
|
|
|
108
110
|
Raises:
|
|
109
111
|
ScanAbortion: Raised if the scan initialization fails.
|
|
@@ -1594,7 +1594,9 @@ class DirectInstructionQueueItem:
|
|
|
1594
1594
|
Args:
|
|
1595
1595
|
msg (ScanQueueMessage): the scan queue message containing the scan information
|
|
1596
1596
|
"""
|
|
1597
|
-
|
|
1597
|
+
scan_cls = self.assembler.scan_manager.scan_dict[msg.scan_type]
|
|
1598
|
+
scan_id = self._scan_id if getattr(scan_cls, "is_scan", True) else None
|
|
1599
|
+
scan = self.assembler.assemble_direct_scan(msg, scan_id=scan_id)
|
|
1598
1600
|
self.scans.append(scan)
|
|
1599
1601
|
self.scan_msgs.append(msg)
|
|
1600
1602
|
|
|
@@ -1718,7 +1720,7 @@ class DirectInstructionQueueItem:
|
|
|
1718
1720
|
def _set_scan_as_active(self, scan: ScanBase_v4):
|
|
1719
1721
|
"""set a given scan as the active scan"""
|
|
1720
1722
|
self.active_scan = scan
|
|
1721
|
-
if scan.scan_info.scan_number is None:
|
|
1723
|
+
if scan.scan_info.scan_number is None and scan.is_scan:
|
|
1722
1724
|
with self.parent.queue_manager._lock:
|
|
1723
1725
|
self.parent.queue_manager.parent.scan_number += 1
|
|
1724
1726
|
if not self.scan_msgs[self.scans.index(scan)].metadata.get("dataset_id_on_hold"):
|
|
@@ -24,6 +24,7 @@ from bec_lib.device import DeviceBase
|
|
|
24
24
|
from bec_lib.logger import bec_logger
|
|
25
25
|
from bec_lib.scan_args import DefaultArgType, ScanArgument, Units
|
|
26
26
|
from bec_server.scan_server.scans import position_generators
|
|
27
|
+
from bec_server.scan_server.scans.position_generators import Direction
|
|
27
28
|
from bec_server.scan_server.scans.scan_base import ScanBase, ScanType
|
|
28
29
|
from bec_server.scan_server.scans.scan_modifier import scan_hook
|
|
29
30
|
|
|
@@ -173,6 +174,18 @@ class FermatSpiralScan(ScanBase):
|
|
|
173
174
|
self.start_positions = self.components.get_start_positions(self.motors)
|
|
174
175
|
self.positions += self.start_positions
|
|
175
176
|
|
|
177
|
+
if self.optim_trajectory:
|
|
178
|
+
self.positions = self.components.optimize_trajectory(
|
|
179
|
+
self.positions,
|
|
180
|
+
optimization_type=self.optim_trajectory,
|
|
181
|
+
fast_axis=0,
|
|
182
|
+
first_direction=(
|
|
183
|
+
Direction.ASCENDING
|
|
184
|
+
if self.motor1_start_stop[1] > self.motor1_start_stop[0]
|
|
185
|
+
else Direction.DESCENDING
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
|
|
176
189
|
self.components.check_limits(self.motors, self.positions)
|
|
177
190
|
|
|
178
191
|
self.update_scan_info(
|
|
@@ -19,7 +19,7 @@ from bec_lib.logger import bec_logger
|
|
|
19
19
|
from bec_server.scan_server.instruction_handler import InstructionHandler
|
|
20
20
|
|
|
21
21
|
from ..errors import LimitError, ScanAbortion
|
|
22
|
-
from ..path_optimization import PathOptimizerMixin
|
|
22
|
+
from ..path_optimization import Direction, PathOptimizerMixin
|
|
23
23
|
from ..scan_stubs import ScanStubs
|
|
24
24
|
|
|
25
25
|
logger = bec_logger.logger
|
|
@@ -519,26 +519,14 @@ class ScanBase(RequestBase, PathOptimizerMixin):
|
|
|
519
519
|
if not self.optim_trajectory:
|
|
520
520
|
return
|
|
521
521
|
|
|
522
|
-
# Get preferred directions from scan parameters if available
|
|
523
|
-
preferred_directions = getattr(self, "preferred_directions", None)
|
|
524
|
-
|
|
525
522
|
if self.optim_trajectory == "corridor":
|
|
526
|
-
#
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
)
|
|
534
|
-
self.positions = self.optimize_corridor(
|
|
535
|
-
self.positions,
|
|
536
|
-
num_iterations=5,
|
|
537
|
-
sort_axis=primary_axis,
|
|
538
|
-
preferred_direction=preferred_direction,
|
|
539
|
-
)
|
|
540
|
-
else:
|
|
541
|
-
self.positions = self.optimize_corridor(self.positions, num_iterations=5)
|
|
523
|
+
# Corridor traversal direction follows the first pass along the axis within each corridor.
|
|
524
|
+
self.positions = self.optimize_corridor(
|
|
525
|
+
self.positions,
|
|
526
|
+
num_iterations=5,
|
|
527
|
+
fast_axis=getattr(self, "fast_axis", 0),
|
|
528
|
+
first_corridor_direction=getattr(self, "first_direction", 1),
|
|
529
|
+
)
|
|
542
530
|
return
|
|
543
531
|
|
|
544
532
|
if self.optim_trajectory == "shell":
|
|
@@ -1102,6 +1090,9 @@ class FermatSpiralScan(ScanBase):
|
|
|
1102
1090
|
self.stop_motor2 = stop_motor2
|
|
1103
1091
|
self.step = step
|
|
1104
1092
|
self.spiral_type = spiral_type
|
|
1093
|
+
self.first_direction = (
|
|
1094
|
+
Direction.ASCENDING if self.stop_motor1 > self.start_motor1 else Direction.DESCENDING
|
|
1095
|
+
)
|
|
1105
1096
|
|
|
1106
1097
|
def update_scan_motors(self):
|
|
1107
1098
|
self.scan_motors = [self.motor1, self.motor2]
|
{bec_server-3.148.0 → bec_server-3.149.0}/bec_server/scan_server/scans/position_generators.py
RENAMED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import enum
|
|
3
4
|
from collections.abc import Iterator, Sequence
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
class Direction(int, enum.Enum):
|
|
10
|
+
ASCENDING = 1
|
|
11
|
+
DESCENDING = -1
|
|
12
|
+
|
|
13
|
+
|
|
8
14
|
def rotate_points(
|
|
9
15
|
points: np.ndarray, angle: float, center: tuple[float, float] | None = None
|
|
10
16
|
) -> np.ndarray:
|
|
@@ -153,15 +153,7 @@ class ScanActions:
|
|
|
153
153
|
|
|
154
154
|
# We support str and DeviceBase inputs as well as lists of those.
|
|
155
155
|
# We convert them to a list of device names for easier processing.
|
|
156
|
-
|
|
157
|
-
device_names = []
|
|
158
|
-
for dev in device:
|
|
159
|
-
if isinstance(dev, DeviceBase):
|
|
160
|
-
device_names.append(dev.name)
|
|
161
|
-
else:
|
|
162
|
-
device_names.append(dev)
|
|
163
|
-
else:
|
|
164
|
-
device_names = [device.name if isinstance(device, DeviceBase) else device]
|
|
156
|
+
device_names = self._normalize_device_names(device)
|
|
165
157
|
if len(device_names) == 1:
|
|
166
158
|
device_names = device_names[0]
|
|
167
159
|
status = self._create_status(name=status_name or f"stage_{device_names}")
|
|
@@ -290,7 +282,7 @@ class ScanActions:
|
|
|
290
282
|
|
|
291
283
|
status = self._create_status(is_container=True, name="set")
|
|
292
284
|
for dev, val in zip(devices, values, strict=False):
|
|
293
|
-
device_name =
|
|
285
|
+
device_name = self._normalize_device_name(dev)
|
|
294
286
|
sub_status = self._create_status(name=f"set_{device_name}")
|
|
295
287
|
instr = messages.DeviceInstructionMessage(
|
|
296
288
|
device=device_name,
|
|
@@ -320,7 +312,7 @@ class ScanActions:
|
|
|
320
312
|
Returns:
|
|
321
313
|
ScanStubStatus: status object to track the kickoff process
|
|
322
314
|
"""
|
|
323
|
-
device_name =
|
|
315
|
+
device_name = self._normalize_device_name(device)
|
|
324
316
|
status = self._create_status(name=f"kickoff_{device_name}")
|
|
325
317
|
|
|
326
318
|
instr = messages.DeviceInstructionMessage(
|
|
@@ -347,7 +339,7 @@ class ScanActions:
|
|
|
347
339
|
Returns:
|
|
348
340
|
ScanStubStatus: status object to track the completion process
|
|
349
341
|
"""
|
|
350
|
-
device_name =
|
|
342
|
+
device_name = self._normalize_device_name(device)
|
|
351
343
|
status = self._create_status(name=f"complete_{device_name}")
|
|
352
344
|
|
|
353
345
|
instr = messages.DeviceInstructionMessage(
|
|
@@ -620,7 +612,7 @@ class ScanActions:
|
|
|
620
612
|
Returns:
|
|
621
613
|
ScanStubStatus: status object to track the unstaging process
|
|
622
614
|
"""
|
|
623
|
-
device_name =
|
|
615
|
+
device_name = self._normalize_device_name(device)
|
|
624
616
|
status = self._create_status(name=f"unstage_{device_name}")
|
|
625
617
|
|
|
626
618
|
instr = messages.DeviceInstructionMessage(
|
|
@@ -687,7 +679,7 @@ class ScanActions:
|
|
|
687
679
|
request_id (str, optional): request ID to associate the readback instruction with. If None, the scan's RID will be used. Defaults to None.
|
|
688
680
|
"""
|
|
689
681
|
request_id = request_id or self._scan.scan_info.metadata["RID"]
|
|
690
|
-
device_names =
|
|
682
|
+
device_names = self._normalize_device_names(devices)
|
|
691
683
|
scan_report_instruction = {
|
|
692
684
|
"readback": {"RID": request_id, "devices": device_names, "start": start, "end": stop}
|
|
693
685
|
}
|
|
@@ -705,10 +697,7 @@ class ScanActions:
|
|
|
705
697
|
Args:
|
|
706
698
|
device (str | DeviceBase): name of the device or DeviceBase instance to report
|
|
707
699
|
"""
|
|
708
|
-
|
|
709
|
-
device_name = device.name
|
|
710
|
-
else:
|
|
711
|
-
device_name = device
|
|
700
|
+
device_name = self._normalize_device_name(device)
|
|
712
701
|
scan_report_instruction = {"device_progress": [device_name]}
|
|
713
702
|
self._scan.scan_info.scan_report_instructions.append(scan_report_instruction)
|
|
714
703
|
if self._update_queue_info_callback is not None:
|
|
@@ -762,10 +751,7 @@ class ScanActions:
|
|
|
762
751
|
devices = [devices]
|
|
763
752
|
|
|
764
753
|
for device in devices:
|
|
765
|
-
|
|
766
|
-
device_name = device.name
|
|
767
|
-
else:
|
|
768
|
-
device_name = device
|
|
754
|
+
device_name = self._normalize_device_name(device)
|
|
769
755
|
self._scan.scan_info.readout_priority_modification[priority].append(device_name)
|
|
770
756
|
|
|
771
757
|
def close_scan(self):
|
|
@@ -834,13 +820,15 @@ class ScanActions:
|
|
|
834
820
|
"""
|
|
835
821
|
if isinstance(device, list):
|
|
836
822
|
for dev in device:
|
|
837
|
-
device_name =
|
|
823
|
+
device_name = self._normalize_device_name(dev)
|
|
838
824
|
self._devices_with_required_response.add(device_name)
|
|
839
825
|
else:
|
|
840
|
-
device_name =
|
|
826
|
+
device_name = self._normalize_device_name(device)
|
|
841
827
|
self._devices_with_required_response.add(device_name)
|
|
842
828
|
|
|
843
|
-
def rpc_call(
|
|
829
|
+
def rpc_call(
|
|
830
|
+
self, device: str | DeviceBase, func_name: str, *args, **kwargs
|
|
831
|
+
) -> Any | ScanStubStatus:
|
|
844
832
|
"""
|
|
845
833
|
Make an RPC call to a device. This will call the given function on the device with the given arguments.
|
|
846
834
|
The device server will execute the function and return the result in the instruction response.
|
|
@@ -864,26 +852,49 @@ class ScanActions:
|
|
|
864
852
|
Any | ScanStubStatus: The result of the RPC call or a ScanStubStatus object if the result is a status object.
|
|
865
853
|
|
|
866
854
|
"""
|
|
867
|
-
status = self._create_status(name=f"rpc_{device}_{func_name}")
|
|
868
855
|
rpc_id = str(uuid.uuid4())
|
|
856
|
+
status = self.rpc_call_no_wait(device, func_name, rpc_id, *args, **kwargs)
|
|
857
|
+
status.wait(resolve_on_known_type=True)
|
|
858
|
+
if status._result_is_status:
|
|
859
|
+
return status
|
|
860
|
+
return status.result
|
|
861
|
+
|
|
862
|
+
def rpc_call_no_wait(
|
|
863
|
+
self, device: str | DeviceBase, func_name: str, rpc_id: str, *args, **kwargs
|
|
864
|
+
) -> ScanStubStatus:
|
|
865
|
+
"""
|
|
866
|
+
Make an RPC call to a device without waiting for the result. This will call the given function on the device with the given arguments.
|
|
867
|
+
The device server will execute the function and return the result in the instruction response.
|
|
868
|
+
This method is a low-level interface to call arbitrary functions on the device server and should be used with caution.
|
|
869
|
+
|
|
870
|
+
Args:
|
|
871
|
+
device (str | DeviceBase): name of the device or device instance to call the function on
|
|
872
|
+
func_name (str): name of the function to call on the device
|
|
873
|
+
rpc_id (str): unique identifier for the RPC call, used to match the response with the request
|
|
874
|
+
*args: positional arguments to pass to the function
|
|
875
|
+
**kwargs: keyword arguments to pass to the function
|
|
876
|
+
|
|
877
|
+
Returns:
|
|
878
|
+
ScanStubStatus: A ScanStubStatus object that can be used to wait for the result of the RPC call.
|
|
879
|
+
"""
|
|
880
|
+
device_name = self._normalize_device_name(device)
|
|
881
|
+
status = self._create_status(name=f"rpc_{device_name}_{func_name}")
|
|
882
|
+
|
|
869
883
|
parameter = {
|
|
870
|
-
"device":
|
|
884
|
+
"device": device_name,
|
|
871
885
|
"func": func_name,
|
|
872
886
|
"rpc_id": rpc_id,
|
|
873
887
|
"args": args,
|
|
874
888
|
"kwargs": kwargs,
|
|
875
889
|
}
|
|
876
890
|
msg = messages.DeviceInstructionMessage(
|
|
877
|
-
device=
|
|
891
|
+
device=device_name,
|
|
878
892
|
action="rpc",
|
|
879
893
|
parameter=parameter,
|
|
880
894
|
metadata={"device_instr_id": status._device_instr_id},
|
|
881
895
|
)
|
|
882
896
|
self._send(msg)
|
|
883
|
-
status
|
|
884
|
-
if status._result_is_status:
|
|
885
|
-
return status
|
|
886
|
-
return status.result
|
|
897
|
+
return status
|
|
887
898
|
|
|
888
899
|
def send_client_info(self, message: str):
|
|
889
900
|
"""
|
|
@@ -959,12 +970,37 @@ class ScanActions:
|
|
|
959
970
|
metadata[key] = value + self._metadata_suffix
|
|
960
971
|
return metadata
|
|
961
972
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
973
|
+
@staticmethod
|
|
974
|
+
def _normalize_device_name(device: str | DeviceBase) -> str:
|
|
975
|
+
"""
|
|
976
|
+
Normalize the device name to a string. If the device is a DeviceBase instance, return its dotted name.
|
|
977
|
+
We use the dotted name because it is uniquely identifying a device in the device tree, even for
|
|
978
|
+
sub-devices.
|
|
979
|
+
|
|
980
|
+
Args:
|
|
981
|
+
device (str | DeviceBase): device name or DeviceBase instance
|
|
982
|
+
|
|
983
|
+
Returns:
|
|
984
|
+
str: normalized device name
|
|
985
|
+
"""
|
|
986
|
+
if isinstance(device, DeviceBase):
|
|
987
|
+
return getattr(device, "dotted_name", None) or device.name
|
|
988
|
+
return device
|
|
989
|
+
|
|
990
|
+
@staticmethod
|
|
991
|
+
def _normalize_device_names(devices: str | DeviceBase | list[str | DeviceBase]) -> list[str]:
|
|
992
|
+
"""
|
|
993
|
+
Normalize a list of device names to a list of strings. If the devices are DeviceBase instances, return their dotted names.
|
|
994
|
+
|
|
995
|
+
Args:
|
|
996
|
+
devices (str | DeviceBase | list[str | DeviceBase]): device name(s) or DeviceBase instance(s)
|
|
997
|
+
|
|
998
|
+
Returns:
|
|
999
|
+
list[str]: list of normalized device names
|
|
1000
|
+
"""
|
|
965
1001
|
if not isinstance(devices, list):
|
|
966
1002
|
devices = [devices]
|
|
967
|
-
return [
|
|
1003
|
+
return [ScanActions._normalize_device_name(dev) for dev in devices]
|
|
968
1004
|
|
|
969
1005
|
def _get_monitored_device_names(self) -> list[str]:
|
|
970
1006
|
monitored_devices = [
|
|
@@ -54,7 +54,7 @@ class ScanInfo(BaseModel):
|
|
|
54
54
|
|
|
55
55
|
# General scan information
|
|
56
56
|
scan_name: Annotated[str, Field(description="Name of the scan type, e.g. 'grid_scan'")]
|
|
57
|
-
scan_id: Annotated[str, Field(description="Unique identifier for the scan")]
|
|
57
|
+
scan_id: Annotated[str | None, Field(description="Unique identifier for the scan")] = None
|
|
58
58
|
scan_type: Annotated[
|
|
59
59
|
ScanType | None,
|
|
60
60
|
Field(
|
|
@@ -167,7 +167,7 @@ class ScanBase(ABC):
|
|
|
167
167
|
|
|
168
168
|
def __init__(
|
|
169
169
|
self,
|
|
170
|
-
scan_id: str,
|
|
170
|
+
scan_id: str | None,
|
|
171
171
|
redis_connector: RedisConnector,
|
|
172
172
|
device_manager: DeviceManager,
|
|
173
173
|
instruction_handler: InstructionHandler,
|
|
@@ -8,6 +8,7 @@ import numpy as np
|
|
|
8
8
|
from bec_lib.device import DeviceBase
|
|
9
9
|
from bec_server.scan_server.errors import LimitError
|
|
10
10
|
from bec_server.scan_server.path_optimization import PathOptimizerMixin
|
|
11
|
+
from bec_server.scan_server.scans.position_generators import Direction
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from bec_lib.devicemanager import DeviceContainer
|
|
@@ -160,49 +161,43 @@ class ScanComponents:
|
|
|
160
161
|
self,
|
|
161
162
|
positions: np.ndarray,
|
|
162
163
|
optimization_type: Literal["corridor", "shell", "nearest"] = "corridor",
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
fast_axis: Literal[0, 1] = 0,
|
|
165
|
+
first_direction: Literal[-1, 1] | Direction = Direction.ASCENDING,
|
|
166
|
+
snaked: bool = True,
|
|
165
167
|
corridor_size: float | None = None,
|
|
166
168
|
num_iterations: int = 5,
|
|
167
169
|
) -> np.ndarray:
|
|
168
170
|
"""
|
|
169
171
|
Optimize the trajectory of the scan by reordering the positions. This can help to minimize the movement time of the motors.
|
|
172
|
+
|
|
173
|
+
Important note: The optimization is only done for 2D scans. For higher-dimensional scans, the positions are not reordered and simply
|
|
174
|
+
returned as they are.
|
|
175
|
+
|
|
170
176
|
The optimization can be done in different ways, depending on the optimization_type parameter:
|
|
171
|
-
- "corridor": optimize the trajectory in a corridor-like way, where the scan
|
|
177
|
+
- "corridor": optimize the trajectory in a corridor-like way, where the scan progresses between corridors along the slow axis and traverses each corridor along the fast axis. This is typically a good choice for grid scans. If first_direction is provided, it sets the traversal direction of the first corridor and snaked controls whether later corridors alternate direction, producing a snaked path along the fast axis.
|
|
172
178
|
- "shell": optimize the trajectory in a shell-like way, where the scan moves in a spiral from the outside to the inside. This is typically a good choice for round scans.
|
|
173
179
|
- "nearest": optimize the trajectory by always moving to the nearest next point. This is typically a good choice for random scans.
|
|
174
180
|
|
|
175
181
|
Args:
|
|
176
182
|
positions (np.ndarray): Array of positions to optimize, shape (num_points, num_motors).
|
|
177
183
|
optimization_type (str, optional): Type of optimization to perform. Defaults to "corridor".
|
|
178
|
-
|
|
179
|
-
|
|
184
|
+
fast_axis (int, optional): Fast axis for corridor optimization. Defaults to 0.
|
|
185
|
+
first_direction (Direction | Literal[-1, 1], optional): Traversal direction for the first corridor along the fast axis. Positive means ascending, negative means descending. Defaults to Direction.ASCENDING.
|
|
186
|
+
snaked (bool, optional): If True, alternate direction between corridors. Defaults to True.
|
|
180
187
|
corridor_size (float | None, optional): Size of the corridor for corridor optimization. Defaults to None, which means the default corridor size will be used.
|
|
181
188
|
Returns:
|
|
182
189
|
np.ndarray: Optimized array of positions, shape (num_points, num_motors).
|
|
183
190
|
"""
|
|
184
191
|
|
|
185
192
|
if optimization_type == "corridor":
|
|
186
|
-
|
|
187
|
-
positions
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
preferred_direction = (
|
|
195
|
-
preferred_directions[primary_axis]
|
|
196
|
-
if len(preferred_directions) > primary_axis
|
|
197
|
-
else None
|
|
198
|
-
)
|
|
199
|
-
positions = self._path_optimizer.optimize_corridor(
|
|
200
|
-
positions,
|
|
201
|
-
num_iterations=num_iterations,
|
|
202
|
-
sort_axis=primary_axis,
|
|
203
|
-
preferred_direction=preferred_direction,
|
|
204
|
-
corridor_size=corridor_size,
|
|
205
|
-
)
|
|
193
|
+
positions = self._path_optimizer.optimize_corridor(
|
|
194
|
+
positions,
|
|
195
|
+
num_iterations=num_iterations,
|
|
196
|
+
corridor_size=corridor_size,
|
|
197
|
+
fast_axis=fast_axis,
|
|
198
|
+
first_corridor_direction=first_direction,
|
|
199
|
+
snaked=snaked,
|
|
200
|
+
)
|
|
206
201
|
|
|
207
202
|
elif optimization_type == "shell":
|
|
208
203
|
positions = self._path_optimizer.optimize_shell(
|
|
@@ -119,6 +119,10 @@ class _MockDevice(DeviceBase):
|
|
|
119
119
|
def full_name(self):
|
|
120
120
|
return self.name
|
|
121
121
|
|
|
122
|
+
@property
|
|
123
|
+
def dotted_name(self):
|
|
124
|
+
return self.name
|
|
125
|
+
|
|
122
126
|
@property
|
|
123
127
|
def limits(self):
|
|
124
128
|
return self._limits
|
|
@@ -222,6 +226,10 @@ class MockCustomDevice(DeviceBase):
|
|
|
222
226
|
def full_name(self):
|
|
223
227
|
return self.name
|
|
224
228
|
|
|
229
|
+
@property
|
|
230
|
+
def dotted_name(self):
|
|
231
|
+
return self.name
|
|
232
|
+
|
|
225
233
|
@property
|
|
226
234
|
def limits(self):
|
|
227
235
|
return self._limits
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bec-server"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.149.0"
|
|
8
8
|
description = "BEC server"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
classifiers = [
|
|
@@ -135,6 +135,7 @@ Homepage = "https://github.com/bec-project/bec"
|
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
|
|
138
|
+
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
[tool.hatch.build.targets.wheel]
|