From 2e4cd258a832793ac963751ccec6e559b0067ea0 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 10 Jun 2026 17:30:30 +0200 Subject: [PATCH] Dev/refactor caller tests master attributes (#1462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactoring * wip * refactored, yet to take out checkers * checker added * wip acquire ctb state * wip acquire ctb state * wip, acq test * acquire moved out * fix * wip, To fix masterattribtues * wip, masterfilecheks * wip, at ctbexpectedstate * wip, fixing acquire * wip, acquire done * wip * compiles * refactoring * minor * minor * reduced unnecessary includes * fix in tests * removed ctb state check * ctb fix with ctb state * fixed ctb guard tests * minor debug * work for xilinx ctb --------- Co-authored-by: Erik Fröjdh --- slsDetectorSoftware/tests/CMakeLists.txt | 6 + .../tests/Caller/test-Caller-acquire.cpp | 517 ++++---- .../tests/Caller/test-Caller-global.cpp | 163 +-- .../tests/Caller/test-Caller-global.h | 80 +- .../Caller/test-Caller-master-attributes.cpp | 1086 +---------------- .../tests/Caller/test-Caller-rx.cpp | 38 +- slsDetectorSoftware/tests/acquire/Acquire.cpp | 47 + slsDetectorSoftware/tests/acquire/Acquire.h | 60 + slsDetectorSoftware/tests/acquire/CTBState.h | 138 +++ .../tests/acquire/ExpectedState.cpp | 355 ++++++ .../tests/acquire/ExpectedState.h | 119 ++ slsDetectorSoftware/tests/acquire/FileState.h | 100 ++ .../tests/checks/MasterFileChecks.h | 456 +++++++ .../tests/master_file/Checker.h | 59 + .../tests/master_file/Context.h | 63 + .../tests/master_file/Readers.h | 30 + .../tests/master_file/ReadersH5.h | 295 +++++ .../tests/master_file/ReadersJson.h | 145 +++ 18 files changed, 2193 insertions(+), 1564 deletions(-) create mode 100644 slsDetectorSoftware/tests/acquire/Acquire.cpp create mode 100644 slsDetectorSoftware/tests/acquire/Acquire.h create mode 100644 slsDetectorSoftware/tests/acquire/CTBState.h create mode 100644 slsDetectorSoftware/tests/acquire/ExpectedState.cpp create mode 100644 slsDetectorSoftware/tests/acquire/ExpectedState.h create mode 100644 slsDetectorSoftware/tests/acquire/FileState.h create mode 100644 slsDetectorSoftware/tests/checks/MasterFileChecks.h create mode 100644 slsDetectorSoftware/tests/master_file/Checker.h create mode 100644 slsDetectorSoftware/tests/master_file/Context.h create mode 100644 slsDetectorSoftware/tests/master_file/Readers.h create mode 100644 slsDetectorSoftware/tests/master_file/ReadersH5.h create mode 100644 slsDetectorSoftware/tests/master_file/ReadersJson.h diff --git a/slsDetectorSoftware/tests/CMakeLists.txt b/slsDetectorSoftware/tests/CMakeLists.txt index d25b35718..e39ce339b 100755 --- a/slsDetectorSoftware/tests/CMakeLists.txt +++ b/slsDetectorSoftware/tests/CMakeLists.txt @@ -5,6 +5,9 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test-SharedMemory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/acquire/Acquire.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/acquire/ExpectedState.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-rx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-rx-running.cpp @@ -18,6 +21,7 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-moench.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-global.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-acquire.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-master-attributes.cpp @@ -44,6 +48,8 @@ target_compile_options(tests target_include_directories(tests PUBLIC + "$" + "$/Caller" "$" "$" "$" diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp index 94e294ab7..8084efd84 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp @@ -2,289 +2,272 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Caller.h" #include "catch.hpp" -#include "sls/Detector.h" -#include "sls/sls_detector_defs.h" -#include "sls/versionAPI.h" -#include "test-Caller-global.h" -#include "tests/globals.h" -#include -#include +#include "acquire/Acquire.h" +#include "acquire/CTBState.h" +#include "acquire/ExpectedState.h" +#include "acquire/FileState.h" +#include "checks/MasterFileChecks.h" namespace sls { -using test::GET; -using test::PUT; +namespace acq = sls::test::acquire; +namespace checks = sls::test::checks; + +void acquire_and_check_file_size( + Detector &det, std::optional ctb_state = std::nullopt) { + auto acq_state = acq::default_acquisition_state(); + acq_state.num_frames = 2; + auto file_state = acq::default_file_state(); + acq::run(det, acq_state, file_state); + auto image_size = acq::get_expected_image_size(det, ctb_state); + REQUIRE_NOTHROW( + checks::check_binary_file_size(image_size, acq_state.num_frames)); +} + +void test_ctb_binary_file_size(Detector &det) { + auto detType = det.getDetectorType().squash(defs::GENERIC); + acq::CTBState ctb_state = acq::default_ctb_state(detType); + // default ctb state for tests + // readout mode = defs::ANALOG_AND_DIGITAL + // analog samples = 5000 + // digital samples = 6000 + // trans samples = 288 + // ten_giga = isAltera ? false : true + // adc enable 1g = 0xFFFFFF00 + // adc enable 1g = 0xFF00FFFF + // dbit offset = 0 + // dbit list = {0, 12, 2, 43} + // dbit reorder = false + // trans mask = 0x3 + + acq::CTBStateGuard ctb_guard(det, std::make_optional(ctb_state)); + + { + ctb_state = acq::default_ctb_state(detType); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::ANALOG_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } +} // disable for jungfrau as it requires higher maximum receive buffer size // sysctl net.core.rmem_max=$((100*1024*1024)) // sysctl net.core.rmem_default=$((100*1024*1024)) -TEST_CASE("jungfrau_or_moench_acquire_check_file_size", +TEST_CASE("acquire_check_binary_file_size", "[.detectorintegration][.disable_check_data_file]") { Detector det; - Caller caller(&det); auto det_type = det.getDetectorType().tsquash("Inconsistent detector types to test"); - if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) { - - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - // if 2 udp interfaces, data split into half - size_t expected_image_size = (par.nChanX * par.nChanY * par.nChipX * - par.nChipY * bytes_per_pixel) / - num_udp_interfaces; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("eiger_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::EIGER) { - - int dynamic_range = det.getDynamicRange().squash(); - if (dynamic_range != 16) { - throw RuntimeError( - "Eiger detector must have dynamic range 16 to test"); - } - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - // data split into half due to 2 udp interfaces per half module - int num_chips = (par.nChipX / 2); - int bytes_per_pixel = (dynamic_range / 8); - size_t expected_image_size = - par.nChanX * par.nChanY * num_chips * bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("mythen3_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::MYTHEN3) { - - int dynamic_range = det.getDynamicRange().squash(); - int counter_mask = det.getCounterMask().squash(); - if (dynamic_range != 16 && counter_mask != 0x3) { - throw RuntimeError("Mythen3 detector must have dynamic range 16 " - "and counter mask 0x3 to test"); - } - int num_counters = __builtin_popcount(counter_mask); - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = dynamic_range / 8; - int num_channels_per_counter = par.nChanX / 3; - size_t expected_image_size = num_channels_per_counter * - num_counters * par.nChipX * - bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("gotthard2_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::GOTTHARD2) { - - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - size_t expected_image_size = - par.nChanX * par.nChipX * bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -void test_ctb_file_size_with_acquire(Detector &det, Caller &caller, - int64_t num_frames, - const testCtbAcquireInfo &test_info, - bool isXilinxCtb) { - - create_files_for_acquire(det, caller, num_frames, test_info); - - // check file size (assuming local pc) - uint64_t expected_image_size = - calculate_ctb_image_size(test_info, isXilinxCtb).first; - testFileInfo test_file_info; - REQUIRE_NOTHROW(test_acquire_binary_file_size(test_file_info, num_frames, - expected_image_size)); -} - -// disable for xilinx_ctb as it requires higher maximum receive buffer size -// sysctl net.core.rmem_max=$((100*1024*1024)) -// sysctl net.core.rmem_default=$((100*1024*1024)) -TEST_CASE("ctb_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); + INFO("Testing acquire and checking binary file size with " + << ToString(det_type)); if (det_type == defs::CHIPTESTBOARD || - det_type == defs::XILINX_CHIPTESTBOARD) { - bool isXilinxCtb = (det_type == defs::XILINX_CHIPTESTBOARD); - int num_frames_to_acquire = 2; - // all the test cases - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::TRANSCEIVER_ONLY; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_ONLY; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - } + det_type == defs::XILINX_CHIPTESTBOARD) + test_ctb_binary_file_size(det); + else + REQUIRE_NOTHROW(acquire_and_check_file_size(det)); } } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp index b3c2d1f00..5b4ba685f 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp @@ -3,13 +3,16 @@ #include "test-Caller-global.h" #include "Caller.h" #include "GeneralData.h" -#include "catch.hpp" #include "sls/Detector.h" #include "sls/logger.h" #include "tests/globals.h" +#include "catch.hpp" + namespace sls { +namespace acq = sls::test::acquire; + using test::GET; using test::PUT; void test_valid_port_caller(const std::string &command, @@ -96,164 +99,10 @@ void test_onchip_dac_caller(defs::dacIndex index, const std::string &dacname, } } -testFileInfo get_file_state(const Detector &det) { - return testFileInfo{ - det.getFilePath().tsquash("Inconsistent file path"), - det.getFileNamePrefix().tsquash("Inconsistent file prefix"), - det.getAcquisitionIndex().tsquash( - "Inconsistent file acquisition index"), - det.getFileWrite().tsquash("Inconsistent file write state"), - det.getFileOverWrite().tsquash("Inconsistent file overwrite state"), - det.getFileFormat().tsquash("Inconsistent file format")}; -} - -void set_file_state(Detector &det, const testFileInfo &file_info) { - if (!file_info.file_path.empty()) - det.setFilePath(file_info.file_path); - det.setFileNamePrefix(file_info.file_prefix); - det.setAcquisitionIndex(file_info.file_acq_index); - det.setFileWrite(file_info.file_write); - det.setFileOverWrite(file_info.file_overwrite); - det.setFileFormat(file_info.file_format); -} - -void test_acquire_binary_file_size(const testFileInfo &file_info, - uint64_t num_frames_to_acquire, - uint64_t expected_image_size) { - assert(file_info.file_format == defs::BINARY); - std::string fname = file_info.file_path + "/" + file_info.file_prefix + - "_d0_f0_" + std::to_string(file_info.file_acq_index) + - ".raw"; - uint64_t expected_file_size = - num_frames_to_acquire * - (expected_image_size + sizeof(defs::sls_receiver_header)); - auto actual_file_size = std::filesystem::file_size(fname); - REQUIRE(actual_file_size == expected_file_size); -} - -void test_acquire_with_receiver(Caller &caller, const Detector &det) { - REQUIRE_NOTHROW(caller.call("rx_start", {}, -1, PUT)); - REQUIRE_NOTHROW(caller.call("start", {}, -1, PUT)); - bool idle = false; - while (!idle) { - std::ostringstream oss; - REQUIRE_NOTHROW(caller.call("status", {}, -1, GET)); - auto statusList = det.getDetectorStatus(); - if (statusList.any(defs::ERROR)) { - throw std::runtime_error("error status while acquiring"); - } - if (statusList.contains_only(defs::IDLE, defs::STOPPED)) { - idle = true; - } - } - REQUIRE_NOTHROW(caller.call("rx_stop", {}, -1, PUT)); -} - -void create_files_for_acquire( - Detector &det, Caller &caller, int64_t num_frames, - const std::optional &test_info) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - auto prev_num_frames = det.getNumberOfFrames().tsquash( - "Inconsistent number of frames to acquire"); - std::optional prev_ctb_config_info{}; - if (test_info) { - prev_ctb_config_info = get_ctb_config_state(det); - } - - // set state for acquire - testFileInfo test_file_info; - set_file_state(det, test_file_info); - det.setNumberOfFrames(num_frames); - if (test_info) { - set_ctb_config_state(det, *test_info); - } - - // acquire and get num frames caught - REQUIRE_NOTHROW(test_acquire_with_receiver(caller, det)); - // TODO: maybe there should not be REQUIRE statements in void function at - // all, but traceback should be handled - { - auto frames_caught = det.getFramesCaught()[0][0]; - REQUIRE(frames_caught == num_frames); - } - - // hdf5 -#ifdef HDF5C - test_file_info.file_format = defs::HDF5; - test_file_info.file_acq_index = 0; - set_file_state(det, test_file_info); - - // acquire and get num frames caught - test_acquire_with_receiver(caller, det); - { - auto frames_caught = det.getFramesCaught()[0][0]; - REQUIRE(frames_caught == num_frames); - } -#endif - - // restore previous state - // file - set_file_state(det, prev_file_info); - det.setNumberOfFrames(prev_num_frames); - if (test_info) { - set_ctb_config_state(det, *prev_ctb_config_info); - } -} - -testCtbAcquireInfo get_ctb_config_state(const Detector &det) { - testCtbAcquireInfo ctb_config_info{ - det.getReadoutMode().tsquash("inconsistent readout mode to test"), - true, - det.getNumberOfAnalogSamples().tsquash( - "inconsistent number of analog samples to test"), - det.getNumberOfDigitalSamples().tsquash( - "inconsistent number of digital samples to test"), - det.getNumberOfTransceiverSamples().tsquash( - "inconsistent number of transceiver samples to test"), - 0, - det.getTenGigaADCEnableMask().tsquash( - "inconsistent ten giga adc enable mask to test"), - det.getRxDbitOffset().tsquash("inconsistent rx dbit offset to test"), - det.getRxDbitList().tsquash("inconsistent rx dbit list to test"), - det.getRxDbitReorder().tsquash("inconsistent rx dbit reorder to test"), - det.getTransceiverEnableMask().tsquash( - "inconsistent transceiver mask to test")}; - - if (det.getDetectorType().tsquash("inconsistent detector type to test") == - slsDetectorDefs::CHIPTESTBOARD) { - ctb_config_info.ten_giga = - det.getTenGiga().tsquash("inconsistent ten giga enable to test"); - ctb_config_info.adc_enable_1g = det.getADCEnableMask().tsquash( - "inconsistent adc enable mask to test"); - } - return ctb_config_info; -} - -void set_ctb_config_state(Detector &det, - const testCtbAcquireInfo &ctb_config_info) { - det.setReadoutMode(ctb_config_info.readout_mode); - if (det.getDetectorType().tsquash("inconsistent detector type to test") == - slsDetectorDefs::CHIPTESTBOARD) { - det.setTenGiga(ctb_config_info.ten_giga); - det.setADCEnableMask(ctb_config_info.adc_enable_1g); - } - det.setNumberOfAnalogSamples(ctb_config_info.num_adc_samples); - det.setNumberOfDigitalSamples(ctb_config_info.num_dbit_samples); - det.setNumberOfTransceiverSamples(ctb_config_info.num_trans_samples); - det.setTenGigaADCEnableMask(ctb_config_info.adc_enable_10g); - det.setRxDbitOffset(ctb_config_info.dbit_offset); - det.setRxDbitList(ctb_config_info.dbit_list); - det.setRxDbitReorder(ctb_config_info.dbit_reorder); - det.setTransceiverEnableMask(ctb_config_info.transceiver_mask); -} - std::pair -calculate_ctb_image_size(const testCtbAcquireInfo &test_info, - bool isXilinxCtb) { +calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb) { - // test_info.print(); // for debugging + LOG(logDEBUG1) << test_info; sls::CtbImageInputs inputs{}; inputs.mode = test_info.readout_mode; inputs.nAnalogSamples = test_info.num_adc_samples; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.h b/slsDetectorSoftware/tests/Caller/test-Caller-global.h index 344dd81e7..47e3d7c19 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.h +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.h @@ -2,10 +2,7 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once -#include "Caller.h" -#include "sls/Detector.h" -#include "sls/ToString.h" -#include "sls/logger.h" +#include "acquire/CTBState.h" #include "sls/sls_detector_defs.h" #include @@ -14,64 +11,8 @@ #include namespace sls { -struct testFileInfo { - std::string file_path{"/tmp"}; - std::string file_prefix{"sls_test"}; - int64_t file_acq_index{0}; - bool file_write{true}; - bool file_overwrite{true}; - slsDetectorDefs::fileFormat file_format{slsDetectorDefs::BINARY}; - std::string getMasterFileNamePrefix() const { - return file_path + "/" + file_prefix + "_master_" + - std::to_string(file_acq_index); - } - std::string getVirtualFileName() const { - return file_path + "/" + file_prefix + "_virtual_" + - std::to_string(file_acq_index) + ".h5"; - } - inline void print() const { - LOG(logINFO) << "File Info: " - << "\n\tFile Path: " << file_path - << "\n\tFile Prefix: " << file_prefix - << "\n\tFile Acquisition Index: " << file_acq_index - << "\n\tFile Write: " << file_write - << "\n\tFile Overwrite: " << file_overwrite - << "\n\tFile Format: " << ToString(file_format) - << "\n\tMaster Filename: " << getMasterFileNamePrefix() - << "\n\tVirtual Filename: " << getVirtualFileName(); - } -}; -struct testCtbAcquireInfo { - defs::readoutMode readout_mode{defs::ANALOG_AND_DIGITAL}; - bool ten_giga{false}; - int num_adc_samples{5000}; - int num_dbit_samples{6000}; - int num_trans_samples{288}; - uint32_t adc_enable_1g{0xFFFFFF00}; - uint32_t adc_enable_10g{0xFF00FFFF}; - int dbit_offset{0}; - std::vector dbit_list{0, 12, 2, 43}; - bool dbit_reorder{false}; - uint32_t transceiver_mask{0x3}; - - inline void print() const { - LOG(logINFO) << "CTB Acquire Info: " - << "\n\tReadout Mode: " << ToString(readout_mode) - << "\n\tTen Giga: " << ten_giga - << "\n\tADC Enable 1G: " << std::hex << adc_enable_1g - << std::dec << "\n\tADC Enable 10G: " << std::hex - << adc_enable_10g << std::dec - << "\n\tNumber of Analog Samples: " << num_adc_samples - << "\n\tNumber of Digital Samples: " << num_dbit_samples - << "\n\tNumber of Transceiver Samples: " - << num_trans_samples << "\n\tDBIT Offset: " << dbit_offset - << "\n\tDBIT Reorder: " << dbit_reorder - << "\n\tDBIT List: " << ToString(dbit_list) - << "\n\tTransceiver Mask: " << std::hex << transceiver_mask - << std::dec << std::endl; - } -}; +namespace acq = sls::test::acquire; void test_valid_port_caller(const std::string &command, const std::vector &arguments, @@ -82,22 +23,7 @@ void test_dac_caller(slsDetectorDefs::dacIndex index, void test_onchip_dac_caller(slsDetectorDefs::dacIndex index, const std::string &dacname, int dacvalue); -testFileInfo get_file_state(const Detector &det); -void set_file_state(Detector &det, const testFileInfo &file_info); -void test_acquire_binary_file_size(const testFileInfo &file_info, - uint64_t num_frames_to_acquire, - uint64_t expected_image_size); - -void test_acquire_with_receiver(Caller &caller, const Detector &det); - -void create_files_for_acquire( - Detector &det, Caller &caller, int64_t num_frames = 1, - const std::optional &test_info = std::nullopt); - -testCtbAcquireInfo get_ctb_config_state(const Detector &det); -void set_ctb_config_state(Detector &det, - const testCtbAcquireInfo &ctb_config_info); std::pair -calculate_ctb_image_size(const testCtbAcquireInfo &test_info, bool isXilinxCtb); +calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb); } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp index 4d3c5faef..5a3933ea8 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp @@ -1,16 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package -#include "Caller.h" -#include "MasterAttributes.h" -#include "catch.hpp" -#include "receiver_defs.h" +#include "acquire/ExpectedState.h" +#include "checks/MasterFileChecks.h" + #include "sls/Detector.h" #include "sls/ToString.h" #include "sls/logger.h" -#include "sls/sls_detector_defs.h" -#include "test-Caller-global.h" -#include "tests/globals.h" +#include "catch.hpp" #include #include #include @@ -20,1061 +17,58 @@ #ifdef HDF5C #include "H5Cpp.h" -const std::string HDF5_GROUP = "/entry/instrument/detector/"; #endif namespace sls { -using test::GET; -using test::PUT; -using namespace rapidjson; - -inline bool operator==(sls::ns lhs, sls::ns rhs) { - return lhs.count() == rhs.count(); -} - -#ifdef HDF5C -std::optional h5File{}; -#endif - -/** std::string */ -void read_from_json(const Document &doc, const std::string &name, - std::string &retval) { - retval = doc[name.c_str()].GetString(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::string &retval) { - dataset.read(retval, dataset.getStrType()); -} -#endif - -/** int */ -void read_from_json(const Document &doc, const std::string &name, int &retval) { - retval = doc[name.c_str()].GetInt(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - int &retval) { - dataset.read(&retval, H5::PredType::NATIVE_INT); -} -#endif - -/** uint64_t */ -void read_from_json(const Document &doc, const std::string &name, - uint64_t &retval) { - retval = doc[name.c_str()].GetUint64(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - uint64_t &retval) { - dataset.read(&retval, H5::PredType::STD_U64LE); -} -#endif - -/** uint32_t */ -void read_from_json(const Document &doc, const std::string &name, - uint32_t &retval) { - retval = doc[name.c_str()].GetUint(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - uint32_t &retval) { - dataset.read(&retval, H5::PredType::STD_U32LE); -} -#endif - -/** double */ -void read_from_json(const Document &doc, const std::string &name, - double &retval) { - retval = doc[name.c_str()].GetDouble(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - double &retval) { - dataset.read(&retval, H5::PredType::NATIVE_DOUBLE); -} -#endif - -/** std::vector */ -void read_from_json(const Document &doc, const std::string &name, - std::vector &retval) { - for (const auto &item : doc[name.c_str()].GetArray()) { - retval.push_back(item.GetInt64()); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::vector &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - retval.resize(dims[0]); - dataset.read(retval.data(), H5::PredType::STD_I64LE); -} -#endif - -/** std::vector */ -void read_from_json(const Document &doc, const std::string &name, - std::vector &retval) { - for (const auto &item : doc[name.c_str()].GetArray()) { - defs::ROI r{}; - r.xmin = item["xmin"].GetInt(); - r.xmax = item["xmax"].GetInt(); - r.ymin = item["ymin"].GetInt(); - r.ymax = item["ymax"].GetInt(); - retval.push_back(r); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::vector &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - H5::CompType cType(sizeof(defs::ROI)); - cType.insertMember("xmin", HOFFSET(defs::ROI, xmin), - H5::PredType::NATIVE_INT); - cType.insertMember("xmax", HOFFSET(defs::ROI, xmax), - H5::PredType::NATIVE_INT); - cType.insertMember("ymin", HOFFSET(defs::ROI, ymin), - H5::PredType::NATIVE_INT); - cType.insertMember("ymax", HOFFSET(defs::ROI, ymax), - H5::PredType::NATIVE_INT); - retval.resize(dims[0]); - dataset.read(retval.data(), cType); -} -#endif - -/** std::array */ -void read_from_json(const Document &doc, const std::string &name, - std::array &retval) { - const auto &json_values = doc[name.c_str()].GetArray(); - if (json_values.Size() != retval.size()) { - throw sls::RuntimeError("JSON array " + name + - " does not have num elements as expected"); - } - int index = 0; - for (const auto &item : json_values) { - retval[index++] = item.GetInt(); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::array &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] != retval.size()) { - throw sls::RuntimeError("HDF5 dataset " + name + - " does not have num elements as expected"); - } - dataset.read(retval.data(), H5::PredType::NATIVE_INT); -} -#endif - -/* std::array */ -void read_from_json(const Document &doc, const std::string &name, - std::array &retval) { - const auto &json_values = doc[name.c_str()].GetArray(); - if (json_values.Size() != retval.size()) { - throw sls::RuntimeError("JSON array " + name + - " does not have num elements as expected"); - } - int index = 0; - for (const auto &item : json_values) { - std::string sval = item.GetString(); - retval[index++] = StringTo(sval); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::array &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] != retval.size()) { - throw sls::RuntimeError("HDF5 dataset " + name + - " does not have num elements as expected"); - } - std::vector strValues(dims[0]); - dataset.read(strValues.data(), dataset.getStrType()); - for (size_t i = 0; i < dims[0]; ++i) { - retval[i] = StringTo(strValues[i]); - } -} -#endif - -/** defs::xy */ -void read_from_json(const Document &doc, const std::string &name, - defs::xy &retval) { - retval.x = doc[name.c_str()]["x"].GetInt(); - retval.y = doc[name.c_str()]["y"].GetInt(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - defs::xy &retval) { - H5::CompType cType(sizeof(defs::xy)); - cType.insertMember("x", HOFFSET(defs::xy, x), H5::PredType::NATIVE_INT); - cType.insertMember("y", HOFFSET(defs::xy, y), H5::PredType::NATIVE_INT); - dataset.read(&retval, cType); -} -#endif - -/** defs::scanParameters */ -void read_from_json(const Document &doc, const std::string &name, - defs::scanParameters &retval) { - const auto &s = doc[name.c_str()].GetObject(); - retval.enable = s["enable"].GetInt(); - retval.dacInd = static_cast(s["dacInd"].GetInt()); - retval.startOffset = s["start offset"].GetInt(); - retval.stopOffset = s["stop offset"].GetInt(); - retval.stepSize = s["step size"].GetInt(); - retval.dacSettleTime_ns = s["dac settle time ns"].GetInt64(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - defs::scanParameters &retval) { - H5::CompType cType(sizeof(defs::scanParameters)); - cType.insertMember("enable", HOFFSET(defs::scanParameters, enable), - H5::PredType::NATIVE_INT); - cType.insertMember("dacInd", HOFFSET(defs::scanParameters, dacInd), - H5::PredType::NATIVE_INT); - cType.insertMember("startOffset", - HOFFSET(defs::scanParameters, startOffset), - H5::PredType::NATIVE_INT); - cType.insertMember("stopOffset", HOFFSET(defs::scanParameters, stopOffset), - H5::PredType::NATIVE_INT); - cType.insertMember("stepSize", HOFFSET(defs::scanParameters, stepSize), - H5::PredType::NATIVE_INT); - cType.insertMember("dacSettleTime_ns", - HOFFSET(defs::scanParameters, dacSettleTime_ns), - H5::PredType::STD_I64LE); - dataset.read(&retval, cType); -} -#endif - -/** std::map */ -void read_from_json(const Document &doc, const std::string &name, - std::map &retval) { - for (const auto &m : doc[name.c_str()].GetObject()) { - retval[m.name.GetString()] = m.value.GetString(); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::map &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] == 0) { - return; // empty dataset - } - auto strType = dataset.getStrType(); - H5::CompType mapType(sizeof(char *) * 2); - mapType.insertMember("key", 0, strType); - mapType.insertMember("value", sizeof(char *), strType); - struct KeyValue { - const char *key; - const char *value; - }; - std::vector kv_vector(dims[0]); - dataset.read(kv_vector.data(), mapType); - for (const auto &kv : kv_vector) { - retval[kv.key] = kv.value; - } -} -#endif - -/** test parameter in file */ -template -void test_json_parameter(const Document &doc, const std::string &name, - const T &expected) { - REQUIRE(doc.HasMember(name.c_str())); - T retval{}; - read_from_json(doc, name, retval); - REQUIRE(retval == expected); -} -#ifdef HDF5C -template -void test_h5_dataset(const std::string &name, const T &expected) { - auto dataset = h5File->openDataSet(HDF5_GROUP + name); - T retval{}; - read_from_h5_dataset(dataset, name, retval); - REQUIRE(retval == expected); -} -#endif - -template -void check_master_file(const std::optional &doc, - const std::string &name, const T &expected) { - if (doc.has_value()) { - const auto &d = *doc; - test_json_parameter(d, name, expected); - } else { -#ifdef HDF5C - if (!h5File.has_value()) { - throw sls::RuntimeError("HDF5 file is not opened for testing " + - name); - } - test_h5_dataset(name, expected); -#else - throw sls::RuntimeError("Document is not available for testing " + - name); -#endif - } -} - -void test_master_file_version(const Detector &det, - const std::optional &doc) { - // different values for json and hdf5 - // hdf5 version in atttribute and not dataset - double retval{}; - std::string name = MasterAttributes::N_VERSION.data(); - if (doc.has_value()) { - const auto &d = *doc; - REQUIRE(d.HasMember(MasterAttributes::N_VERSION.data())); - read_from_json(d, name, retval); - REQUIRE(retval == BINARY_WRITER_VERSION); - } else { -#ifdef HDF5C - if (!h5File.has_value()) { - throw sls::RuntimeError( - "HDF5 file is not opened for testing Version"); - } - auto attr = h5File->openAttribute(MasterAttributes::N_VERSION.data()); - attr.read(attr.getDataType(), &retval); - REQUIRE(retval == HDF5_WRITER_VERSION); -#else - throw sls::RuntimeError( - "Document is not available for testing Version"); -#endif - } -} - -void test_master_file_type(const Detector &det, - const std::optional &doc) { - auto det_type = det.getDetectorType().tsquash("Inconsistent detector type"); - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DETECTOR_TYPE.data(), ToString(det_type))); -} - -void test_master_file_timing_mode(const Detector &det, - const std::optional &doc) { - auto timing_mode = det.getTimingMode().tsquash("Inconsistent timing mode"); - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TIMING_MODE.data(), ToString(timing_mode))); -} - -void test_master_file_geometry(const Detector &det, - const std::optional &doc) { - auto modGeometry = det.getModuleGeometry(); - auto portperModGeometry = det.getPortPerModuleGeometry(); - auto geometry = defs::xy{modGeometry.x * portperModGeometry.x, - modGeometry.y * portperModGeometry.y}; - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_GEOMETRY.data(), geometry)); -} - -void test_master_file_image_size(const Detector &det, - const std::optional &doc) { - - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - detParameters par(det_type); - - int image_size = 0; - switch (det_type) { - - case defs::EIGER: { - int num_chips = (par.nChipX / 2); - image_size = par.nChanX * par.nChanY * num_chips * bytes_per_pixel; - } break; - - case defs::JUNGFRAU: - case defs::MOENCH: { - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - image_size = (par.nChanX * par.nChanY * par.nChipX * par.nChipY * - bytes_per_pixel) / - num_udp_interfaces; - } break; - - case defs::MYTHEN3: { - int counter_mask = det.getCounterMask().squash(); - int num_counters = __builtin_popcount(counter_mask); - int num_channels_per_counter = par.nChanX / MAX_NUM_COUNTERS; - image_size = num_channels_per_counter * num_counters * par.nChipX * - bytes_per_pixel; - } break; - - case defs::GOTTHARD2: { - image_size = par.nChanX * par.nChipX * bytes_per_pixel; - } break; - - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: { - testCtbAcquireInfo test_info{}; - image_size = calculate_ctb_image_size( - test_info, (det_type == defs::XILINX_CHIPTESTBOARD)) - .first; - } break; - - default: - throw sls::RuntimeError("Unsupported detector type for this test"); - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_IMAGE_SIZE.data(), image_size)); -} - -void test_master_file_det_size(const Detector &det, - const std::optional &doc) { - - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - auto portSize = det.getPortSize()[0]; - - // m3 assumes all counters enabled when getting num channels from client - // TODO: in future, remove assumption - if (det_type == defs::MYTHEN3) { - int nchan = portSize.x / MAX_NUM_COUNTERS; - auto counter_mask = det.getCounterMask().tsquash( - "Inconsistent counter mask for Mythen3 detector"); - int num_counters = __builtin_popcount(counter_mask); - portSize.x = nchan * num_counters; - } else if (det_type == defs::CHIPTESTBOARD || - det_type == defs::XILINX_CHIPTESTBOARD) { - testCtbAcquireInfo test_info{}; - portSize.x = calculate_ctb_image_size( - test_info, det_type == defs::XILINX_CHIPTESTBOARD) - .second; - portSize.y = 1; - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_PIXELS.data(), portSize)); -} - -void test_master_file_max_frames_per_file(const Detector &det, - const std::optional &doc) { - auto max_frames_per_file = - det.getFramesPerFile().tsquash("Inconsistent max frames per file"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_MAX_FRAMES_PER_FILE.data(), - max_frames_per_file)); -} - -void test_master_file_frame_discard_policy(const Detector &det, - const std::optional &doc) { - auto policy = det.getRxFrameDiscardPolicy().tsquash( - "Inconsistent frame discard policy"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAME_DISCARD_POLICY.data(), - ToString(policy))); -} - -void test_master_file_frame_padding(const Detector &det, - const std::optional &doc) { - auto padding = static_cast( - det.getPartialFramesPadding().tsquash("Inconsistent frame padding")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAME_PADDING.data(), padding)); -} - -void test_master_file_scan_parameters(const Detector &det, - const std::optional &doc) { - auto scan_params = det.getScan().tsquash("Inconsistent scan parameters"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SCAN_PARAMETERS.data(), scan_params)); -} - -void test_master_file_total_frames(const Detector &det, - const std::optional &doc) { - uint64_t repeats = - det.getNumberOfTriggers().tsquash("Inconsistent number of triggers"); - uint64_t numFrames = - det.getNumberOfFrames().tsquash("Inconsistent number of frames"); - int numAdditionalStorageCells = 0; - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types"); - if (det_type == defs::GOTTHARD2) { - auto timing_mode = - det.getTimingMode().tsquash("Inconsistent timing mode"); - auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); - auto numBursts = - det.getNumberOfBursts().tsquash("Inconsistent number of bursts"); - // auto - if (timing_mode == defs::AUTO_TIMING) { - // burst mode, repeats = #bursts - if (burst_mode == defs::BURST_INTERNAL || - burst_mode == defs::BURST_EXTERNAL) { - repeats = numBursts; - } - // continuous, repeats = 1 (no trigger as well) - else { - repeats = 1; - } - } - // trigger - else { - // continuous, numFrames is limited - if (burst_mode == defs::CONTINUOUS_INTERNAL || - burst_mode == defs::CONTINUOUS_EXTERNAL) { - numFrames = 1; - } - } - } else if (det_type == defs::JUNGFRAU) { - numAdditionalStorageCells = - det.getNumberOfAdditionalStorageCells().tsquash( - "Inconsistent number of additional storage cells"); - } - uint64_t total_frames = - numFrames * repeats * (int64_t)(numAdditionalStorageCells + 1); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TOTAL_FRAMES.data(), total_frames)); -} - -void test_master_file_rois(const Detector &det, - const std::optional &doc) { - auto rois = det.getRxROI(); - auto detsize = det.getDetectorSize(); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - // compensate for m3 channel size and counter mask mess - if (det_type == defs::MYTHEN3) { - int nchan = detsize.x / MAX_NUM_COUNTERS; - auto counter_mask = det.getCounterMask().tsquash( - "Inconsistent counter mask for Mythen3 detector"); - int num_counters = __builtin_popcount(counter_mask); - detsize.x = nchan * num_counters; - } - // replace -1 for complete ROI - bool is2D = (detsize.y > 1); - for (auto &roi : rois) { - if (roi.completeRoi()) { - roi.xmin = 0; - roi.xmax = detsize.x - 1; - if (is2D) { - roi.ymin = 0; - roi.ymax = detsize.y - 1; - } - } - } - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_RECEIVER_ROIS.data(), rois)); -} - -void test_master_file_exptime(const Detector &det, - const std::optional &doc) { - auto exptime = det.getExptime().tsquash("Inconsistent exposure time"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_EXPOSURE_TIME.data(), ToString(exptime))); -} - -void test_master_file_period(const Detector &det, - const std::optional &doc) { - auto period = det.getPeriod().tsquash("Inconsistent period"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ACQUISITION_PERIOD.data(), ToString(period))); -} - -void test_master_file_num_udp_interfaces(const Detector &det, - const std::optional &doc) { - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "Inconsistent number of UDP interfaces"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_NUM_UDP_INTERFACES.data(), - num_udp_interfaces)); -} - -void test_master_file_read_n_rows(const Detector &det, - const std::optional &doc) { - auto readnrows = det.getReadNRows().tsquash("Inconsistent number of rows"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_NUMBER_OF_ROWS.data(), readnrows)); -} - -void test_master_file_readout_speed(const Detector &det, - const std::optional &doc) { - auto readout_speed = - det.getReadoutSpeed().tsquash("Inconsistent readout speed"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_READOUT_SPEED.data(), - ToString(readout_speed))); -} - -void test_master_file_frames_in_file(const std::optional &doc, - const int frames_in_file) { - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAMES_IN_FILE.data(), frames_in_file)); -} - -void test_master_file_json_header(const Detector &det, - const std::optional &doc) { - auto json_header = - det.getAdditionalJsonHeader().tsquash("Inconsistent JSON header"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_ADDITIONAL_JSON_HEADER.data(), json_header)); -} - -void test_master_file_dynamic_range(const Detector &det, - const std::optional &doc) { - auto dr = det.getDynamicRange().tsquash("Inconsistent dynamic range"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DYNAMIC_RANGE.data(), dr)); -} - -void test_master_file_ten_giga(const Detector &det, - const std::optional &doc) { - auto ten_giga = - static_cast(det.getTenGiga().tsquash("Inconsistent ten giga")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TEN_GIGA.data(), ten_giga)); -} - -void test_master_file_threshold_energy(const Detector &det, - const std::optional &doc) { - auto threshold = - det.getThresholdEnergy().tsquash("Inconsistent threshold energy"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_THRESHOLD_ENERGY.data(), threshold)); -} - -void test_master_file_sub_exptime(const Detector &det, - const std::optional &doc) { - auto sub_exptime = - det.getSubExptime().tsquash("Inconsistent sub exposure time"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SUB_EXPOSURE_TIME.data(), - ToString(sub_exptime))); -} - -void test_master_file_sub_period(const Detector &det, - const std::optional &doc) { - auto exptime = det.getSubExptime().tsquash("Inconsistent sub exptime"); - auto deadtime = det.getSubDeadTime().tsquash("Inconsistent sub deadtime"); - auto sub_period = exptime + deadtime; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SUB_ACQUISITION_PERIOD.data(), - ToString(sub_period))); -} - -void test_master_file_quad(const Detector &det, - const std::optional &doc) { - auto quad = static_cast(det.getQuad().tsquash("Inconsistent quad")); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_QUAD.data(), quad)); -} - -void test_master_file_rate_corrections(const Detector &det, - const std::optional &doc) { - std::vector dead_times; - for (auto item : det.getRateCorrection()) - dead_times.push_back(item.count()); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_RATE_CORRECTIONS.data(), dead_times)); -} - -void test_master_file_counter_mask(const Detector &det, - const std::optional &doc) { - auto counter_mask = static_cast( - det.getCounterMask().tsquash("Inconsistent counter mask")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_COUNTER_MASK.data(), counter_mask)); -} - -void test_master_file_exptimes(const Detector &det, - const std::optional &doc) { - auto exptimes = - det.getExptimeForAllGates().tsquash("Inconsistent exposure times"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_EXPOSURE_TIMES.data(), exptimes)); -} - -void test_master_file_gate_delays(const Detector &det, - const std::optional &doc) { - auto gate_delays = - det.getGateDelayForAllGates().tsquash("Inconsistent GateDelay"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_GATE_DELAYS.data(), gate_delays)); -} - -void test_master_file_gates(const Detector &det, - const std::optional &doc) { - auto gates = det.getNumberOfGates().tsquash("Inconsistent number of gates"); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_GATES.data(), gates)); -} - -void test_master_file_threadhold_energies(const Detector &det, - const std::optional &doc) { - auto threshold_energies = - det.getAllThresholdEnergy().tsquash("Inconsistent threshold energies"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_THRESHOLD_ENERGIES.data(), - threshold_energies)); -} - -void test_master_file_burst_mode(const Detector &det, - const std::optional &doc) { - auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_BURST_MODE.data(), ToString(burst_mode))); -} - -void test_master_file_adc_mask(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_ctb_config{}; - auto adc_mask = test_ctb_config.adc_enable_10g; - auto det_type = det.getDetectorType().squash(); - if (det_type == defs::CHIPTESTBOARD) { - auto tengiga = test_ctb_config.ten_giga; - if (!tengiga) - adc_mask = test_ctb_config.adc_enable_1g; - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ADC_MASK.data(), adc_mask)); -} - -void test_master_file_analog_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto analog = static_cast( - (romode == defs::ANALOG_ONLY || romode == defs::ANALOG_AND_DIGITAL)); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_ANALOG.data(), analog)); -} - -void test_master_file_analog_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto analog_samples = test_info.num_adc_samples; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ANALOG_SAMPLES.data(), analog_samples)); -} - -void test_master_file_digital_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto digital = static_cast(romode == defs::DIGITAL_ONLY || - romode == defs::ANALOG_AND_DIGITAL || - romode == defs::DIGITAL_AND_TRANSCEIVER); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DIGITAL.data(), digital)); -} - -void test_master_file_digital_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto digital_samples = test_info.num_dbit_samples; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DIGITAL_SAMPLES.data(), digital_samples)); -} - -void test_master_file_dbit_offset(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto dbit_offset = test_info.dbit_offset; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_OFFSET.data(), dbit_offset)); -} - -void test_master_file_dbit_reorder(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto dbit_reorder = test_info.dbit_reorder; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_REORDER.data(), dbit_reorder)); -} - -void test_master_file_dbit_bitset(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - uint64_t dbit_bitset = 0; - for (auto &i : test_info.dbit_list) { - dbit_bitset |= (static_cast(1) << i); - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_BITSET.data(), dbit_bitset)); -} - -void test_master_file_transceiver_mask(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto trans_mask = test_info.transceiver_mask; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER_MASK.data(), trans_mask)); -} - -void test_master_file_transceiver_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto trans = static_cast(romode == defs::DIGITAL_AND_TRANSCEIVER || - romode == defs::TRANSCEIVER_ONLY); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER.data(), trans)); -} - -void test_master_file_transceiver_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto trans_samples = test_info.num_trans_samples; - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER_SAMPLES.data(), trans_samples)); -} - -void test_master_file_common_metadata(const Detector &det, - const std::optional &doc) { - test_master_file_version(det, doc); - test_master_file_type(det, doc); - test_master_file_timing_mode(det, doc); - test_master_file_geometry(det, doc); - test_master_file_image_size(det, doc); - test_master_file_det_size(det, doc); - test_master_file_max_frames_per_file(det, doc); - test_master_file_frame_discard_policy(det, doc); - test_master_file_frame_padding(det, doc); - test_master_file_scan_parameters(det, doc); - test_master_file_total_frames(det, doc); - test_master_file_json_header(det, doc); - // TODO: test frame header format? -} - -void test_master_file_jungfrau_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Jungfrau specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_num_udp_interfaces(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_eiger_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Eiger specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_dynamic_range(det, doc)); - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_threshold_energy(det, doc)); - REQUIRE_NOTHROW(test_master_file_sub_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_sub_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_quad(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_rate_corrections(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_moench_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Moench specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_num_udp_interfaces(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_mythen3_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Mythen3 specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_dynamic_range(det, doc)); - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_counter_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptimes(det, doc)); - REQUIRE_NOTHROW(test_master_file_gate_delays(det, doc)); - REQUIRE_NOTHROW(test_master_file_gates(det, doc)); - REQUIRE_NOTHROW(test_master_file_threadhold_energies(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_gotthard2_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Gotthard2 specific metadata - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_burst_mode(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_ctb_metadata(const Detector &det, - const std::optional &doc) { - auto det_type = det.getDetectorType().squash(); - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Ctb specific metadata - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - if (det_type == defs::CHIPTESTBOARD) - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_adc_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_analog_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_analog_samples(det, doc)); - REQUIRE_NOTHROW(test_master_file_digital_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_digital_samples(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_offset(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_reorder(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_bitset(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_samples(det, doc)); -} - -void test_master_file_metadata(const Detector &det, - const std::optional &doc) { - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types"); - switch (det_type) { - case defs::JUNGFRAU: - test_master_file_jungfrau_metadata(det, doc); - break; - case defs::EIGER: - test_master_file_eiger_metadata(det, doc); - break; - case defs::MOENCH: - test_master_file_moench_metadata(det, doc); - break; - case defs::MYTHEN3: - test_master_file_mythen3_metadata(det, doc); - break; - case defs::GOTTHARD2: - test_master_file_gotthard2_metadata(det, doc); - break; - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: - test_master_file_ctb_metadata(det, doc); - break; - default: - break; - } -} - -Document parse_binary_master_attributes(std::string file_path) { - REQUIRE(std::filesystem::exists(file_path) == true); - std::ifstream file(file_path); - REQUIRE(file.is_open()); - std::stringstream buffer; - buffer << file.rdbuf(); - std::string json_str = buffer.str(); - - Document doc; - ParseResult result = doc.Parse(json_str.c_str()); - if (!result) { - std::cout << "JSON parse error: " << GetParseError_En(result.Code()) - << " (at offset " << result.Offset() << ")" << std::endl; - - // Optional: Show problematic snippet - size_t offset = result.Offset(); - std::string context = - json_str.substr(std::max(0, (int)offset - 20), 40); - std::cout << "Context around error: \"" << context << "\"" << std::endl; - } - REQUIRE(result); - return doc; -} - -#ifdef HDF5C -void open_hdf5_file(const std::string &file_path) { - REQUIRE(std::filesystem::exists(file_path) == true); - h5File = std::make_optional(file_path, H5F_ACC_RDONLY); - REQUIRE(H5Lexists(h5File->getId(), HDF5_GROUP.c_str(), H5P_DEFAULT) == - true); -} -#endif +namespace mf = sls::test::master_file; +namespace acq = sls::test::acquire; +namespace checks = sls::test::checks; TEST_CASE("check_master_file_attributes", "[.detectorintegration][.disable_check_data_file]") { Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); + auto detType = det.getDetectorType().squash(defs::GENERIC); + INFO("Testing master file attributes with " << ToString(detType)); - int64_t num_frames = 1; - switch (det_type) { - case defs::JUNGFRAU: - case defs::EIGER: - case defs::MOENCH: - case defs::MYTHEN3: - case defs::GOTTHARD2: - create_files_for_acquire(det, caller, num_frames); - break; - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: { - testCtbAcquireInfo test_ctb_config{}; - create_files_for_acquire(det, caller, num_frames, test_ctb_config); - } break; - default: - throw sls::RuntimeError("Unsupported detector type for this test"); + // currently num frame = 1 (default) + auto acq_state = acq::default_acquisition_state(); + auto file_state = acq::default_file_state(); + + // if ctb, set to default and restore after test + std::optional ctb_state = std::nullopt; + if (detType == defs::CHIPTESTBOARD || + detType == defs::XILINX_CHIPTESTBOARD) { + ctb_state = std::make_optional(acq::default_ctb_state(detType)); } + acq::CTBStateGuard ctb_guard(det, ctb_state); - testFileInfo file_info; - std::string master_file_prefix = file_info.getMasterFileNamePrefix(); + // binary => /tmp/sls_test_master_0.json + file_state.file_format = defs::BINARY; + acq::run(det, acq_state, file_state); - // binary - std::string fname = - master_file_prefix + ".json"; // /tmp/sls_test_master_0.json - auto doc = std::make_optional(parse_binary_master_attributes(fname)); - test_master_file_metadata(det, doc); - test_master_file_frames_in_file(doc, num_frames); + std::string fname = acq::get_master_file_name(file_state); + mf::Checker checker(fname); + + // get expected state of parameters and check against master file + acq::ExpectedState expected_state = + acq::build_expected_state(det, acq_state, file_state, ctb_state); + checks::check_metadata(checker, expected_state); - // hdf5 #ifdef HDF5C - fname = master_file_prefix + ".h5"; // /tmp/sls_test_master_0.h5 try { - open_hdf5_file(fname); - test_master_file_metadata(det, std::nullopt); - test_master_file_frames_in_file(std::nullopt, num_frames); + // hdf5 => /tmp/sls_test_master_0.h5 + file_state.file_format = defs::HDF5; + acq::run(det, acq_state, file_state); + + std::string fname = acq::get_master_file_name(file_state); + mf::Checker checker(fname); + + // get expected state of parameters and check against master file + acq::ExpectedState expected_state = + acq::build_expected_state(det, acq_state, file_state, ctb_state); + checks::check_metadata(checker, expected_state); } catch (H5::Exception &e) { LOG(logERROR) << "HDF5 error: " << e.getDetailMsg(); throw; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index 828f16316..2e8d8a9bd 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -1,20 +1,21 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Caller.h" -#include "catch.hpp" #include "sls/Detector.h" #include "sls/Version.h" -#include "sls/sls_detector_defs.h" -#include "test-Caller-global.h" - -#include -#include - #include "sls/versionAPI.h" +#include "test-Caller-global.h" #include "tests/globals.h" +#include "acquire/Acquire.h" +#include "acquire/FileState.h" + +#include "catch.hpp" + namespace sls { +namespace acq = sls::test::acquire; + using test::GET; using test::PUT; @@ -835,19 +836,22 @@ TEST_CASE("rx_roi", "[.detectorintegration][.disable_check_data_file]") { // check master file creation // TODO: check roi in master file { - REQUIRE_NOTHROW(create_files_for_acquire(det, caller)); - testFileInfo file_info; - std::string master_file_prefix = - file_info.getMasterFileNamePrefix(); + auto acq_state = acq::default_acquisition_state(); + auto file_state = acq::default_file_state(); + + file_state.file_format = defs::BINARY; + acq::run(det, acq_state, file_state); + std::string fname = acq::get_master_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); - std::string fname = master_file_prefix + ".json"; - REQUIRE(std::filesystem::exists(fname) == true); #ifdef HDF5C - fname = master_file_prefix + ".h5"; - REQUIRE(std::filesystem::exists(fname) == true); + file_state.file_format = defs::HDF5; + acq::run(det, acq_state, file_state); + fname = acq::get_master_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); if (det.size() > 1 || numinterfaces > 1) { - fname = file_info.getVirtualFileName(); - REQUIRE(std::filesystem::exists(fname) == true); + fname = acq::get_virtual_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); } #endif } diff --git a/slsDetectorSoftware/tests/acquire/Acquire.cpp b/slsDetectorSoftware/tests/acquire/Acquire.cpp new file mode 100644 index 000000000..bc738174d --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/Acquire.cpp @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#include "Acquire.h" +#include "FileState.h" + +#include "catch.hpp" +#include +#include + +namespace sls::test::acquire { + +void wait_until_idle(const Detector &det) { + bool idle = false; + while (!idle) { + auto statusList = det.getDetectorStatus(); + if (statusList.any(defs::ERROR)) { + throw RuntimeError("error status during acquisition"); + } + if (statusList.contains_only(defs::IDLE, defs::STOPPED)) { + idle = true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void run_acquisition(Detector &det) { + det.startReceiver(); + det.startDetector(); + wait_until_idle(det); + det.stopReceiver(); +} + +void run(Detector &det, const AcquisitionState &acq_state, + const FileState &file_state) { + + INFO(ToString(det.getDetectorType().squash(defs::GENERIC)) + << " acquiring with num_frames = " << acq_state.num_frames); + + FileStateGuard file_guard(det, file_state); + AcquisitionStateGuard acq_guard(det, acq_state); + run_acquisition(det); + auto frames_caught = det.getFramesCaught()[0][0]; + REQUIRE(frames_caught == acq_state.num_frames); +} + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/Acquire.h b/slsDetectorSoftware/tests/acquire/Acquire.h new file mode 100644 index 000000000..47cfc56fb --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/Acquire.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +namespace sls::test::acquire { + +class FileState; + +// at the moment, only number of frames +struct AcquisitionState { + int64_t num_frames; +}; + +inline AcquisitionState default_acquisition_state() { return {1}; } + +inline AcquisitionState get_acquisition_state(const Detector &det) { + return AcquisitionState{ + det.getNumberOfFrames().tsquash("Inconsistent number of frames")}; +} + +inline void set_acquisition_state(Detector &det, const AcquisitionState &s) { + det.setNumberOfFrames(s.num_frames); +} + +inline std::ostream &operator<<(std::ostream &os, const AcquisitionState &s) { + os << "Acquisition State:" + << "\n Number of Frames: " << s.num_frames; + return os; +} + +/** + * @brief RAII guard for restoring the acquisition state of a detector. + * The constructor saves the current acquisition state and sets a new state, + * while the destructor restores the original state. + * + */ +class AcquisitionStateGuard { + public: + explicit AcquisitionStateGuard(Detector &det, + const AcquisitionState &new_state) + : det(det), saved_(get_acquisition_state(det)) { + set_acquisition_state(det, new_state); + } + ~AcquisitionStateGuard() { set_acquisition_state(det, saved_); } + + private: + Detector &det; + AcquisitionState saved_; +}; + +void wait_until_idle(const Detector &det); +void run_acquisition(Detector &det); +void run(Detector &det, const AcquisitionState &acq_state, + const FileState &file_state); + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/CTBState.h b/slsDetectorSoftware/tests/acquire/CTBState.h new file mode 100644 index 000000000..dede4d9e4 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/CTBState.h @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +#include +#include +#include + +namespace sls::test::acquire { + +struct CTBState { + defs::readoutMode readout_mode; + int num_adc_samples; + int num_dbit_samples; + int num_trans_samples; + bool ten_giga; + uint32_t adc_enable_1g; + uint32_t adc_enable_10g; + int dbit_offset; + std::vector dbit_list; + bool dbit_reorder; + uint32_t transceiver_mask; +}; + +/** an example of CTB config */ +inline CTBState +default_ctb_state(const defs::detectorType &detType = defs::GENERIC) { + if (detType == defs::GENERIC) { + throw sls::RuntimeError( + "Cannot determine default CTB state for generic detector type."); + } + auto isAltera = (detType == defs::CHIPTESTBOARD); + return {defs::ANALOG_AND_DIGITAL, 5000, 6000, 288, + isAltera ? false : true, 0xFFFFFF00, 0xFF00FFFF, 0, + {0, 12, 2, 43}, false, 0x3}; +} + +inline CTBState get_ctb_state(const Detector &det) { + auto isAltera = + (det.getDetectorType().squash(defs::GENERIC) == defs::CHIPTESTBOARD); + return CTBState{ + det.getReadoutMode().tsquash("Inconsistent readout mode"), + det.getNumberOfAnalogSamples().tsquash( + "Inconsistent number of analog samples"), + det.getNumberOfDigitalSamples().tsquash( + "Inconsistent number of digital samples"), + det.getNumberOfTransceiverSamples().tsquash( + "Inconsistent number of transceiver samples"), + isAltera ? det.getTenGiga().tsquash("Inconsisten ten giga enable") + : true, + isAltera + ? det.getADCEnableMask().tsquash("Inconsistent adc enable mask") + : 0, + det.getTenGigaADCEnableMask().tsquash( + "Inconsistent ten giga adc enable mask"), + det.getRxDbitOffset().tsquash("Inconsistent rx dbit offset"), + det.getRxDbitList().tsquash("Inconsistent rx dbit list"), + det.getRxDbitReorder().tsquash("Inconsistent rx dbit reorder"), + det.getTransceiverEnableMask().tsquash( + "Inconsistent transceiver mask")}; +} + +inline void set_ctb_state(Detector &det, const CTBState &s) { + auto isAltera = + (det.getDetectorType().squash(defs::GENERIC) == defs::CHIPTESTBOARD); + det.setReadoutMode(s.readout_mode); + if (isAltera) { + det.setTenGiga(s.ten_giga); + det.setADCEnableMask(s.adc_enable_1g); + } + det.setNumberOfAnalogSamples(s.num_adc_samples); + det.setNumberOfDigitalSamples(s.num_dbit_samples); + det.setNumberOfTransceiverSamples(s.num_trans_samples); + det.setTenGigaADCEnableMask(s.adc_enable_10g); + det.setRxDbitOffset(s.dbit_offset); + det.setRxDbitList(s.dbit_list); + det.setRxDbitReorder(s.dbit_reorder); + det.setTransceiverEnableMask(s.transceiver_mask); +} + +inline std::ostream &operator<<(std::ostream &os, const CTBState &s) { + os << "CTB State:" + << "\n Readout Mode: " << ToString(s.readout_mode) + << "\n Num ADC Samples: " << s.num_adc_samples + << "\n Num DBIT Samples: " << s.num_dbit_samples + << "\n Num Trans Samples: " << s.num_trans_samples + << "\n Ten Giga: " << s.ten_giga + << "\n ADC Enable 1G: " << ToStringHex(s.adc_enable_1g) + << "\n ADC Enable 10G: " << ToStringHex(s.adc_enable_10g) + << "\n DBIT Offset: " << s.dbit_offset + << "\n DBIT List: " << ToString(s.dbit_list) + << "\n DBIT Reorder: " << s.dbit_reorder + << "\n Transceiver Mask: " << ToStringHex(s.transceiver_mask); + return os; +} + +inline void print_current_ctb_state(const Detector &det) { + auto ctb_state = get_ctb_state(det); + LOG(logINFO) << ctb_state; +} + +/** + * @brief RAII guard for restoring the existing ctb configuration after a test. + * + * The constructor saves the current ctb config and sets a new config, while the + * destructor restores the original config. + */ +class CTBStateGuard { + public: + explicit CTBStateGuard(Detector &det, + std::optional new_state = std::nullopt) + : det(det) { + auto type = det.getDetectorType().squash(defs::GENERIC); + if (type == defs::CHIPTESTBOARD || type == defs::XILINX_CHIPTESTBOARD) { + active = true; + if (!new_state.has_value()) { + throw sls::RuntimeError( + "New CTB state must be provided for CTBStateGuard."); + } + saved_ = get_ctb_state(det); + set_ctb_state(det, new_state.value()); + } + } + ~CTBStateGuard() { + if (active) + set_ctb_state(det, saved_); + } + + private: + Detector &det; + bool active{false}; + CTBState saved_; +}; + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/ExpectedState.cpp b/slsDetectorSoftware/tests/acquire/ExpectedState.cpp new file mode 100644 index 000000000..17a5edcf0 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/ExpectedState.cpp @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#include "ExpectedState.h" +#include "Caller/test-Caller-global.h" +#include "receiver_defs.h" + +// unnamed namespace for internal linkage +namespace { +using sls::defs; +using sls::Detector; +using ns = std::chrono::nanoseconds; +namespace acq = sls::test::acquire; + +defs::detectorType get_detector_type(const Detector &det) { + return det.getDetectorType().tsquash("Inconsistent detector type"); +} + +defs::xy get_geometry(const Detector &det) { + auto modGeometry = det.getModuleGeometry(); + auto portperModGeometry = det.getPortPerModuleGeometry(); + return defs::xy{modGeometry.x * portperModGeometry.x, + modGeometry.y * portperModGeometry.y}; +} + +int get_dynamic_range(const Detector &det) { + return det.getDynamicRange().tsquash("Inconsistent dynamic range"); +} + +defs::xy get_port_shape(const Detector &det, + std::optional &ctb_state) { + auto det_type = get_detector_type(det); + auto portSize = det.getPortSize()[0]; + + // m3 assumes all counters enabled when getting num channels from client + // TODO: in future, remove assumption + if (det_type == defs::MYTHEN3) { + int nchan = portSize.x / MAX_NUM_COUNTERS; + auto counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + int num_counters = __builtin_popcount(counter_mask); + portSize.x = nchan * num_counters; + } else if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to calculate expected port shape"); + } + portSize.x = + sls::calculate_ctb_image_size( + ctb_state.value(), det_type == defs::XILINX_CHIPTESTBOARD) + .second; + portSize.y = 1; + } + return portSize; +} + +uint64_t get_total_frames(const Detector &det) { + uint64_t repeats = + det.getNumberOfTriggers().tsquash("Inconsistent number of triggers"); + uint64_t numFrames = + det.getNumberOfFrames().tsquash("Inconsistent number of frames"); + int numAdditionalStorageCells = 0; + auto det_type = get_detector_type(det); + if (det_type == defs::GOTTHARD2) { + auto timing_mode = + det.getTimingMode().tsquash("Inconsistent timing mode"); + auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); + auto numBursts = + det.getNumberOfBursts().tsquash("Inconsistent number of bursts"); + if (timing_mode == defs::AUTO_TIMING) { + // burst mode, repeats = #bursts + if (burst_mode == defs::BURST_INTERNAL || + burst_mode == defs::BURST_EXTERNAL) { + repeats = numBursts; + } + // continuous, repeats = 1 (no trigger as well) + else { + repeats = 1; + } + } else { + // trigger + // continuous, numFrames is limited + if (burst_mode == defs::CONTINUOUS_INTERNAL || + burst_mode == defs::CONTINUOUS_EXTERNAL) { + numFrames = 1; + } + } + } else if (det_type == defs::JUNGFRAU) { + numAdditionalStorageCells = + det.getNumberOfAdditionalStorageCells().tsquash( + "Inconsistent number of additional storage cells"); + } + uint64_t total_frames = + numFrames * repeats * (int64_t)(numAdditionalStorageCells + 1); + return total_frames; +} + +std::vector get_rois(const Detector &det) { + auto rois = det.getRxROI(); + auto detsize = det.getDetectorSize(); + auto det_type = get_detector_type(det); + // compensate for m3 channel size and counter mask mess + if (det_type == defs::MYTHEN3) { + int nchan = detsize.x / MAX_NUM_COUNTERS; + auto counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + int num_counters = __builtin_popcount(counter_mask); + detsize.x = nchan * num_counters; + } + // replace -1 for complete ROI + bool is2D = (detsize.y > 1); + for (auto &roi : rois) { + if (roi.completeRoi()) { + roi.xmin = 0; + roi.xmax = detsize.x - 1; + if (is2D) { + roi.ymin = 0; + roi.ymax = detsize.y - 1; + } + } + } + return rois; +} + +ns get_exptime(const Detector &det) { + return ns(det.getExptime().tsquash("Inconsistent exposure time")); +} + +ns get_period(const Detector &det) { + return ns(det.getPeriod().tsquash("Inconsistent exposure time")); +} + +int get_num_udp_interfaces(const Detector &det) { + return det.getNumberofUDPInterfaces().tsquash( + "Inconsistent number of UDP interfaces"); +} + +int get_read_n_rows(const Detector &det) { + return det.getReadNRows().tsquash("Inconsistent number of read rows"); +} + +defs::speedLevel get_readout_speed(const Detector &det) { + return det.getReadoutSpeed().tsquash("Inconsistent readout speed"); +} + +bool get_ten_giga(const Detector &det) { + return det.getTenGiga().tsquash("Inconsistent 10Giga setting"); +} + +std::pair get_sub_exptime_and_sub_period(const Detector &det) { + auto exptime = det.getSubExptime().tsquash("Inconsistent sub exptime"); + auto deadtime = det.getSubDeadTime().tsquash("Inconsistent sub deadtime"); + auto sub_period = exptime + deadtime; + return std::make_pair(ns(exptime), ns(sub_period)); +} + +acq::CommonExpectedState +build_common_state(const Detector &det, + std::optional ctb_state) { + acq::CommonExpectedState e; + e.det_type = get_detector_type(det); + e.timing_mode = det.getTimingMode().tsquash("Inconsistent timing mode"); + e.geometry = get_geometry(det); + e.image_size = acq::get_expected_image_size(det, ctb_state); + e.port_shape = get_port_shape(det, ctb_state); + e.max_frames_per_file = + det.getFramesPerFile().tsquash("Inconsistent frames per file"); + e.frame_discard_policy = det.getRxFrameDiscardPolicy().tsquash( + "Inconsistent frame discard policy"); + e.partial_frames_padding = static_cast( + det.getPartialFramesPadding().tsquash("Inconsistent frame padding")); + e.scan_parameters = det.getScan().tsquash("Inconsistent scan parameters"); + e.total_frames = get_total_frames(det); + e.frames_in_file = det.getFramesCaught()[0][0]; + e.additional_json_header = + det.getAdditionalJsonHeader().tsquash("Inconsistent JSON header"); + return e; +} + +acq::JungfrauExpectedState build_jungfrau_specific_state(const Detector &det) { + acq::JungfrauExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.num_udp_interfaces = get_num_udp_interfaces(det); + e.read_n_rows = get_read_n_rows(det); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::MoenchExpectedState build_moench_specific_state(const Detector &det) { + acq::MoenchExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.num_udp_interfaces = get_num_udp_interfaces(det); + e.read_n_rows = get_read_n_rows(det); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::EigerExpectedState build_eiger_specific_state(const Detector &det) { + acq::EigerExpectedState e; + e.rois = get_rois(det); + e.dynamic_range = get_dynamic_range(det); + e.ten_giga = get_ten_giga(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.threshold_energy = + det.getThresholdEnergy().tsquash("Inconsistent threshold energy"); + auto [sub_exptime, sub_period] = get_sub_exptime_and_sub_period(det); + e.sub_exptime = sub_exptime; + e.sub_period = sub_period; + e.quad = det.getQuad().tsquash("Inconsistent quad setting"); + e.read_n_rows = get_read_n_rows(det); + { + for (auto item : det.getRateCorrection()) + e.rate_corrections.push_back(item.count()); + } + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::Mythen3ExpectedState build_mythen3_specific_state(const Detector &det) { + acq::Mythen3ExpectedState e; + e.rois = get_rois(det); + e.dynamic_range = get_dynamic_range(det); + e.ten_giga = get_ten_giga(det); + e.period = get_period(det); + e.counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + e.exp_times = det.getExptimeForAllGates().tsquash( + "Inconsistent exposure times for all gates"); + e.gate_delays = + det.getGateDelayForAllGates().tsquash("Inconsistent gate delays"); + e.num_gates = det.getNumberOfGates().tsquash( + "Inconsistent number of gates for Mythen3 detector"); + e.threshold_energies = + det.getAllThresholdEnergy().tsquash("Inconsistent threshold energies"); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::Gotthard2ExpectedState +build_gotthard2_specific_state(const Detector &det) { + acq::Gotthard2ExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::CTBExpectedState build_ctb_specific_state(const Detector &det, + const acq::CTBState &ctb_state) { + acq::CTBExpectedState e; + e.exptime = get_exptime(det); + e.period = get_period(det); + e.ctb_acq_state = ctb_state; + return e; +} + +acq::DetectorSpecificState +build_detector_specific_state(const Detector &det, + std::optional ctb_state) { + switch (det.getDetectorType().tsquash("bad type")) { + case defs::JUNGFRAU: + return build_jungfrau_specific_state(det); + case defs::MOENCH: + return build_moench_specific_state(det); + case defs::EIGER: + return build_eiger_specific_state(det); + case defs::MYTHEN3: + return build_mythen3_specific_state(det); + case defs::GOTTHARD2: + return build_gotthard2_specific_state(det); + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to build expected state"); + } + return build_ctb_specific_state(det, ctb_state.value()); + default: + throw sls::RuntimeError("Unsupported detector type"); + } +} +} // anonymous namespace + +namespace sls::test::acquire { + +ExpectedState build_expected_state(const Detector &det, + const AcquisitionState &acq_state, + const FileState &file_state, + std::optional ctb_state) { + ExpectedState e; + e.common_state = build_common_state(det, ctb_state); + e.file_state = file_state; + e.acquisition_state = acq_state; + e.detector_specific_state = build_detector_specific_state(det, ctb_state); + return e; +} + +int get_expected_image_size(const Detector &det, + std::optional ctb_state) { + auto det_type = get_detector_type(det); + auto dynamic_range = get_dynamic_range(det); + int bytes_per_pixel = dynamic_range / 8; + int image_size = 0; + detParameters par(det_type); + + switch (det_type) { + case defs::EIGER: { + int num_chips = (par.nChipX / 2); + image_size = par.nChanX * par.nChanY * num_chips * bytes_per_pixel; + } break; + case defs::JUNGFRAU: + case defs::MOENCH: { + auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( + "inconsistent number of udp interfaces"); + image_size = (par.nChanX * par.nChanY * par.nChipX * par.nChipY * + bytes_per_pixel) / + num_udp_interfaces; + } break; + case defs::MYTHEN3: { + int counter_mask = det.getCounterMask().squash(); + int num_counters = __builtin_popcount(counter_mask); + int num_channels_per_counter = par.nChanX / MAX_NUM_COUNTERS; + image_size = num_channels_per_counter * num_counters * par.nChipX * + bytes_per_pixel; + } break; + case defs::GOTTHARD2: { + image_size = par.nChanX * par.nChipX * bytes_per_pixel; + } break; + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to calculate expected image size"); + } + LOG(logINFORED) << ctb_state.value(); + image_size = + sls::calculate_ctb_image_size( + ctb_state.value(), (det_type == defs::XILINX_CHIPTESTBOARD)) + .first; + break; + default: + throw sls::RuntimeError("Unsupported detector type for this test"); + } + return image_size; +} + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/ExpectedState.h b/slsDetectorSoftware/tests/acquire/ExpectedState.h new file mode 100644 index 000000000..06b457cef --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/ExpectedState.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#pragma once + +#include "Acquire.h" +#include "CTBState.h" +#include "FileState.h" + +#include + +namespace sls::test::acquire { + +struct CommonExpectedState { + defs::detectorType det_type{}; + defs::timingMode timing_mode{}; + defs::xy geometry{}; + int image_size{}; + defs::xy port_shape{}; + int max_frames_per_file{}; + defs::frameDiscardPolicy frame_discard_policy{}; + bool partial_frames_padding{}; + defs::scanParameters scan_parameters{}; + uint64_t total_frames{}; + uint64_t frames_in_file{}; + std::map additional_json_header{}; +}; + +struct JungfrauExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + int num_udp_interfaces{}; + int read_n_rows{}; + defs::speedLevel readout_speed{}; +}; + +struct MoenchExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + int num_udp_interfaces{}; + int read_n_rows{}; + defs::speedLevel readout_speed{}; +}; + +struct EigerExpectedState { + std::vector rois{}; + int dynamic_range{}; + bool ten_giga{}; + ns exptime{}; + ns period{}; + int threshold_energy{}; + ns sub_exptime{}; + ns sub_period{}; + bool quad{}; + int read_n_rows{}; + std::vector rate_corrections{}; + defs::speedLevel readout_speed{}; +}; + +struct Mythen3ExpectedState { + std::vector rois{}; + int dynamic_range{}; + bool ten_giga{}; + ns period{}; + int counter_mask{}; + std::array exp_times{}; + std::array gate_delays{}; + int num_gates{}; + std::array threshold_energies{}; + defs::speedLevel readout_speed{}; +}; + +struct Gotthard2ExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + defs::burstMode burst_mode{}; + defs::speedLevel readout_speed{}; +}; + +struct CTBExpectedState { + ns exptime{}; + ns period{}; + CTBState ctb_acq_state{}; +}; + +using DetectorSpecificState = + std::variant; + +struct ExpectedState { + FileState file_state{}; + AcquisitionState acquisition_state{}; + CommonExpectedState common_state{}; + DetectorSpecificState detector_specific_state{}; +}; + +template +const T &get_detector_specific_state(const ExpectedState &expected_state) { + return std::get(expected_state.detector_specific_state); +} + +template bool is_type(const ExpectedState &e) { + return std::holds_alternative(e.detector_specific_state); +} + +ExpectedState build_expected_state( + const Detector &det, + const AcquisitionState &acq_state = default_acquisition_state(), + const FileState &file_state = default_file_state(), + std::optional ctb_state = std::nullopt); + +int get_expected_image_size(const Detector &det, + std::optional ctb_state = std::nullopt); + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/FileState.h b/slsDetectorSoftware/tests/acquire/FileState.h new file mode 100644 index 000000000..ff33e23b5 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/FileState.h @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +#include + +namespace sls::test::acquire { + +struct FileState { + std::string file_path; + std::string file_prefix; + int64_t file_acq_index; + bool file_write; + bool file_overwrite; + slsDetectorDefs::fileFormat file_format; +}; + +inline FileState default_file_state() { + return {"/tmp", "sls_test", 0, true, true, slsDetectorDefs::BINARY}; +} + +inline FileState get_file_state(const Detector &det) { + return FileState{ + det.getFilePath().tsquash("Inconsistent file path"), + det.getFileNamePrefix().tsquash("Inconsistent file prefix"), + det.getAcquisitionIndex().tsquash( + "Inconsistent file acquisition index"), + det.getFileWrite().tsquash("Inconsistent file write"), + det.getFileOverWrite().tsquash("Inconsistent file overwrite"), + det.getFileFormat().tsquash("Inconsistent file format")}; +} + +inline void set_file_state(Detector &det, const FileState &s) { + if (!s.file_path.empty()) + det.setFilePath(s.file_path); + det.setFileNamePrefix(s.file_prefix); + det.setAcquisitionIndex(s.file_acq_index); + det.setFileWrite(s.file_write); + det.setFileOverWrite(s.file_overwrite); + det.setFileFormat(s.file_format); +} + +inline std::string +get_master_file_name(const FileState &s = default_file_state()) { + auto master_file_prefix = s.file_path + "/" + s.file_prefix + "_master_" + + std::to_string(s.file_acq_index); + if (s.file_format == defs::BINARY) + return master_file_prefix + ".json"; + return master_file_prefix + ".h5"; +} + +inline std::string +get_virtual_file_name(const FileState &s = default_file_state()) { + return s.file_path + "/" + s.file_prefix + "_virtual_" + + std::to_string(s.file_acq_index) + ".h5"; +} + +inline std::string +get_first_port_first_file_name(const FileState &s = default_file_state()) { + auto file_prefix = s.file_path + "/" + s.file_prefix + "_d0_f0_" + + std::to_string(s.file_acq_index); + if (s.file_format == defs::BINARY) + return file_prefix + ".raw"; + return file_prefix + ".h5"; +} + +inline std::ostream &operator<<(std::ostream &os, const FileState &s) { + os << "File State:" + << "\n Path: " << s.file_path << "\n Prefix: " << s.file_prefix + << "\n Acq Index: " << s.file_acq_index << "\n Write: " << s.file_write + << "\n Overwrite: " << s.file_overwrite + << "\n Format: " << ToString(s.file_format) + << "\n Master File: " << get_master_file_name(s) + << "\n Virtual File: " << get_virtual_file_name(s); + return os; +} + +/** + * @brief RAII guard for restoring the file state of a detector. + * + * The constructor saves the current file state and sets a new state, while the + * destructor restores the original state. + */ +class FileStateGuard { + public: + explicit FileStateGuard(Detector &det, const FileState &new_state) + : det(det), saved_(get_file_state(det)) { + set_file_state(det, new_state); + } + ~FileStateGuard() { set_file_state(det, saved_); } + + private: + Detector &det; + FileState saved_; +}; + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/checks/MasterFileChecks.h b/slsDetectorSoftware/tests/checks/MasterFileChecks.h new file mode 100644 index 000000000..db5fff20f --- /dev/null +++ b/slsDetectorSoftware/tests/checks/MasterFileChecks.h @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "acquire/ExpectedState.h" +#include "master_file/Checker.h" + +#include "MasterAttributes.h" + +namespace sls::test::checks { + +namespace acq = sls::test::acquire; +namespace mf = sls::test::master_file; + +inline bool operator==(sls::ns lhs, sls::ns rhs) { + return lhs.count() == rhs.count(); +} + +// different values based on file format +template void check_version(CheckerT &checker) { +#ifdef HDF5C + if constexpr (std::is_same_v>) { + checker.template check(MasterAttributes::N_VERSION.data(), + HDF5_WRITER_VERSION, + mf::AccessType::Attribute); + } else +#endif + { + checker.template check(MasterAttributes::N_VERSION.data(), + BINARY_WRITER_VERSION); + } +} + +template +void check_detector_type(CheckerT &checker, const defs::detectorType &value) { + checker.template check( + MasterAttributes::N_DETECTOR_TYPE.data(), ToString(value)); +} + +template +void check_timing_mode(CheckerT &checker, const defs::timingMode &value) { + checker.template check(MasterAttributes::N_TIMING_MODE.data(), + ToString(value)); +} + +template +void check_geometry(CheckerT &checker, const defs::xy &value) { + checker.template check(MasterAttributes::N_GEOMETRY.data(), + value); +} + +template +void check_image_size(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_IMAGE_SIZE.data(), value); +} + +template +void check_port_shape(CheckerT &checker, const defs::xy &value) { + checker.template check(MasterAttributes::N_PIXELS.data(), value); +} + +template +void check_frames_per_file(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_MAX_FRAMES_PER_FILE.data(), + value); +} + +template +void check_frame_discard_policy(CheckerT &checker, + const defs::frameDiscardPolicy &value) { + checker.template check( + MasterAttributes::N_FRAME_DISCARD_POLICY.data(), ToString(value)); +} + +template +void check_partial_frames_padding(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_FRAME_PADDING.data(), + static_cast(value)); +} + +template +void check_scan_parameters(CheckerT &checker, + const defs::scanParameters &value) { + checker.template check( + MasterAttributes::N_SCAN_PARAMETERS.data(), value); +} + +template +void check_total_frames(CheckerT &checker, const uint64_t &value) { + checker.template check(MasterAttributes::N_TOTAL_FRAMES.data(), + value); +} + +template +void check_frames_in_file(CheckerT &checker, const uint64_t &value) { + checker.template check(MasterAttributes::N_FRAMES_IN_FILE.data(), + value); +} + +template +void check_additional_json_header( + CheckerT &checker, const std::map &value) { + checker.template check>( + MasterAttributes::N_ADDITIONAL_JSON_HEADER.data(), value); +} + +template +void check_rois(CheckerT &checker, const std::vector &value) { + checker.template check>( + MasterAttributes::N_RECEIVER_ROIS.data(), value); +} + +template +void check_exptime(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_EXPOSURE_TIME.data(), ToString(value)); +} + +template +void check_period(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_ACQUISITION_PERIOD.data(), ToString(value)); +} + +template +void check_num_udp_interfaces(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_NUM_UDP_INTERFACES.data(), + value); +} + +template +void check_read_n_rows(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_NUMBER_OF_ROWS.data(), + value); +} + +template +void check_readout_speed(CheckerT &checker, const defs::speedLevel &value) { + checker.template check( + MasterAttributes::N_READOUT_SPEED.data(), ToString(value)); +} + +template +void check_dynamic_range(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DYNAMIC_RANGE.data(), + value); +} + +template +void check_ten_giga(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_TEN_GIGA.data(), + static_cast(value)); +} + +template +void check_threshold_energy(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_THRESHOLD_ENERGY.data(), + value); +} + +template +void check_sub_exptime(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_SUB_EXPOSURE_TIME.data(), ToString(value)); +} + +template +void check_sub_period(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_SUB_ACQUISITION_PERIOD.data(), ToString(value)); +} + +template +void check_quad(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_QUAD.data(), + static_cast(value)); +} + +template +void check_rate_corrections(CheckerT &checker, + const std::vector &value) { + checker.template check>( + MasterAttributes::N_RATE_CORRECTIONS.data(), value); +} + +template +void check_counter_mask(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_COUNTER_MASK.data(), value); +} + +template +void check_exptime_array(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_EXPOSURE_TIMES.data(), value); +} + +template +void check_gate_delay_array(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_GATE_DELAYS.data(), value); +} + +template +void check_gates(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_GATES.data(), value); +} + +template +void check_threshold_energies(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_THRESHOLD_ENERGIES.data(), value); +} + +template +void check_burst_mode(CheckerT &checker, const defs::burstMode &value) { + checker.template check(MasterAttributes::N_BURST_MODE.data(), + ToString(value)); +} + +template +void check_readout_mode(CheckerT &checker, const defs::readoutMode &value) { + auto analog = static_cast( + (value == defs::ANALOG_ONLY || value == defs::ANALOG_AND_DIGITAL)); + auto digital = static_cast(value == defs::DIGITAL_ONLY || + value == defs::ANALOG_AND_DIGITAL || + value == defs::DIGITAL_AND_TRANSCEIVER); + auto trans = static_cast(value == defs::DIGITAL_AND_TRANSCEIVER || + value == defs::TRANSCEIVER_ONLY); + + if (analog) + checker.template check(MasterAttributes::N_ANALOG.data(), + static_cast(analog)); + if (digital) + checker.template check(MasterAttributes::N_DIGITAL.data(), + static_cast(digital)); + if (trans) + checker.template check(MasterAttributes::N_TRANSCEIVER.data(), + static_cast(trans)); +} + +template +void check_analog_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_ANALOG_SAMPLES.data(), + value); +} + +template +void check_digital_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DIGITAL_SAMPLES.data(), + value); +} + +template +void check_transceiver_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_TRANSCEIVER_SAMPLES.data(), + value); +} + +template +void check_adc_mask(CheckerT &checker, const uint32_t &value) { + checker.template check(MasterAttributes::N_ADC_MASK.data(), + value); +} + +template +void check_dbit_offset(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DBIT_OFFSET.data(), value); +} + +template +void check_dbit_list(CheckerT &checker, const std::vector &value) { + uint64_t dbit_bitset = 0; + for (auto &i : value) { + dbit_bitset |= (static_cast(1) << i); + } + checker.template check(MasterAttributes::N_DBIT_BITSET.data(), + dbit_bitset); +} + +template +void check_dbit_reorder(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DBIT_REORDER.data(), value); +} + +template +void check_transceiver_mask(CheckerT &checker, const uint32_t &value) { + checker.template check( + MasterAttributes::N_TRANSCEIVER_MASK.data(), value); +} + +template +void check_common_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + check_version(checker); + auto st = expected.common_state; + check_detector_type(checker, st.det_type); + check_geometry(checker, st.geometry); + check_image_size(checker, st.image_size); + check_port_shape(checker, st.port_shape); + check_frames_per_file(checker, st.max_frames_per_file); + check_frame_discard_policy(checker, st.frame_discard_policy); + check_partial_frames_padding(checker, st.partial_frames_padding); + check_scan_parameters(checker, st.scan_parameters); + check_total_frames(checker, st.total_frames); + check_frames_in_file(checker, st.frames_in_file); + check_additional_json_header(checker, st.additional_json_header); +} + +template +void check_jungfrau_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_num_udp_interfaces(checker, st.num_udp_interfaces); + check_read_n_rows(checker, st.read_n_rows); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_moench_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_num_udp_interfaces(checker, st.num_udp_interfaces); + check_read_n_rows(checker, st.read_n_rows); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_eiger_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_dynamic_range(checker, st.dynamic_range); + check_ten_giga(checker, st.ten_giga); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_threshold_energy(checker, st.threshold_energy); + check_sub_exptime(checker, st.sub_exptime); + check_sub_period(checker, st.sub_period); + check_quad(checker, st.quad); + check_read_n_rows(checker, st.read_n_rows); + check_rate_corrections(checker, st.rate_corrections); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_mythen3_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_dynamic_range(checker, st.dynamic_range); + check_ten_giga(checker, st.ten_giga); + check_period(checker, st.period); + check_counter_mask(checker, st.counter_mask); + check_exptime_array(checker, st.exp_times); + check_gate_delay_array(checker, st.gate_delays); + check_gates(checker, st.num_gates); + check_threshold_energies(checker, st.threshold_energies); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_gotthard2_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_burst_mode(checker, st.burst_mode); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_ctb_metadata(CheckerT &checker, const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + + // 10g for xilinx, 1g/10g for altera ctb + bool ten_giga = true; + if (expected.common_state.det_type == defs::CHIPTESTBOARD) { + check_ten_giga(checker, st.ctb_acq_state.ten_giga); + if (!st.ctb_acq_state.ten_giga) { + ten_giga = false; + } + } + if (ten_giga) + check_adc_mask(checker, st.ctb_acq_state.adc_enable_10g); + else + check_adc_mask(checker, st.ctb_acq_state.adc_enable_1g); + + check_readout_mode(checker, st.ctb_acq_state.readout_mode); + check_analog_samples(checker, st.ctb_acq_state.num_adc_samples); + check_digital_samples(checker, st.ctb_acq_state.num_dbit_samples); + check_transceiver_samples(checker, st.ctb_acq_state.num_trans_samples); + check_dbit_offset(checker, st.ctb_acq_state.dbit_offset); + check_dbit_list(checker, st.ctb_acq_state.dbit_list); + check_dbit_reorder(checker, st.ctb_acq_state.dbit_reorder); + check_transceiver_mask(checker, st.ctb_acq_state.transceiver_mask); +} + +template +void check_metadata(CheckerT &checker, const acq::ExpectedState &expected) { + check_common_metadata(checker, expected); + switch (expected.common_state.det_type) { + case defs::JUNGFRAU: + check_jungfrau_metadata(checker, expected); + break; + case defs::MOENCH: + check_moench_metadata(checker, expected); + break; + case defs::EIGER: + check_eiger_metadata(checker, expected); + break; + case defs::MYTHEN3: + check_mythen3_metadata(checker, expected); + break; + case defs::GOTTHARD2: + check_gotthard2_metadata(checker, expected); + break; + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + check_ctb_metadata(checker, expected); + break; + default: + throw std::runtime_error( + "Unsupported detector type for metadata checks"); + } +} + +inline void +check_binary_file_size(const int expected_image_size, const int64_t num_frames, + const acq::FileState &st = acq::default_file_state()) { + assert(st.file_format == defs::BINARY); + auto fname = acq::get_first_port_first_file_name(st); + auto expected_file_size = + num_frames * (expected_image_size + sizeof(defs::sls_receiver_header)); + auto actual_file_size = std::filesystem::file_size(fname); + REQUIRE(actual_file_size == expected_file_size); +} + +} // namespace sls::test::checks \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/Checker.h b/slsDetectorSoftware/tests/master_file/Checker.h new file mode 100644 index 000000000..94f1de687 --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Checker.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" +#include "ReadersJson.h" +#ifdef HDF5C +#include "ReadersH5.h" +#endif + +#include "catch.hpp" + +namespace sls::test::master_file { + +template class Checker; + +/** JSON Specialization */ +template <> class Checker { + public: + explicit Checker(const std::string &path) : ctx_(path) {} + explicit Checker(JsonContext ctx) : ctx_(std::move(ctx)) {} + const JsonContext &context() const { return ctx_; } + + template + void check(const std::string &name, const T &expected, + AccessType = AccessType::Dataset) const { + CAPTURE(name); + REQUIRE(ctx_.doc.HasMember(name.c_str())); + auto retval = read(ctx_, name); + REQUIRE(retval == expected); + } + + private: + JsonContext ctx_; +}; + +/** HDF5 Specialization */ +#ifdef HDF5C + +template <> class Checker { + public: + explicit Checker(const std::string &path) : ctx_(path) {} + explicit Checker(H5Context ctx) : ctx_(std::move(ctx)) {} + const H5Context &context() const { return ctx_; } + + template + void check(const std::string &name, const T &expected, + AccessType access = AccessType::Dataset) const { + auto retval = read(ctx_, name, access); + REQUIRE(retval == expected); + } + + private: + H5Context ctx_; +}; + +#endif + +} // namespace sls::test::master_file \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/Context.h b/slsDetectorSoftware/tests/master_file/Context.h new file mode 100644 index 000000000..00f8ad55a --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Context.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "catch.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifdef HDF5C +#include "H5Cpp.h" +#endif + +namespace sls::test::master_file { + +struct JsonContext { + rapidjson::Document doc; + + explicit JsonContext(const std::string &path) { + parse_binary_master_attributes(path); + } + + private: + void parse_binary_master_attributes(const std::string &file_path) { + REQUIRE(std::filesystem::exists(file_path)); + + std::ifstream file(file_path); + REQUIRE(file.is_open()); + + std::stringstream buffer; + buffer << file.rdbuf(); + + std::string json_str = buffer.str(); + + rapidjson::ParseResult result = doc.Parse(json_str.c_str()); + + if (!result) { + std::cout << "JSON parse error: " << GetParseError_En(result.Code()) + << " (at offset " << result.Offset() << ")" << std::endl; + + size_t offset = result.Offset(); + std::string context = + json_str.substr(std::max(0, (int)offset - 20), 40); + std::cout << "Context around error: \"" << context << "\"" + << std::endl; + } + + REQUIRE(result); + } +}; + +#ifdef HDF5C +struct H5Context { + H5::H5File file; + explicit H5Context(const std::string &path) : file(path, H5F_ACC_RDONLY) {} +}; +#endif + +} // namespace sls::test::master_file diff --git a/slsDetectorSoftware/tests/master_file/Readers.h b/slsDetectorSoftware/tests/master_file/Readers.h new file mode 100644 index 000000000..99675f54a --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Readers.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Context.h" +#include "sls/sls_detector_defs.h" + +namespace sls::test::master_file { + +template struct Reader; +template struct AttributeReader; + +enum class AccessType { Dataset, Attribute }; + +template +T read(const Context &ctx, const std::string &name, + AccessType access = AccessType::Dataset) { + return Reader::read(ctx, name, access); +} + +inline void check_size(size_t actual, size_t expected, const std::string &name, + const std::string &doc) { + if (actual != expected) { + throw sls::RuntimeError( + doc + " array " + name + " has " + std::to_string(actual) + + " elements instead of " + std::to_string(expected)); + } +} + +} // namespace sls::test::master_file diff --git a/slsDetectorSoftware/tests/master_file/ReadersH5.h b/slsDetectorSoftware/tests/master_file/ReadersH5.h new file mode 100644 index 000000000..fcd4fa9f7 --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/ReadersH5.h @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" +#include "sls/sls_detector_defs.h" +#include + +#ifdef HDF5C +#include "H5Cpp.h" + +namespace sls::test::master_file { +using ns = std::chrono::nanoseconds; + +static const std::string HDF5_GROUP = "/entry/instrument/detector/"; + +/* --safety checks-- */ +inline void require_dataset(const H5Context &ctx, const std::string &name) { + const std::string full_name = HDF5_GROUP + name; + if (H5Lexists(ctx.file.getId(), full_name.c_str(), H5P_DEFAULT) <= 0) { + throw sls::RuntimeError("Missing HDF5 dataset: " + full_name); + } +} + +inline void require_file_attribute(const H5Context &ctx, + const std::string &name) { + if (!H5Aexists_by_name(ctx.file.getId(), "/", name.c_str(), H5P_DEFAULT)) { + throw sls::RuntimeError("Missing HDF5 attribute: " + name); + } +} + +/* --scalar reads-- */ +template <> struct Reader { + static int read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("int attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + int out{}; + ds.read(&out, H5::PredType::NATIVE_INT); + return out; + } +}; + +template <> struct Reader { + static uint32_t read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "uint32_t attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + uint32_t out{}; + ds.read(&out, H5::PredType::STD_U32LE); + return out; + } +}; + +template <> struct Reader { + static uint64_t read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "'uint64_t' attribute access not supported for HDF5"); + } + uint64_t out{}; + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + ds.read(&out, H5::PredType::STD_U64LE); + return out; + } +}; + +template <> struct Reader { + static double read(const H5Context &ctx, const std::string &name, + AccessType access) { + double out{}; + if (access == AccessType::Attribute) { + require_file_attribute(ctx, name); + auto attr = ctx.file.openAttribute(name); + attr.read(attr.getDataType(), &out); + return out; + } + // dataset + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + ds.read(&out, H5::PredType::NATIVE_DOUBLE); + return out; + } +}; + +template <> struct Reader { + static std::string read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "string attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + std::string out{}; + ds.read(out, ds.getStrType()); + return out; + } +}; + +/** complex types */ +template <> struct Reader { + static defs::xy read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "'defs::xy' attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + // define type + H5::CompType type(sizeof(defs::xy)); + type.insertMember("x", HOFFSET(defs::xy, x), H5::PredType::NATIVE_INT); + type.insertMember("y", HOFFSET(defs::xy, y), H5::PredType::NATIVE_INT); + // read + defs::xy out{}; + ds.read(&out, type); + return out; + } +}; + +inline hsize_t get_1d_size(const H5::DataSet &ds) { + H5::DataSpace space = ds.getSpace(); + hsize_t dims[1]; + space.getSimpleExtentDims(dims); + return dims[0]; +} + +template <> struct Reader> { + static std::vector + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::vector' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + // define type + H5::CompType type(sizeof(defs::ROI)); + type.insertMember("xmin", HOFFSET(defs::ROI, xmin), + H5::PredType::NATIVE_INT); + type.insertMember("xmax", HOFFSET(defs::ROI, xmax), + H5::PredType::NATIVE_INT); + type.insertMember("ymin", HOFFSET(defs::ROI, ymin), + H5::PredType::NATIVE_INT); + type.insertMember("ymax", HOFFSET(defs::ROI, ymax), + H5::PredType::NATIVE_INT); + // read + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + std::vector out{}; + out.resize(len); + ds.read(out.data(), type); + return out; + } +}; + +template <> struct Reader { + static defs::scanParameters + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'defs::scanParameters' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + // define type + H5::CompType type(sizeof(defs::scanParameters)); + type.insertMember("enable", HOFFSET(defs::scanParameters, enable), + H5::PredType::NATIVE_INT); + type.insertMember("dacInd", HOFFSET(defs::scanParameters, dacInd), + H5::PredType::NATIVE_INT); + type.insertMember("startOffset", + HOFFSET(defs::scanParameters, startOffset), + H5::PredType::NATIVE_INT); + type.insertMember("stopOffset", + HOFFSET(defs::scanParameters, stopOffset), + H5::PredType::NATIVE_INT); + type.insertMember("stepSize", HOFFSET(defs::scanParameters, stepSize), + H5::PredType::NATIVE_INT); + type.insertMember("dacSettleTime_ns", + HOFFSET(defs::scanParameters, dacSettleTime_ns), + H5::PredType::STD_I64LE); + // read + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + defs::scanParameters out{}; + ds.read(&out, type); + return out; + } +}; + +/** arrays/vectors/maps */ +template <> struct Reader> { + static std::array + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::array' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + check_size(len, 3, name, "HDF5"); + std::array out{}; + ds.read(out.data(), H5::PredType::NATIVE_INT); + return out; + } +}; + +template <> struct Reader> { + static std::array + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::array' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + check_size(len, 3, name, "HDF5"); + + // read int raw char buffer + std::vector raw(len); + ds.read(raw.data(), ds.getStrType()); + + std::array out{}; + for (size_t i = 0; i != len; ++i) { + out[i] = StringTo(raw[i]); + } + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::vector' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + std::vector out{}; + out.resize(len); + ds.read(out.data(), H5::PredType::STD_I64LE); + return out; + } +}; + +template <> struct Reader> { + static std::map + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::map' attribute " + "access not supported for HDF5"); + } + require_dataset(ctx, name); + std::map out{}; + // dim + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + // empty ds + if (len == 0) { + return out; + } + // define type + auto strType = ds.getStrType(); + H5::CompType mapType(sizeof(char *) * 2); + mapType.insertMember("key", 0, strType); + mapType.insertMember("value", sizeof(char *), strType); + struct KeyValue { + const char *key; + const char *value; + }; + // read + std::vector kv_vector(len); + ds.read(kv_vector.data(), mapType); + for (const auto &kv : kv_vector) { + out[kv.key] = kv.value; + } + return out; + } +}; + +} // namespace sls::test::master_file + +#endif \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/ReadersJson.h b/slsDetectorSoftware/tests/master_file/ReadersJson.h new file mode 100644 index 000000000..013f8f0fd --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/ReadersJson.h @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" + +#include "sls/ToString.h" +#include "sls/sls_detector_defs.h" + +#include + +namespace sls::test::master_file { +using ns = std::chrono::nanoseconds; + +/* --scalar reads-- */ +template <> struct Reader { + static int read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetInt(); + } +}; + +template <> struct Reader { + static uint32_t read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetUint(); + } +}; + +template <> struct Reader { + static uint64_t read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetUint64(); + } +}; + +template <> struct Reader { + static double read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetDouble(); + } +}; + +template <> struct Reader { + static std::string read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetString(); + } +}; + +/** complex types */ +template <> struct Reader { + static defs::xy read(const JsonContext &ctx, const std::string &name, + AccessType access) { + defs::xy out{}; + out.x = ctx.doc[name.c_str()]["x"].GetInt(); + out.y = ctx.doc[name.c_str()]["y"].GetInt(); + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::vector out{}; + for (const auto &item : ctx.doc[name.c_str()].GetArray()) { + defs::ROI r{}; + r.xmin = item["xmin"].GetInt(); + r.xmax = item["xmax"].GetInt(); + r.ymin = item["ymin"].GetInt(); + r.ymax = item["ymax"].GetInt(); + out.push_back(r); + } + return out; + } +}; + +template <> struct Reader { + static defs::scanParameters + read(const JsonContext &ctx, const std::string &name, AccessType access) { + defs::scanParameters out{}; + const auto &s = ctx.doc[name.c_str()].GetObject(); + out.enable = s["enable"].GetInt(); + out.dacInd = static_cast(s["dacInd"].GetInt()); + out.startOffset = s["start offset"].GetInt(); + out.stopOffset = s["stop offset"].GetInt(); + out.stepSize = s["step size"].GetInt(); + out.dacSettleTime_ns = s["dac settle time ns"].GetInt64(); + return out; + } +}; + +/** arrays/vectors/maps */ +template <> struct Reader> { + static std::array + read(const JsonContext &ctx, const std::string &name, AccessType access) { + const auto &arr = ctx.doc[name.c_str()].GetArray(); + check_size(arr.Size(), 3, name, "JSON"); + + std::array out{}; + for (size_t i = 0; i < 3; ++i) { + out[i] = arr[i].GetInt(); + } + return out; + } +}; + +template <> struct Reader> { + static std::array + read(const JsonContext &ctx, const std::string &name, AccessType access) { + const auto &arr = ctx.doc[name.c_str()].GetArray(); + check_size(arr.Size(), 3, name, "JSON"); + + std::array out{}; + for (size_t i = 0; i < 3; ++i) { + std::string sval = arr[i].GetString(); + out[i] = StringTo(sval); + } + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::vector out{}; + for (const auto &item : ctx.doc[name.c_str()].GetArray()) { + out.push_back(item.GetInt64()); + } + return out; + } +}; + +template <> struct Reader> { + static std::map + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::map out{}; + for (const auto &m : ctx.doc[name.c_str()].GetObject()) { + out[m.name.GetString()] = m.value.GetString(); + } + return out; + } +}; + +} // namespace sls::test::master_file \ No newline at end of file