Merge branch 'developer' into dev/rx_disable_datastream_port
Build on RHEL9 docker image / build (push) Successful in 3m43s
Build on RHEL8 docker image / build (push) Successful in 5m12s
Run Simulator Tests on local RHEL9 / build (push) Successful in 18m33s
Run Simulator Tests on local RHEL8 / build (push) Successful in 21m59s

This commit is contained in:
2026-06-11 10:46:35 +02:00
18 changed files with 2193 additions and 1564 deletions
+6
View File
@@ -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
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/Caller"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include/sls>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../slsReceiverSoftware/src>"
@@ -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 <filesystem>
#include <sstream>
#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<acq::CTBState> 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
@@ -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<testCtbAcquireInfo> &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<testCtbAcquireInfo> 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<uint64_t, int>
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;
@@ -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 <chrono>
@@ -14,64 +11,8 @@
#include <thread>
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<int> 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<std::string> &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<testCtbAcquireInfo> &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<uint64_t, int>
calculate_ctb_image_size(const testCtbAcquireInfo &test_info, bool isXilinxCtb);
calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb);
} // namespace sls
File diff suppressed because it is too large Load Diff
@@ -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 <filesystem>
#include <sstream>
#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
}
@@ -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 <chrono>
#include <thread>
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
@@ -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
@@ -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 <cstdint>
#include <optional>
#include <vector>
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<int> 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<CTBState> 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
@@ -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<acq::CTBState> &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<defs::ROI> 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<ns, ns> 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<acq::CTBState> 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<int>(
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<acq::CTBState> 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<CTBState> 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<CTBState> 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
@@ -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 <variant>
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<std::string, std::string> additional_json_header{};
};
struct JungfrauExpectedState {
std::vector<defs::ROI> rois{};
ns exptime{};
ns period{};
int num_udp_interfaces{};
int read_n_rows{};
defs::speedLevel readout_speed{};
};
struct MoenchExpectedState {
std::vector<defs::ROI> rois{};
ns exptime{};
ns period{};
int num_udp_interfaces{};
int read_n_rows{};
defs::speedLevel readout_speed{};
};
struct EigerExpectedState {
std::vector<defs::ROI> 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<int64_t> rate_corrections{};
defs::speedLevel readout_speed{};
};
struct Mythen3ExpectedState {
std::vector<defs::ROI> rois{};
int dynamic_range{};
bool ten_giga{};
ns period{};
int counter_mask{};
std::array<ns, 3> exp_times{};
std::array<ns, 3> gate_delays{};
int num_gates{};
std::array<int, 3> threshold_energies{};
defs::speedLevel readout_speed{};
};
struct Gotthard2ExpectedState {
std::vector<defs::ROI> 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<JungfrauExpectedState, MoenchExpectedState, EigerExpectedState,
Mythen3ExpectedState, Gotthard2ExpectedState,
CTBExpectedState>;
struct ExpectedState {
FileState file_state{};
AcquisitionState acquisition_state{};
CommonExpectedState common_state{};
DetectorSpecificState detector_specific_state{};
};
template <typename T>
const T &get_detector_specific_state(const ExpectedState &expected_state) {
return std::get<T>(expected_state.detector_specific_state);
}
template <typename T> bool is_type(const ExpectedState &e) {
return std::holds_alternative<T>(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<CTBState> ctb_state = std::nullopt);
int get_expected_image_size(const Detector &det,
std::optional<CTBState> ctb_state = std::nullopt);
} // namespace sls::test::acquire
@@ -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 <string>
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
@@ -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 <typename CheckerT> void check_version(CheckerT &checker) {
#ifdef HDF5C
if constexpr (std::is_same_v<CheckerT, mf::Checker<mf::H5Context>>) {
checker.template check<double>(MasterAttributes::N_VERSION.data(),
HDF5_WRITER_VERSION,
mf::AccessType::Attribute);
} else
#endif
{
checker.template check<double>(MasterAttributes::N_VERSION.data(),
BINARY_WRITER_VERSION);
}
}
template <typename CheckerT>
void check_detector_type(CheckerT &checker, const defs::detectorType &value) {
checker.template check<std::string>(
MasterAttributes::N_DETECTOR_TYPE.data(), ToString(value));
}
template <typename CheckerT>
void check_timing_mode(CheckerT &checker, const defs::timingMode &value) {
checker.template check<std::string>(MasterAttributes::N_TIMING_MODE.data(),
ToString(value));
}
template <typename CheckerT>
void check_geometry(CheckerT &checker, const defs::xy &value) {
checker.template check<defs::xy>(MasterAttributes::N_GEOMETRY.data(),
value);
}
template <typename CheckerT>
void check_image_size(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_IMAGE_SIZE.data(), value);
}
template <typename CheckerT>
void check_port_shape(CheckerT &checker, const defs::xy &value) {
checker.template check<defs::xy>(MasterAttributes::N_PIXELS.data(), value);
}
template <typename CheckerT>
void check_frames_per_file(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_MAX_FRAMES_PER_FILE.data(),
value);
}
template <typename CheckerT>
void check_frame_discard_policy(CheckerT &checker,
const defs::frameDiscardPolicy &value) {
checker.template check<std::string>(
MasterAttributes::N_FRAME_DISCARD_POLICY.data(), ToString(value));
}
template <typename CheckerT>
void check_partial_frames_padding(CheckerT &checker, const bool &value) {
checker.template check<int>(MasterAttributes::N_FRAME_PADDING.data(),
static_cast<int>(value));
}
template <typename CheckerT>
void check_scan_parameters(CheckerT &checker,
const defs::scanParameters &value) {
checker.template check<defs::scanParameters>(
MasterAttributes::N_SCAN_PARAMETERS.data(), value);
}
template <typename CheckerT>
void check_total_frames(CheckerT &checker, const uint64_t &value) {
checker.template check<uint64_t>(MasterAttributes::N_TOTAL_FRAMES.data(),
value);
}
template <typename CheckerT>
void check_frames_in_file(CheckerT &checker, const uint64_t &value) {
checker.template check<uint64_t>(MasterAttributes::N_FRAMES_IN_FILE.data(),
value);
}
template <typename CheckerT>
void check_additional_json_header(
CheckerT &checker, const std::map<std::string, std::string> &value) {
checker.template check<std::map<std::string, std::string>>(
MasterAttributes::N_ADDITIONAL_JSON_HEADER.data(), value);
}
template <typename CheckerT>
void check_rois(CheckerT &checker, const std::vector<defs::ROI> &value) {
checker.template check<std::vector<defs::ROI>>(
MasterAttributes::N_RECEIVER_ROIS.data(), value);
}
template <typename CheckerT>
void check_exptime(CheckerT &checker, const ns &value) {
checker.template check<std::string>(
MasterAttributes::N_EXPOSURE_TIME.data(), ToString(value));
}
template <typename CheckerT>
void check_period(CheckerT &checker, const ns &value) {
checker.template check<std::string>(
MasterAttributes::N_ACQUISITION_PERIOD.data(), ToString(value));
}
template <typename CheckerT>
void check_num_udp_interfaces(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_NUM_UDP_INTERFACES.data(),
value);
}
template <typename CheckerT>
void check_read_n_rows(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_NUMBER_OF_ROWS.data(),
value);
}
template <typename CheckerT>
void check_readout_speed(CheckerT &checker, const defs::speedLevel &value) {
checker.template check<std::string>(
MasterAttributes::N_READOUT_SPEED.data(), ToString(value));
}
template <typename CheckerT>
void check_dynamic_range(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_DYNAMIC_RANGE.data(),
value);
}
template <typename CheckerT>
void check_ten_giga(CheckerT &checker, const bool &value) {
checker.template check<int>(MasterAttributes::N_TEN_GIGA.data(),
static_cast<int>(value));
}
template <typename CheckerT>
void check_threshold_energy(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_THRESHOLD_ENERGY.data(),
value);
}
template <typename CheckerT>
void check_sub_exptime(CheckerT &checker, const ns &value) {
checker.template check<std::string>(
MasterAttributes::N_SUB_EXPOSURE_TIME.data(), ToString(value));
}
template <typename CheckerT>
void check_sub_period(CheckerT &checker, const ns &value) {
checker.template check<std::string>(
MasterAttributes::N_SUB_ACQUISITION_PERIOD.data(), ToString(value));
}
template <typename CheckerT>
void check_quad(CheckerT &checker, const bool &value) {
checker.template check<int>(MasterAttributes::N_QUAD.data(),
static_cast<int>(value));
}
template <typename CheckerT>
void check_rate_corrections(CheckerT &checker,
const std::vector<int64_t> &value) {
checker.template check<std::vector<int64_t>>(
MasterAttributes::N_RATE_CORRECTIONS.data(), value);
}
template <typename CheckerT>
void check_counter_mask(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_COUNTER_MASK.data(), value);
}
template <typename CheckerT>
void check_exptime_array(CheckerT &checker,
const std::array<sls::ns, 3UL> &value) {
checker.template check<std::array<sls::ns, 3UL>>(
MasterAttributes::N_EXPOSURE_TIMES.data(), value);
}
template <typename CheckerT>
void check_gate_delay_array(CheckerT &checker,
const std::array<sls::ns, 3UL> &value) {
checker.template check<std::array<sls::ns, 3UL>>(
MasterAttributes::N_GATE_DELAYS.data(), value);
}
template <typename CheckerT>
void check_gates(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_GATES.data(), value);
}
template <typename CheckerT>
void check_threshold_energies(CheckerT &checker,
const std::array<int, 3UL> &value) {
checker.template check<std::array<int, 3UL>>(
MasterAttributes::N_THRESHOLD_ENERGIES.data(), value);
}
template <typename CheckerT>
void check_burst_mode(CheckerT &checker, const defs::burstMode &value) {
checker.template check<std::string>(MasterAttributes::N_BURST_MODE.data(),
ToString(value));
}
template <typename CheckerT>
void check_readout_mode(CheckerT &checker, const defs::readoutMode &value) {
auto analog = static_cast<int>(
(value == defs::ANALOG_ONLY || value == defs::ANALOG_AND_DIGITAL));
auto digital = static_cast<int>(value == defs::DIGITAL_ONLY ||
value == defs::ANALOG_AND_DIGITAL ||
value == defs::DIGITAL_AND_TRANSCEIVER);
auto trans = static_cast<int>(value == defs::DIGITAL_AND_TRANSCEIVER ||
value == defs::TRANSCEIVER_ONLY);
if (analog)
checker.template check<int>(MasterAttributes::N_ANALOG.data(),
static_cast<int>(analog));
if (digital)
checker.template check<int>(MasterAttributes::N_DIGITAL.data(),
static_cast<int>(digital));
if (trans)
checker.template check<int>(MasterAttributes::N_TRANSCEIVER.data(),
static_cast<int>(trans));
}
template <typename CheckerT>
void check_analog_samples(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_ANALOG_SAMPLES.data(),
value);
}
template <typename CheckerT>
void check_digital_samples(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_DIGITAL_SAMPLES.data(),
value);
}
template <typename CheckerT>
void check_transceiver_samples(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_TRANSCEIVER_SAMPLES.data(),
value);
}
template <typename CheckerT>
void check_adc_mask(CheckerT &checker, const uint32_t &value) {
checker.template check<uint32_t>(MasterAttributes::N_ADC_MASK.data(),
value);
}
template <typename CheckerT>
void check_dbit_offset(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_DBIT_OFFSET.data(), value);
}
template <typename CheckerT>
void check_dbit_list(CheckerT &checker, const std::vector<int> &value) {
uint64_t dbit_bitset = 0;
for (auto &i : value) {
dbit_bitset |= (static_cast<uint64_t>(1) << i);
}
checker.template check<uint64_t>(MasterAttributes::N_DBIT_BITSET.data(),
dbit_bitset);
}
template <typename CheckerT>
void check_dbit_reorder(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_DBIT_REORDER.data(), value);
}
template <typename CheckerT>
void check_transceiver_mask(CheckerT &checker, const uint32_t &value) {
checker.template check<uint32_t>(
MasterAttributes::N_TRANSCEIVER_MASK.data(), value);
}
template <typename CheckerT>
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 <typename CheckerT>
void check_jungfrau_metadata(CheckerT &checker,
const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::JungfrauExpectedState>(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 <typename CheckerT>
void check_moench_metadata(CheckerT &checker,
const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::MoenchExpectedState>(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 <typename CheckerT>
void check_eiger_metadata(CheckerT &checker,
const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::EigerExpectedState>(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 <typename CheckerT>
void check_mythen3_metadata(CheckerT &checker,
const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::Mythen3ExpectedState>(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 <typename CheckerT>
void check_gotthard2_metadata(CheckerT &checker,
const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::Gotthard2ExpectedState>(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 <typename CheckerT>
void check_ctb_metadata(CheckerT &checker, const acq::ExpectedState &expected) {
const auto &st =
std::get<acq::CTBExpectedState>(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 <typename CheckerT>
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
@@ -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 <typename Context> class Checker;
/** JSON Specialization */
template <> class Checker<JsonContext> {
public:
explicit Checker(const std::string &path) : ctx_(path) {}
explicit Checker(JsonContext ctx) : ctx_(std::move(ctx)) {}
const JsonContext &context() const { return ctx_; }
template <typename T>
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<JsonContext, T>(ctx_, name);
REQUIRE(retval == expected);
}
private:
JsonContext ctx_;
};
/** HDF5 Specialization */
#ifdef HDF5C
template <> class Checker<H5Context> {
public:
explicit Checker(const std::string &path) : ctx_(path) {}
explicit Checker(H5Context ctx) : ctx_(std::move(ctx)) {}
const H5Context &context() const { return ctx_; }
template <typename T>
void check(const std::string &name, const T &expected,
AccessType access = AccessType::Dataset) const {
auto retval = read<H5Context, T>(ctx_, name, access);
REQUIRE(retval == expected);
}
private:
H5Context ctx_;
};
#endif
} // namespace sls::test::master_file
@@ -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 <filesystem>
#include <fstream>
#include <optional>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <sstream>
#include <string>
#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
@@ -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 <typename Context, typename T> struct Reader;
template <typename Context, typename T> struct AttributeReader;
enum class AccessType { Dataset, Attribute };
template <typename Context, typename T>
T read(const Context &ctx, const std::string &name,
AccessType access = AccessType::Dataset) {
return Reader<Context, T>::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
@@ -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 <iostream>
#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<H5Context, int> {
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<H5Context, uint32_t> {
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<H5Context, uint64_t> {
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<H5Context, double> {
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<H5Context, std::string> {
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<H5Context, defs::xy> {
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<H5Context, std::vector<defs::ROI>> {
static std::vector<defs::ROI>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::vector<defs::ROI>' 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<defs::ROI> out{};
out.resize(len);
ds.read(out.data(), type);
return out;
}
};
template <> struct Reader<H5Context, defs::scanParameters> {
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<H5Context, std::array<int, 3UL>> {
static std::array<int, 3UL>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::array<int, 3UL>' 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<int, 3UL> out{};
ds.read(out.data(), H5::PredType::NATIVE_INT);
return out;
}
};
template <> struct Reader<H5Context, std::array<ns, 3UL>> {
static std::array<ns, 3UL>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::array<ns, 3UL>' 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<const char *> raw(len);
ds.read(raw.data(), ds.getStrType());
std::array<ns, 3UL> out{};
for (size_t i = 0; i != len; ++i) {
out[i] = StringTo<ns>(raw[i]);
}
return out;
}
};
template <> struct Reader<H5Context, std::vector<int64_t>> {
static std::vector<int64_t>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::vector<int64_t>' 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<int64_t> out{};
out.resize(len);
ds.read(out.data(), H5::PredType::STD_I64LE);
return out;
}
};
template <> struct Reader<H5Context, std::map<std::string, std::string>> {
static std::map<std::string, std::string>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::map<std::string, std::string>' attribute "
"access not supported for HDF5");
}
require_dataset(ctx, name);
std::map<std::string, std::string> 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<KeyValue> 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
@@ -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 <rapidjson/document.h>
namespace sls::test::master_file {
using ns = std::chrono::nanoseconds;
/* --scalar reads-- */
template <> struct Reader<JsonContext, int> {
static int read(const JsonContext &ctx, const std::string &name,
AccessType access) {
return ctx.doc[name.c_str()].GetInt();
}
};
template <> struct Reader<JsonContext, uint32_t> {
static uint32_t read(const JsonContext &ctx, const std::string &name,
AccessType access) {
return ctx.doc[name.c_str()].GetUint();
}
};
template <> struct Reader<JsonContext, uint64_t> {
static uint64_t read(const JsonContext &ctx, const std::string &name,
AccessType access) {
return ctx.doc[name.c_str()].GetUint64();
}
};
template <> struct Reader<JsonContext, double> {
static double read(const JsonContext &ctx, const std::string &name,
AccessType access) {
return ctx.doc[name.c_str()].GetDouble();
}
};
template <> struct Reader<JsonContext, std::string> {
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<JsonContext, defs::xy> {
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<JsonContext, std::vector<defs::ROI>> {
static std::vector<defs::ROI>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
std::vector<defs::ROI> 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<JsonContext, defs::scanParameters> {
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<defs::dacIndex>(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<JsonContext, std::array<int, 3UL>> {
static std::array<int, 3UL>
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<int, 3UL> out{};
for (size_t i = 0; i < 3; ++i) {
out[i] = arr[i].GetInt();
}
return out;
}
};
template <> struct Reader<JsonContext, std::array<ns, 3UL>> {
static std::array<ns, 3UL>
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<ns, 3UL> out{};
for (size_t i = 0; i < 3; ++i) {
std::string sval = arr[i].GetString();
out[i] = StringTo<ns>(sval);
}
return out;
}
};
template <> struct Reader<JsonContext, std::vector<int64_t>> {
static std::vector<int64_t>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
std::vector<int64_t> out{};
for (const auto &item : ctx.doc[name.c_str()].GetArray()) {
out.push_back(item.GetInt64());
}
return out;
}
};
template <> struct Reader<JsonContext, std::map<std::string, std::string>> {
static std::map<std::string, std::string>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
std::map<std::string, std::string> 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