bec-server 4.0.0__tar.gz → 4.0.2__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-4.0.0 → bec_server-4.0.2}/PKG-INFO +1 -1
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/async_writer.py +83 -29
- {bec_server-4.0.0 → bec_server-4.0.2}/pyproject.toml +1 -1
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_file_writer/test_async_file_writer.py +234 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/.gitignore +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/.zed/debug.json +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/README.md +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/actor.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/builtin_actor_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/scan_interlock.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/actors/worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/bec_server_utils/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/bec_server_utils/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/bec_server_utils/service_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/bec_server_utils/subprocess_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/bec_server_utils/tmux_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/dap_server.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/dap_service.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/dap_service_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/data_processing/lmfit1d_service.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/bec_message_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/device_server.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/devices/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/devices/config_update_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/devices/device_serializer.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/devices/devicemanager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/friendly_device_exceptions.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/ophyd_callback_monitor.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/rpc_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/tests/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/tests/utils.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/default_writer.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/file_writer.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/file_writer_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/file_writer/merged_dicts.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/Containerfile.bec_linux_base +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/Containerfile.requirements +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/Containerfile.worker +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/_dev_runner.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/builtin_procedures.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/constants.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/container_utils.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/container_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/oop_worker_base.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/procedure_registry.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/protocol.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/subprocess_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/procedures/worker_base.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/bec_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_bundler/scan_bundler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/beamline_state_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/device_lock_registry.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/direct_scan_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/errors.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/instruction_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_assembler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_guard.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_queue.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_server.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scan_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/acquire.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/cont_line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/device_rpc.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/fermat_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/grid_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/hexagonal_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/line_sweep_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/list_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/log_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/move.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/multi_region_grid_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/multi_region_line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/path_optimization.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/position_generators.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/round_roi_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/round_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_actions.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_argument_modifier.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_base.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_components.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_modifier.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/scan_status.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/time_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/scans/updated_move.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/tests/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/tests/fixtures.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/tests/scan_fixtures.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/tests/scan_hook_tests.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scan_server/tests/utils.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/atlas_active_client_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/atlas_connector.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/atlas_forwarder.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/atlas_metadata_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/atlas_plugin_repo_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/atlas/config_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/cli/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/cli/launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/scihub.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/service_handler/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/scihub/service_handler/service_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/test/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/test/actor_test_utils.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/bec_server/test/helpers.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_actors/test_actors.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_bec_server_utils/test_main.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_bec_server_utils/test_service_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_bec_server_utils/test_subprocess_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_bec_server_utils/test_tmux_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_data_processing/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_data_processing/test_dap_cli_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_data_processing/test_dap_server.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_data_processing/test_dap_service_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_data_processing/test_lmfit1d_service.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_config_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_device_instructions.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_device_manager_ds.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_device_serializer.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_device_server.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_device_server_cli_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_ophyd_callback_monitor.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_device_server/test_rpc_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_file_writer/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_file_writer/test_file_writer.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_file_writer/test_file_writer_cli_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_file_writer/test_file_writer_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_bundler/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_bundler/test_bec_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_bundler/test_scan_bundler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_bundler/test_scan_bundler_cli_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/__init__.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_acquire.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_cont_line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_device_rpc_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_fermat_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_grid_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_hexagonal_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_line_sweep_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_list_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_log_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_move_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_multi_region_grid_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_multi_region_line_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_position_generators.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_relative_scan_return_to_start.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_round_roi_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_round_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_scan_actions.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_scan_components.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_scan_status.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_time_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/scans/test_updated_move_scan.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_beamline_state_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_builtin_actor_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_container_utils.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_device_lock_registry.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_direct_scan_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_instruction_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_path_optimization.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_procedure_container_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_procedures.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_argument_modifier.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_assembler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_fixture_imports.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_guard.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_interlock.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_modifier.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_server_cli_launch.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_server_queue.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_server_scan_manager.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scan_server/test_scan_worker.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/conftest.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_active_client_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_config_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_connector.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_forwarder.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_metadata_handler.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_atlas_plugin_repo_emitter.py +0 -0
- {bec_server-4.0.0 → bec_server-4.0.2}/tests/tests_scihub/test_scihub_cli_launch.py +0 -0
|
@@ -65,7 +65,9 @@ class AsyncWriter(threading.Thread):
|
|
|
65
65
|
"index": int
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
"index" is
|
|
68
|
+
"index" is required for 'add_slice' and must be nonnegative. It selects the row whose
|
|
69
|
+
contents are extended and allows revisiting earlier rows. Use 'add' to append new rows
|
|
70
|
+
along the first axis; 'add' ignores "index".
|
|
69
71
|
"max_shape" is required for the 'add' and 'add_slice' types and specifies the maximum shape of the dataset. If the dataset is 1D, 'max_shape' should be [None].
|
|
70
72
|
|
|
71
73
|
"""
|
|
@@ -301,7 +303,12 @@ class AsyncWriter(threading.Thread):
|
|
|
301
303
|
if key == "value":
|
|
302
304
|
self.write_value_data(signal_group, value, async_update)
|
|
303
305
|
elif key == "timestamp":
|
|
304
|
-
self.write_timestamp_data(
|
|
306
|
+
self.write_timestamp_data(
|
|
307
|
+
signal_group,
|
|
308
|
+
value,
|
|
309
|
+
replace=isinstance(async_update, dict)
|
|
310
|
+
and async_update.get("type") == "replace",
|
|
311
|
+
)
|
|
305
312
|
else: # pragma: no cover
|
|
306
313
|
# this should never happen as the keys are fixed in the pydantic model
|
|
307
314
|
msg = f"Unknown key: {key}. Data will not be written."
|
|
@@ -352,7 +359,7 @@ class AsyncWriter(threading.Thread):
|
|
|
352
359
|
|
|
353
360
|
elif update_type == "replace":
|
|
354
361
|
# store the data to be written after the scan is complete
|
|
355
|
-
self.device_data_replace[signal_group.name] = value
|
|
362
|
+
self.device_data_replace[signal_group.name] = np.asarray(value)
|
|
356
363
|
else:
|
|
357
364
|
msg = f"Unknown async update type: {update_type}. Data will not be written."
|
|
358
365
|
self.connector.raise_alarm(
|
|
@@ -444,7 +451,11 @@ class AsyncWriter(threading.Thread):
|
|
|
444
451
|
|
|
445
452
|
if len(max_shape) != 2:
|
|
446
453
|
# We currently only support 2D datasets for the 'add_slice' async update type
|
|
447
|
-
msg =
|
|
454
|
+
msg = (
|
|
455
|
+
f"Invalid max_shape for signal group {signal_group.name} async update type "
|
|
456
|
+
f"'add_slice': {max_shape}. max_shape must have exactly two dimensions. "
|
|
457
|
+
"Data will not be written."
|
|
458
|
+
)
|
|
448
459
|
self.connector.raise_alarm(
|
|
449
460
|
severity=Alarms.WARNING,
|
|
450
461
|
info=messages.ErrorInfo(
|
|
@@ -474,43 +485,41 @@ class AsyncWriter(threading.Thread):
|
|
|
474
485
|
# if max_shape contains more than one None, we have to use a vlen_dtype
|
|
475
486
|
num_undefined = sum(1 for i in max_shape if i is None)
|
|
476
487
|
row_index = async_update["index"]
|
|
488
|
+
if row_index < 0 or (max_shape[0] is not None and row_index >= max_shape[0]):
|
|
489
|
+
msg = (
|
|
490
|
+
f"Invalid row index {row_index} for signal group {signal_group.name}: "
|
|
491
|
+
"'add_slice' updates require a nonnegative row index "
|
|
492
|
+
f"within max_shape {max_shape}. "
|
|
493
|
+
"Data will not be written."
|
|
494
|
+
)
|
|
495
|
+
self.connector.raise_alarm(
|
|
496
|
+
severity=Alarms.WARNING,
|
|
497
|
+
info=messages.ErrorInfo(
|
|
498
|
+
error_message=msg,
|
|
499
|
+
compact_error_message=msg,
|
|
500
|
+
exception_type="ValueError",
|
|
501
|
+
device=signal_group.name,
|
|
502
|
+
),
|
|
503
|
+
metadata={"scan_id": self.scan_id, "scan_number": self.scan_number},
|
|
504
|
+
)
|
|
505
|
+
return
|
|
477
506
|
|
|
478
507
|
if "value" not in signal_group:
|
|
479
508
|
if num_undefined > 1:
|
|
480
|
-
row_index = 0
|
|
481
|
-
shape = (1,) if value.ndim < len(max_shape) else (value.shape[0],)
|
|
482
509
|
signal_group.create_dataset(
|
|
483
510
|
"value",
|
|
484
|
-
shape=
|
|
511
|
+
shape=(row_index + 1,),
|
|
485
512
|
maxshape=(None,),
|
|
486
513
|
dtype=h5py.vlen_dtype(np.dtype(value[0].dtype)),
|
|
487
514
|
)
|
|
488
515
|
signal_group["value"][row_index] = value
|
|
489
516
|
else:
|
|
490
|
-
|
|
491
|
-
value = value.reshape((1,) + value.shape)
|
|
492
|
-
if value.shape[1] > max_shape[1]:
|
|
493
|
-
msg = f"Data for {signal_group.name} exceeds the defined max_shape {max_shape}. Data will be truncated."
|
|
494
|
-
self.connector.raise_alarm(
|
|
495
|
-
severity=Alarms.WARNING,
|
|
496
|
-
info=messages.ErrorInfo(
|
|
497
|
-
error_message=msg,
|
|
498
|
-
compact_error_message=msg,
|
|
499
|
-
exception_type="ValueError",
|
|
500
|
-
device=signal_group.name,
|
|
501
|
-
),
|
|
502
|
-
metadata={"scan_id": self.scan_id, "scan_number": self.scan_number},
|
|
503
|
-
)
|
|
504
|
-
value = value[:, : max_shape[1]]
|
|
505
|
-
create_dataset_safe(signal_group, "value", data=value, maxshape=max_shape)
|
|
506
|
-
self.cursor[signal_group.name][row_index] = value.shape[1]
|
|
517
|
+
self._write_initial_fixed_slice(signal_group, value, max_shape, row_index)
|
|
507
518
|
return
|
|
508
519
|
|
|
509
520
|
# add a slice to the already existing dataset
|
|
510
521
|
if num_undefined > 1:
|
|
511
522
|
max_index = signal_group["value"].shape[0]
|
|
512
|
-
if row_index == -1:
|
|
513
|
-
row_index = max_index + 1
|
|
514
523
|
if row_index >= max_index:
|
|
515
524
|
signal_group["value"].resize((row_index + 1,))
|
|
516
525
|
signal_group["value"][row_index] = value
|
|
@@ -532,23 +541,68 @@ class AsyncWriter(threading.Thread):
|
|
|
532
541
|
metadata={"scan_id": self.scan_id, "scan_number": self.scan_number},
|
|
533
542
|
)
|
|
534
543
|
value = value[: max_shape[1] - col_index]
|
|
535
|
-
signal_group["value"].resize(
|
|
544
|
+
signal_group["value"].resize(
|
|
545
|
+
(max(signal_group["value"].shape[0], row_index + 1), max_shape[1])
|
|
546
|
+
)
|
|
536
547
|
signal_group["value"][row_index, col_index : col_index + len(value)] = value
|
|
537
548
|
self.cursor[signal_group.name][row_index] = col_index + len(value)
|
|
538
549
|
|
|
539
|
-
def
|
|
550
|
+
def _write_initial_fixed_slice(
|
|
551
|
+
self,
|
|
552
|
+
signal_group: h5py.Group,
|
|
553
|
+
value: np.ndarray,
|
|
554
|
+
max_shape: list[int | None],
|
|
555
|
+
row_index: int,
|
|
556
|
+
) -> None:
|
|
557
|
+
"""Create a fixed-width slice dataset at the validated initial row index."""
|
|
558
|
+
value = value.reshape((1,) + value.shape)
|
|
559
|
+
if value.shape[1] > max_shape[1]:
|
|
560
|
+
msg = (
|
|
561
|
+
f"Data for {signal_group.name} exceeds the defined max_shape {max_shape}. "
|
|
562
|
+
"Data will be truncated."
|
|
563
|
+
)
|
|
564
|
+
self.connector.raise_alarm(
|
|
565
|
+
severity=Alarms.WARNING,
|
|
566
|
+
info=messages.ErrorInfo(
|
|
567
|
+
error_message=msg,
|
|
568
|
+
compact_error_message=msg,
|
|
569
|
+
exception_type="ValueError",
|
|
570
|
+
device=signal_group.name,
|
|
571
|
+
),
|
|
572
|
+
metadata={"scan_id": self.scan_id, "scan_number": self.scan_number},
|
|
573
|
+
)
|
|
574
|
+
value = value[:, : max_shape[1]]
|
|
575
|
+
dataset = create_dataset_safe(signal_group, "value", data=value, maxshape=max_shape)
|
|
576
|
+
if row_index > 0:
|
|
577
|
+
# Keep dtype inference from the data and let HDF5 fill the skipped rows.
|
|
578
|
+
dataset.resize((0, value.shape[1]))
|
|
579
|
+
dataset.resize((row_index + 1, value.shape[1]))
|
|
580
|
+
if value.size:
|
|
581
|
+
dataset.write_direct(
|
|
582
|
+
np.ascontiguousarray(value), dest_sel=np.s_[row_index : row_index + 1, :]
|
|
583
|
+
)
|
|
584
|
+
self.cursor[signal_group.name][row_index] = value.shape[1]
|
|
585
|
+
|
|
586
|
+
def write_timestamp_data(
|
|
587
|
+
self, signal_group: h5py.Group, value: Any, replace: bool = False
|
|
588
|
+
) -> None:
|
|
540
589
|
"""
|
|
541
590
|
Write the timestamp data to the file.
|
|
542
591
|
Timestamp data is always written as a 1D array, irrespective of the async update type.
|
|
592
|
+
Replacement updates retain only the latest timestamps.
|
|
543
593
|
|
|
544
594
|
Args:
|
|
545
595
|
signal_group (h5py.Group): The group to write the data to
|
|
546
|
-
value (
|
|
596
|
+
value (Any): The timestamp data to write
|
|
597
|
+
replace (bool): Whether to replace existing timestamps instead of appending
|
|
547
598
|
"""
|
|
548
599
|
if not isinstance(value, (list, np.ndarray)):
|
|
549
600
|
value = [value]
|
|
550
601
|
if "timestamp" not in signal_group:
|
|
551
602
|
create_dataset_safe(signal_group, "timestamp", data=value, maxshape=(None,))
|
|
603
|
+
elif replace:
|
|
604
|
+
signal_group["timestamp"].resize((len(value),))
|
|
605
|
+
signal_group["timestamp"][:] = value
|
|
552
606
|
else:
|
|
553
607
|
signal_group["timestamp"].resize((len(signal_group["timestamp"]) + len(value),))
|
|
554
608
|
signal_group["timestamp"][-len(value) :] = value
|
|
@@ -210,6 +210,37 @@ def test_async_writer_add_slice_var_size(async_writer, data):
|
|
|
210
210
|
assert out[1].shape == (10,)
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
@pytest.mark.parametrize("initial_row", [1, 3])
|
|
214
|
+
@pytest.mark.parametrize("dtype", [np.int16, np.float64])
|
|
215
|
+
def test_async_writer_add_slice_var_size_starts_at_requested_row(
|
|
216
|
+
async_writer, tmp_path, initial_row, dtype
|
|
217
|
+
):
|
|
218
|
+
"""A sparse first vlen row retains its data when earlier rows and more slices arrive."""
|
|
219
|
+
with h5py.File(tmp_path / "initial_vlen_row.h5", "w") as file:
|
|
220
|
+
signal_group = file.create_group("signal")
|
|
221
|
+
async_writer.write_value_data(
|
|
222
|
+
signal_group,
|
|
223
|
+
np.array([7, 8], dtype=dtype),
|
|
224
|
+
{"type": "add_slice", "index": initial_row, "max_shape": [None, None]},
|
|
225
|
+
)
|
|
226
|
+
dataset = signal_group["value"]
|
|
227
|
+
assert dataset.shape == (initial_row + 1,)
|
|
228
|
+
assert h5py.check_vlen_dtype(dataset.dtype) == np.dtype(dtype)
|
|
229
|
+
assert all(row.size == 0 for row in dataset[:initial_row])
|
|
230
|
+
np.testing.assert_array_equal(dataset[initial_row], [7, 8])
|
|
231
|
+
|
|
232
|
+
for row_index, values in [(0, [1, 2]), (initial_row, [9]), (0, [3])]:
|
|
233
|
+
async_writer.write_value_data(
|
|
234
|
+
signal_group,
|
|
235
|
+
np.array(values, dtype=dtype),
|
|
236
|
+
{"type": "add_slice", "index": row_index, "max_shape": [None, None]},
|
|
237
|
+
)
|
|
238
|
+
assert dataset.shape == (initial_row + 1,)
|
|
239
|
+
np.testing.assert_array_equal(dataset[0], [1, 2, 3])
|
|
240
|
+
np.testing.assert_array_equal(dataset[initial_row], [7, 8, 9])
|
|
241
|
+
assert all(row.size == 0 for row in dataset[1:initial_row])
|
|
242
|
+
|
|
243
|
+
|
|
213
244
|
def test_async_writer_add_slice_var_size_2D_data_warns(async_writer):
|
|
214
245
|
"""
|
|
215
246
|
Test that adding a slice with 2D data when max_shape is [None, None] raises a warning and skips writing the data.
|
|
@@ -271,6 +302,153 @@ def test_async_writer_add_slice_fixed_size(async_writer, data):
|
|
|
271
302
|
assert out.shape == (2, 20)
|
|
272
303
|
|
|
273
304
|
|
|
305
|
+
@pytest.mark.parametrize("max_rows", [None, 4])
|
|
306
|
+
@pytest.mark.parametrize("later_row", [1, 3])
|
|
307
|
+
@pytest.mark.parametrize("earlier_slice", [[3], [3, 4, 5]])
|
|
308
|
+
def test_async_writer_add_slice_fixed_size_preserves_later_rows(
|
|
309
|
+
async_writer, tmp_path, max_rows, later_row, earlier_slice
|
|
310
|
+
):
|
|
311
|
+
"""Interleaved slices preserve rows and append positions, including after truncation."""
|
|
312
|
+
with h5py.File(tmp_path / "interleaved.h5", "w") as file:
|
|
313
|
+
signal_group = file.create_group("signal")
|
|
314
|
+
with mock.patch.object(async_writer.connector, "raise_alarm") as raise_alarm:
|
|
315
|
+
for row_index, values in [(0, [1, 2]), (later_row, [10, 11]), (0, earlier_slice)]:
|
|
316
|
+
async_writer.write_value_data(
|
|
317
|
+
signal_group,
|
|
318
|
+
values,
|
|
319
|
+
{"type": "add_slice", "index": row_index, "max_shape": [max_rows, 4]},
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
expected = np.zeros((later_row + 1, 4), dtype=int)
|
|
323
|
+
row_values = [1, 2] + earlier_slice[:2]
|
|
324
|
+
expected[0, : len(row_values)] = row_values
|
|
325
|
+
expected[later_row, :2] = [10, 11]
|
|
326
|
+
np.testing.assert_array_equal(signal_group["value"][:], expected)
|
|
327
|
+
assert raise_alarm.call_count == (len(earlier_slice) > 2)
|
|
328
|
+
|
|
329
|
+
async_writer.write_value_data(
|
|
330
|
+
signal_group,
|
|
331
|
+
[12, 13],
|
|
332
|
+
{"type": "add_slice", "index": later_row, "max_shape": [max_rows, 4]},
|
|
333
|
+
)
|
|
334
|
+
expected[later_row] = [10, 11, 12, 13]
|
|
335
|
+
np.testing.assert_array_equal(signal_group["value"][:], expected)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@pytest.mark.parametrize("max_rows", [None, 4])
|
|
339
|
+
@pytest.mark.parametrize("initial_values", [[7, 8], [7, 8, 9, 10, 11], []])
|
|
340
|
+
@pytest.mark.parametrize("strided", [False, True])
|
|
341
|
+
def test_async_writer_add_slice_fixed_size_starts_at_requested_row(
|
|
342
|
+
async_writer, tmp_path, max_rows, initial_values, strided
|
|
343
|
+
):
|
|
344
|
+
"""A nonzero first row keeps its data and cursor when earlier rows arrive later."""
|
|
345
|
+
with h5py.File(tmp_path / "initial_row.h5", "w") as file:
|
|
346
|
+
signal_group = file.create_group("signal")
|
|
347
|
+
values = np.asarray(initial_values, dtype=np.int16)
|
|
348
|
+
if strided:
|
|
349
|
+
values = np.repeat(values, 2)[::2]
|
|
350
|
+
async_writer.write_value_data(
|
|
351
|
+
signal_group, values, {"type": "add_slice", "index": 3, "max_shape": [max_rows, 4]}
|
|
352
|
+
)
|
|
353
|
+
expected_initial = np.zeros((4, min(len(initial_values), 4)), dtype=np.int16)
|
|
354
|
+
expected_initial[3] = initial_values[:4]
|
|
355
|
+
np.testing.assert_array_equal(signal_group["value"][:], expected_initial)
|
|
356
|
+
assert signal_group["value"].dtype == np.dtype(np.int16)
|
|
357
|
+
|
|
358
|
+
for row_index, values in [(0, [1, 2]), (3, [12, 13])]:
|
|
359
|
+
async_writer.write_value_data(
|
|
360
|
+
signal_group,
|
|
361
|
+
values,
|
|
362
|
+
{"type": "add_slice", "index": row_index, "max_shape": [max_rows, 4]},
|
|
363
|
+
)
|
|
364
|
+
expected = np.zeros((4, 4), dtype=np.int16)
|
|
365
|
+
expected[0, :2] = [1, 2]
|
|
366
|
+
row_values = (initial_values + [12, 13])[:4]
|
|
367
|
+
expected[3, : len(row_values)] = row_values
|
|
368
|
+
np.testing.assert_array_equal(signal_group["value"][:], expected)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def test_async_writer_add_slice_fixed_size_nonzero_first_row_preserves_string_dtype(
|
|
372
|
+
async_writer, tmp_path
|
|
373
|
+
):
|
|
374
|
+
"""Moving the initial row preserves HDF5's inference of variable-length strings."""
|
|
375
|
+
with h5py.File(tmp_path / "initial_string_row.h5", "w") as file:
|
|
376
|
+
signal_group = file.create_group("signal")
|
|
377
|
+
async_writer.write_value_data(
|
|
378
|
+
signal_group,
|
|
379
|
+
np.array(["a", "bb"], dtype=object),
|
|
380
|
+
{"type": "add_slice", "index": 2, "max_shape": [None, 4]},
|
|
381
|
+
)
|
|
382
|
+
dataset = signal_group["value"]
|
|
383
|
+
np.testing.assert_array_equal(dataset.asstr()[:], [["", ""], ["", ""], ["a", "bb"]])
|
|
384
|
+
assert h5py.check_string_dtype(dataset.dtype).length is None
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
@pytest.mark.parametrize("cells", [[[1, 2], [3]], [[1, 2], [3, 4]], [[1, 2]], [[], []], [[]]])
|
|
388
|
+
def test_async_writer_add_slice_fixed_size_nonzero_first_row_preserves_vlen_dtype(
|
|
389
|
+
async_writer, tmp_path, cells
|
|
390
|
+
):
|
|
391
|
+
"""HDF5 initializes skipped rows correctly for variable-length numeric values."""
|
|
392
|
+
values = np.empty(len(cells), dtype=h5py.vlen_dtype(np.dtype(np.int32)))
|
|
393
|
+
for index, cell in enumerate(cells):
|
|
394
|
+
values[index] = np.array(cell, dtype=np.int32)
|
|
395
|
+
with h5py.File(tmp_path / "initial_vlen_row.h5", "w") as file:
|
|
396
|
+
signal_group = file.create_group("signal")
|
|
397
|
+
async_writer.write_value_data(
|
|
398
|
+
signal_group, values, {"type": "add_slice", "index": 2, "max_shape": [None, 4]}
|
|
399
|
+
)
|
|
400
|
+
dataset = signal_group["value"]
|
|
401
|
+
assert dataset.shape == (3, len(cells))
|
|
402
|
+
assert h5py.check_vlen_dtype(dataset.dtype) == np.dtype(np.int32)
|
|
403
|
+
assert all(cell.size == 0 for row in dataset[:2] for cell in row)
|
|
404
|
+
for index, value in enumerate(values):
|
|
405
|
+
np.testing.assert_array_equal(dataset[2, index], value)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@pytest.mark.parametrize("populated", [False, True])
|
|
409
|
+
@pytest.mark.parametrize("index, max_rows, row_width", [(-1, None, 4), (4, 4, 4), (-1, None, None)])
|
|
410
|
+
def test_async_writer_add_slice_rejects_invalid_index(
|
|
411
|
+
async_writer, tmp_path, populated, index, max_rows, row_width
|
|
412
|
+
):
|
|
413
|
+
"""An invalid row index cannot overwrite existing rows or initialize an invalid cursor."""
|
|
414
|
+
with h5py.File(tmp_path / "invalid_index.h5", "w") as file:
|
|
415
|
+
signal_group = file.create_group("signal")
|
|
416
|
+
if populated:
|
|
417
|
+
for row_index, values in [(0, [1, 2]), (1, [10, 11])]:
|
|
418
|
+
async_writer.write_value_data(
|
|
419
|
+
signal_group,
|
|
420
|
+
values,
|
|
421
|
+
{"type": "add_slice", "index": row_index, "max_shape": [max_rows, row_width]},
|
|
422
|
+
)
|
|
423
|
+
expected = signal_group["value"][:]
|
|
424
|
+
cursor_before = dict(async_writer.cursor.get(signal_group.name, {}))
|
|
425
|
+
|
|
426
|
+
with mock.patch.object(async_writer.connector, "raise_alarm") as raise_alarm:
|
|
427
|
+
async_writer.write_value_data(
|
|
428
|
+
signal_group,
|
|
429
|
+
[99],
|
|
430
|
+
{"type": "add_slice", "index": index, "max_shape": [max_rows, row_width]},
|
|
431
|
+
)
|
|
432
|
+
raise_alarm.assert_called_once()
|
|
433
|
+
assert raise_alarm.call_args.kwargs["severity"] == Alarms.WARNING
|
|
434
|
+
assert "nonnegative row index" in raise_alarm.call_args.kwargs["info"].error_message
|
|
435
|
+
assert async_writer.cursor.get(signal_group.name, {}) == cursor_before
|
|
436
|
+
if populated:
|
|
437
|
+
assert signal_group["value"].shape == expected.shape
|
|
438
|
+
for actual_row, expected_row in zip(signal_group["value"][:], expected):
|
|
439
|
+
np.testing.assert_array_equal(actual_row, expected_row)
|
|
440
|
+
else:
|
|
441
|
+
assert "value" not in signal_group
|
|
442
|
+
|
|
443
|
+
async_writer.write_value_data(
|
|
444
|
+
signal_group, [3], {"type": "add_slice", "index": 0, "max_shape": [max_rows, row_width]}
|
|
445
|
+
)
|
|
446
|
+
expected_row = [1, 2, 3] if populated else [3]
|
|
447
|
+
if populated and row_width is not None:
|
|
448
|
+
expected_row.append(0)
|
|
449
|
+
np.testing.assert_array_equal(signal_group["value"][0], expected_row)
|
|
450
|
+
|
|
451
|
+
|
|
274
452
|
def test_async_writer_add_slice_fixed_size_data_consistency(async_writer):
|
|
275
453
|
endpoint = MessageEndpoints.device_async_readback("scan_id", "monitor_async")
|
|
276
454
|
data = [
|
|
@@ -413,9 +591,65 @@ def test_async_writer_replace(async_writer, data):
|
|
|
413
591
|
# read the data back
|
|
414
592
|
with h5py.File(async_writer.file_path, "r") as f:
|
|
415
593
|
out = f[async_writer.BASE_PATH]["monitor_async"]["monitor_async"]["value"][:]
|
|
594
|
+
timestamps = f[async_writer.BASE_PATH]["monitor_async"]["monitor_async"]["timestamp"][:]
|
|
416
595
|
|
|
417
596
|
assert out.shape == (10,)
|
|
418
597
|
assert np.allclose(out, data[-1].signals["monitor_async"]["value"])
|
|
598
|
+
assert np.array_equal(timestamps, [data[-1].signals["monitor_async"]["timestamp"]])
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
@pytest.mark.parametrize(
|
|
602
|
+
"values",
|
|
603
|
+
[
|
|
604
|
+
(0, 7),
|
|
605
|
+
(0.0, 1.2),
|
|
606
|
+
(False, True),
|
|
607
|
+
(np.int64(0), np.int64(7)),
|
|
608
|
+
(np.float64(0.0), np.float64(1.2)),
|
|
609
|
+
],
|
|
610
|
+
)
|
|
611
|
+
def test_async_writer_replace_scalar(async_writer, values):
|
|
612
|
+
"""Write only the latest scalar value and timestamp after scan completion."""
|
|
613
|
+
for timestamp, value in enumerate(values, start=1):
|
|
614
|
+
message = messages.DeviceMessage(
|
|
615
|
+
signals={"monitor_async": {"value": value, "timestamp": float(timestamp)}},
|
|
616
|
+
metadata={"async_update": {"type": "replace"}},
|
|
617
|
+
)
|
|
618
|
+
async_writer.write_data({"monitor_async": [message]})
|
|
619
|
+
|
|
620
|
+
group = async_writer.file_handle[f"{async_writer.BASE_PATH}/monitor_async/monitor_async"]
|
|
621
|
+
try:
|
|
622
|
+
assert "value" not in group
|
|
623
|
+
async_writer.write_data({}, write_replace=True)
|
|
624
|
+
assert group["value"].shape == ()
|
|
625
|
+
assert group["value"][()] == values[-1]
|
|
626
|
+
assert np.array_equal(group["timestamp"][:], [2.0])
|
|
627
|
+
finally:
|
|
628
|
+
async_writer.file_handle.close()
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
@pytest.mark.parametrize("update_type", ["replace", "add", "add_slice"])
|
|
632
|
+
@pytest.mark.parametrize("timestamps", [[1, 2, 3], [1]])
|
|
633
|
+
def test_async_writer_timestamp_aggregation(async_writer, update_type, timestamps):
|
|
634
|
+
"""Keep replacement timestamps together and preserve append behavior for other updates."""
|
|
635
|
+
metadata = {"type": update_type}
|
|
636
|
+
if update_type == "add":
|
|
637
|
+
metadata["max_shape"] = [None]
|
|
638
|
+
elif update_type == "add_slice":
|
|
639
|
+
metadata.update(max_shape=[None, None], index=0)
|
|
640
|
+
|
|
641
|
+
for timestamp in (timestamps, [4, 5]):
|
|
642
|
+
message = messages.DeviceMessage(
|
|
643
|
+
signals={"monitor_async": {"value": np.array([6, 7]), "timestamp": timestamp}},
|
|
644
|
+
metadata={"async_update": metadata},
|
|
645
|
+
)
|
|
646
|
+
async_writer.write_data({"monitor_async": [message]})
|
|
647
|
+
async_writer.write_data({}, write_replace=True)
|
|
648
|
+
|
|
649
|
+
group = async_writer.file_handle[f"{async_writer.BASE_PATH}/monitor_async/monitor_async"]
|
|
650
|
+
expected = [4, 5] if update_type == "replace" else timestamps + [4, 5]
|
|
651
|
+
assert np.array_equal(group["timestamp"][:], expected)
|
|
652
|
+
async_writer.file_handle.close()
|
|
419
653
|
|
|
420
654
|
|
|
421
655
|
def test_async_writer_async_signal(async_writer):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/devices/config_update_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bec_server-4.0.0 → bec_server-4.0.2}/bec_server/device_server/friendly_device_exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|