diff --git a/python/scripts/generate_functions.py b/python/scripts/generate_functions.py index ae6d13326..5d281cdd9 100644 --- a/python/scripts/generate_functions.py +++ b/python/scripts/generate_functions.py @@ -13,13 +13,17 @@ import subprocess import argparse import sys import time +import ctypes.util, re # to check libclang version from pathlib import Path from parse import system_include_paths, clang_format_version REDC = "\033[91m" GREENC = "\033[92m" +YELLOWC = "\033[93m" ENDC = "\033[0m" +def yellow(msg): + return f"{YELLOWC}{msg}{ENDC}" def red(msg): return f"{REDC}{msg}{ENDC}" @@ -29,6 +33,25 @@ def green(msg): return f"{GREENC}{msg}{ENDC}" +def check_libclang_version(required="12"): + # Use already-loaded libclang, or let cindex resolve it + lib = ctypes.CDLL(cindex.Config.library_file or ctypes.util.find_library("clang")) + + # Get version string + lib.clang_getClangVersion.restype = ctypes.c_void_p + version_ptr = lib.clang_getClangVersion() + version_str = ctypes.cast(version_ptr, ctypes.c_char_p).value.decode() + + # Parse and check version + match = re.search(r"version\s+(\d+)", version_str) + if not match or match.group(1) != required: + msg = red(f"libclang version {match.group(1) if match else '?'} found, but version {required} required. Bye!") + print(msg) + sys.exit(1) + msg = green(f"Found libclang version {required}") + print(msg) + + def check_clang_format_version(required_version): if (ver := clang_format_version()) != required_version: msg = red( @@ -120,6 +143,24 @@ def time_return_lambda(node, args): def visit(node): + + loc = node.location + # skip if ndoe is outside project directory + if loc.file and not str(loc.file).startswith(str(cargs.build_path.parent)): + return + + ''' + # to see which file was causing the error (not in Detector.h, so skipping others in the above code) + try: + kind = node.kind + except ValueError as e: + loc = node.location + file_name = loc.file.name if loc.file else "" + msg = yellow(f"\nWarning: skipping node with unknown CursorKind id {node._kind_id} at {file_name}:{loc.line}:{loc.column}") + print(msg) + return + ''' + if node.kind == cindex.CursorKind.CLASS_DECL: if node.displayname == "Detector": for child in node.get_children(): @@ -161,6 +202,7 @@ if __name__ == "__main__": ) cargs = parser.parse_args() + check_libclang_version("12") check_clang_format_version(12) check_for_compile_commands_json(cargs.build_path) diff --git a/python/src/detector.cpp b/python/src/detector.cpp index f9e3821d7..a313f9acb 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -932,9 +932,9 @@ void init_det(py::module &m) { Detector::setRxArping, py::arg(), py::arg() = Positions{}); CppDetectorApi.def("getRxROI", - (std::vector (Detector::*)(int) const) & + (std::vector(Detector::*)(int) const) & Detector::getRxROI, - py::arg()); + py::arg() = -1); CppDetectorApi.def("setRxROI", (void (Detector::*)(const std::vector &)) & Detector::setRxROI, @@ -1349,10 +1349,11 @@ void init_det(py::module &m) { (void (Detector::*)(const int, const std::string &, sls::Positions)) & Detector::setVetoFile, py::arg(), py::arg(), py::arg() = Positions{}); - CppDetectorApi.def("getBurstMode", - (Result(Detector::*)(sls::Positions)) & - Detector::getBurstMode, - py::arg() = Positions{}); + CppDetectorApi.def( + "getBurstMode", + (Result(Detector::*)(sls::Positions) const) & + Detector::getBurstMode, + py::arg() = Positions{}); CppDetectorApi.def("setBurstMode", (void (Detector::*)(defs::burstMode, sls::Positions)) & Detector::setBurstMode, diff --git a/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer b/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer index e5f92d113..81663a4e9 100755 Binary files a/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer and b/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer differ diff --git a/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer b/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer index 71d124198..3e86577f8 100755 Binary files a/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer and b/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer differ diff --git a/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer b/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer index 68ed02a70..7056841be 100755 Binary files a/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer and b/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer differ diff --git a/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer b/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer index 2a154698e..c0bf925bb 100755 Binary files a/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer and b/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer differ diff --git a/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer b/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer index cbabe9aea..7e646f376 100755 Binary files a/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer and b/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer differ diff --git a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer index bc4c30552..d9bd19470 100755 Binary files a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer and b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer differ diff --git a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c index 89d38f569..39a5eca12 100644 --- a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c +++ b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c @@ -7255,7 +7255,8 @@ int get_receiver_parameters(int file_des) { // dynamic range ret = getDynamicRange(&i32); if (ret == FAIL) { - i32 = 0; + sprintf(mess, "Could not get dynamic range.\n"); + return sendError(file_des); } n += sendData(file_des, &i32, sizeof(i32), INT32); if (n < 0) @@ -7434,6 +7435,20 @@ int get_receiver_parameters(int file_des) { u32 = 0; #endif n += sendData(file_des, &u32, sizeof(u32), INT32); + if (n < 0) + return printSocketReadError(); + + // readout speed +#if !defined(CHIPTESTBOARDD) && !defined(XILINX_CHIPTESTBOARDD) + ret = getReadoutSpeed(&i32); + if (ret == FAIL) { + sprintf(mess, "Could not get readout speed.\n"); + return sendError(file_des); + } +#else + i32 = 0; +#endif + n += sendData(file_des, &i32, sizeof(i32), INT32); if (n < 0) return printSocketReadError(); diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer index 6e6d9a824..dcb78dedf 100755 Binary files a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer and b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer differ diff --git a/slsDetectorSoftware/generator/Caller.in.h b/slsDetectorSoftware/generator/Caller.in.h index 715922dfb..17ca1e3ef 100644 --- a/slsDetectorSoftware/generator/Caller.in.h +++ b/slsDetectorSoftware/generator/Caller.in.h @@ -1,5 +1,5 @@ // This file is used as input to generate the caller class - +#pragma once #include "CmdParser.h" #include "HelpDacs.h" #include "sls/Detector.h" diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index 743c80b77..89d183618 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -1449,7 +1449,7 @@ class Detector { Positions pos = {}); /** [Gotthard2] */ - Result getBurstMode(Positions pos = {}); + Result getBurstMode(Positions pos = {}) const; /** [Gotthard2] BURST_INTERNAL (default), BURST_EXTERNAL, * CONTINUOUS_INTERNAL, CONTINUOUS_EXTERNAL. Also changes clkdiv 2, 3, 4 */ diff --git a/slsDetectorSoftware/src/Caller.h b/slsDetectorSoftware/src/Caller.h index 068aca94c..12a665f79 100644 --- a/slsDetectorSoftware/src/Caller.h +++ b/slsDetectorSoftware/src/Caller.h @@ -1,5 +1,5 @@ // This file is used as input to generate the caller class - +#pragma once #include "CmdParser.h" #include "HelpDacs.h" #include "sls/Detector.h" diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index 8c95fbf63..6672bdafe 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -1883,7 +1883,7 @@ void Detector::setVetoFile(const int chipIndex, const std::string &fname, pimpl->Parallel(&Module::setVetoFile, pos, chipIndex, fname); } -Result Detector::getBurstMode(Positions pos) { +Result Detector::getBurstMode(Positions pos) const { return pimpl->Parallel(&Module::getBurstMode, pos); } diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 440386080..3876becab 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -657,9 +657,16 @@ void Module::setExptime(int gateIndex, int64_t value) { int64_t args[]{static_cast(gateIndex), value}; sendToDetector(F_SET_EXPTIME, args, nullptr); if (shm()->useReceiverFlag) { + // get exact value due to clk + if (shm()->detType == MYTHEN3 && gateIndex == -1) { + value = getExptime(0); // m3 does not support -1 + } else { + value = getExptime(gateIndex); // others only support -1 + } + args[1] = value; sendToReceiver(F_RECEIVER_SET_EXPTIME, args, nullptr); } - if (prevVal != value) { + if (shm()->detType == EIGER && prevVal != value) { updateRateCorrection(); } } @@ -671,6 +678,7 @@ int64_t Module::getPeriod() const { void Module::setPeriod(int64_t value) { sendToDetector(F_SET_PERIOD, value, nullptr); if (shm()->useReceiverFlag) { + value = getPeriod(); // get exact value due to clk sendToReceiver(F_RECEIVER_SET_PERIOD, value, nullptr); } } @@ -749,6 +757,9 @@ slsDetectorDefs::speedLevel Module::getReadoutSpeed() const { void Module::setReadoutSpeed(speedLevel value) { sendToDetector(F_SET_READOUT_SPEED, value, nullptr); + if (shm()->useReceiverFlag) { + sendToReceiver(F_SET_RECEIVER_READOUT_SPEED, value, nullptr); + } } int Module::getClockDivider(int clkIndex) const { @@ -1777,6 +1788,7 @@ void Module::setSubExptime(int64_t value) { } sendToDetector(F_SET_SUB_EXPTIME, value, nullptr); if (shm()->useReceiverFlag) { + value = getSubExptime(); // get exact value due to clk sendToReceiver(F_RECEIVER_SET_SUB_EXPTIME, value, nullptr); } if (prevVal != value) { @@ -1791,6 +1803,7 @@ int64_t Module::getSubDeadTime() const { void Module::setSubDeadTime(int64_t value) { sendToDetector(F_SET_SUB_DEADTIME, value, nullptr); if (shm()->useReceiverFlag) { + value = getSubDeadTime(); // get exact value due to clk sendToReceiver(F_RECEIVER_SET_SUB_DEADTIME, value, nullptr); } } @@ -2288,6 +2301,8 @@ void Module::setBurstMode(slsDetectorDefs::burstMode value) { sendToDetector(F_SET_BURST_MODE, value, nullptr); if (shm()->useReceiverFlag) { sendToReceiver(F_SET_RECEIVER_BURST_MODE, value, nullptr); + // changing burst mode may change exptime due to clk change + setExptime(-1, getExptime(-1)); // update exact exptime in receiver } } @@ -2379,6 +2394,9 @@ void Module::setGateDelay(int gateIndex, int64_t value) { int64_t args[]{static_cast(gateIndex), value}; sendToDetector(F_SET_GATE_DELAY, args, nullptr); if (shm()->useReceiverFlag) { + // get exact value due to clk + args[1] = + getGateDelay(gateIndex == -1 ? 0 : gateIndex); // m3 doesnt allow -1 sendToReceiver(F_SET_RECEIVER_GATE_DELAY, args, nullptr); } } diff --git a/slsDetectorSoftware/tests/CMakeLists.txt b/slsDetectorSoftware/tests/CMakeLists.txt index 6a4c03855..86fe37dc1 100755 --- a/slsDetectorSoftware/tests/CMakeLists.txt +++ b/slsDetectorSoftware/tests/CMakeLists.txt @@ -17,6 +17,8 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-xilinx-chiptestboard.cpp ${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 ${CMAKE_CURRENT_SOURCE_DIR}/test-Result.cpp @@ -34,10 +36,17 @@ if (SLS_USE_HDF5) ) endif (SLS_USE_HDF5) +target_compile_options(tests + PRIVATE + # Suppress warnings for deprecated functions in rapidjson + -Wno-deprecated-declarations +) + target_include_directories(tests PUBLIC "$" "$" + "$" PRIVATE ${SLS_INTERNAL_RAPIDJSON_DIR} ) \ No newline at end of file diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp new file mode 100644 index 000000000..79fc509af --- /dev/null +++ b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp @@ -0,0 +1,279 @@ +// 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/sls_detector_defs.h" +#include "sls/versionAPI.h" +#include "test-Caller-global.h" +#include "tests/globals.h" + +#include +#include + +namespace sls { + +using test::GET; +using test::PUT; + +TEST_CASE("jungfrau_or_moench_acquire_check_file_size", + "[.cmdcall][.cmdacquire]") { + + 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", "[.cmdcall][.cmdacquire]") { + 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", "[.cmdcall][.cmdacquire]") { + 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", "[.cmdcall][.cmdacquire]") { + 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)); +} + +TEST_CASE("ctb_acquire_check_file_size", "[.cmdcall][.cmdacquire]") { + Detector det; + Caller caller(&det); + auto det_type = + det.getDetectorType().tsquash("Inconsistent detector types to test"); + + 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)); + } + } +} + +} // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp index 955a30169..fe30c9d2d 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp @@ -17,119 +17,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("ctb_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::CHIPTESTBOARD || - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - testCtbAcquireInfo test_ctb_config; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - testCtbAcquireInfo test_ctb_config; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - testCtbAcquireInfo test_ctb_config; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - REQUIRE_NOTHROW(test_ctb_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - testCtbAcquireInfo test_ctb_config; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - testCtbAcquireInfo test_ctb_config; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - { - 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_acquire_with_receiver( - test_ctb_config, num_frames_to_acquire, det, caller)); - } - } -} - /* dacs */ TEST_CASE("dacname", "[.cmdcall]") { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp index 265c39510..ae6a6c824 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp @@ -17,70 +17,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("eiger_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::EIGER) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - get_common_acquire_config_state(det); - - // save previous specific det type config - auto exptime = det.getExptime().tsquash("inconsistent exptime to test"); - auto n_rows = - det.getReadNRows().tsquash("inconsistent number of rows to test"); - auto dynamic_range = - det.getDynamicRange().tsquash("inconsistent dynamic range to test"); - REQUIRE(false == - det.getTenGiga().tsquash("inconsistent 10Giga to test")); - - // defaults - int num_frames_to_acquire = 2; - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set default specific det type config - det.setExptime(std::chrono::microseconds{200}); - det.setReadNRows(256); - det.setDynamicRange(16); - - // acquire - test_acquire_with_receiver(caller, det); - - // check frames caught - test_frames_caught(det, 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; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - - // restore previous specific det type config - det.setExptime(exptime); - det.setReadNRows(n_rows); - det.setDynamicRange(dynamic_range); - } -} - /** temperature */ TEST_CASE("temp_fpgaext", "[.cmdcall]") { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp index 0c56ac15d..2d89dd97c 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp @@ -2,6 +2,7 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #include "test-Caller-global.h" #include "Caller.h" +#include "GeneralData.h" #include "catch.hpp" #include "sls/Detector.h" #include "sls/logger.h" @@ -148,20 +149,52 @@ void test_acquire_with_receiver(Caller &caller, const Detector &det) { REQUIRE_NOTHROW(caller.call("rx_stop", {}, -1, PUT)); } -testCommonDetAcquireInfo get_common_acquire_config_state(const Detector &det) { - return testCommonDetAcquireInfo{ - det.getTimingMode().tsquash("Inconsistent timing mode"), - det.getNumberOfFrames().tsquash("Inconsistent number of frames"), - det.getNumberOfTriggers().tsquash("Inconsistent number of triggers"), - det.getPeriod().tsquash("Inconsistent period")}; -} +void create_files_for_acquire(Detector &det, Caller &caller, int64_t num_frames, + std::optional test_info) { -void set_common_acquire_config_state( - Detector &det, const testCommonDetAcquireInfo &det_config_info) { - det.setTimingMode(det_config_info.timing_mode); - det.setNumberOfFrames(det_config_info.num_frames_to_acquire); - det.setNumberOfTriggers(det_config_info.num_triggers); - det.setPeriod(det_config_info.period); + // 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 + test_acquire_with_receiver(caller, det); + auto frames_caught = det.getFramesCaught().tsquash( + "Inconsistent number of frames caught")[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); + frames_caught = det.getFramesCaught().tsquash( + "Inconsistent number of frames caught")[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) { @@ -211,105 +244,30 @@ void set_ctb_config_state(Detector &det, det.setTransceiverEnableMask(ctb_config_info.transceiver_mask); } -uint64_t calculate_ctb_image_size(const testCtbAcquireInfo &test_info) { - uint64_t num_analog_bytes = 0, num_digital_bytes = 0, - num_transceiver_bytes = 0; - if (test_info.readout_mode == defs::ANALOG_ONLY || - test_info.readout_mode == defs::ANALOG_AND_DIGITAL) { - uint32_t adc_enable_mask = - (test_info.ten_giga ? test_info.adc_enable_10g - : test_info.adc_enable_1g); - int num_analog_chans = __builtin_popcount(adc_enable_mask); - const int num_bytes_per_sample = 2; - num_analog_bytes = - num_analog_chans * num_bytes_per_sample * test_info.num_adc_samples; - LOG(logDEBUG1) << "[Analog Databytes: " << num_analog_bytes << ']'; - } +std::pair +calculate_ctb_image_size(const testCtbAcquireInfo &test_info, + bool isXilinxCtb) { - // digital channels - if (test_info.readout_mode == defs::DIGITAL_ONLY || - test_info.readout_mode == defs::ANALOG_AND_DIGITAL || - test_info.readout_mode == defs::DIGITAL_AND_TRANSCEIVER) { - int num_digital_samples = test_info.num_dbit_samples; - if (test_info.dbit_offset > 0) { - uint64_t num_digital_bytes_reserved = - num_digital_samples * sizeof(uint64_t); - num_digital_bytes_reserved -= test_info.dbit_offset; - num_digital_samples = num_digital_bytes_reserved / sizeof(uint64_t); - } - int num_digital_chans = test_info.dbit_list.size(); - if (num_digital_chans == 0) { - num_digital_chans = 64; - } - if (!test_info.dbit_reorder) { - uint32_t num_bits_per_sample = num_digital_chans; - if (num_bits_per_sample % 8 != 0) { - num_bits_per_sample += (8 - (num_bits_per_sample % 8)); - } - num_digital_bytes = (num_bits_per_sample / 8) * num_digital_samples; - } else { - uint32_t num_bits_per_bit = num_digital_samples; - if (num_bits_per_bit % 8 != 0) { - num_bits_per_bit += (8 - (num_bits_per_bit % 8)); - } - num_digital_bytes = num_digital_chans * (num_bits_per_bit / 8); - } - LOG(logDEBUG1) << "[Digital Databytes: " << num_digital_bytes << ']'; - } - // transceiver channels - if (test_info.readout_mode == defs::TRANSCEIVER_ONLY || - test_info.readout_mode == defs::DIGITAL_AND_TRANSCEIVER) { - int num_transceiver_chans = - __builtin_popcount(test_info.transceiver_mask); - const int num_bytes_per_channel = 8; - num_transceiver_bytes = num_transceiver_chans * num_bytes_per_channel * - test_info.num_trans_samples; - LOG(logDEBUG1) << "[Transceiver Databytes: " << num_transceiver_bytes - << ']'; + sls::CtbImageInputs inputs{}; + inputs.nAnalogSamples = test_info.num_adc_samples; + inputs.adcMask = test_info.adc_enable_10g; + if (!isXilinxCtb && !test_info.ten_giga) { + inputs.adcMask = test_info.adc_enable_1g; } + inputs.nTransceiverSamples = test_info.num_trans_samples; + inputs.transceiverMask = test_info.transceiver_mask; + inputs.nDigitalSamples = test_info.num_dbit_samples; + inputs.dbitOffset = test_info.dbit_offset; + inputs.dbitList = test_info.dbit_list; + inputs.dbitReorder = test_info.dbit_reorder; + auto out = computeCtbImageSize(inputs); uint64_t image_size = - num_analog_bytes + num_digital_bytes + num_transceiver_bytes; + out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes; LOG(logDEBUG1) << "Expected image size: " << image_size; - return image_size; -} - -void test_ctb_acquire_with_receiver(const testCtbAcquireInfo &test_info, - int64_t num_frames_to_acquire, - Detector &det, Caller &caller) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - // overwrite exptime if not using virtual ctb server - get_common_acquire_config_state(det); - testCtbAcquireInfo prev_ctb_config_info = get_ctb_config_state(det); - - // defaults - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set ctb config - set_ctb_config_state(det, test_info); - - // acquire - REQUIRE_NOTHROW(test_acquire_with_receiver(caller, det)); - - // check frames caught - REQUIRE_NOTHROW(test_frames_caught(det, num_frames_to_acquire)); - - // check file size (assuming local pc) - uint64_t expected_image_size = calculate_ctb_image_size(test_info); - REQUIRE_NOTHROW(test_acquire_binary_file_size( - test_file_info, num_frames_to_acquire, expected_image_size)); - - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - set_ctb_config_state(det, prev_ctb_config_info); + int npixelx = out.nPixelsX; + LOG(logDEBUG1) << "Expected number of pixels in x: " << npixelx; + return std::make_pair(image_size, npixelx); } } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.h b/slsDetectorSoftware/tests/Caller/test-Caller-global.h index be944ecd2..34bdb948c 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.h +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.h @@ -2,12 +2,13 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once -class Caller; +#include "Caller.h" #include "sls/Detector.h" #include "sls/sls_detector_defs.h" #include #include +#include #include namespace sls { @@ -18,13 +19,14 @@ struct testFileInfo { bool file_write{true}; bool file_overwrite{true}; slsDetectorDefs::fileFormat file_format{slsDetectorDefs::BINARY}; -}; - -struct testCommonDetAcquireInfo { - slsDetectorDefs::timingMode timing_mode{slsDetectorDefs::AUTO_TIMING}; - int64_t num_frames_to_acquire{2}; - int64_t num_triggers{1}; - std::chrono::nanoseconds period{std::chrono::milliseconds{2}}; + 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"; + } }; struct testCtbAcquireInfo { @@ -60,16 +62,14 @@ void test_frames_caught(const Detector &det, int num_frames_to_acquire); void test_acquire_with_receiver(Caller &caller, const Detector &det); -testCommonDetAcquireInfo get_common_acquire_config_state(const Detector &det); -void set_common_acquire_config_state( - Detector &det, const testCommonDetAcquireInfo &det_config_info); +void create_files_for_acquire( + Detector &det, Caller &caller, int64_t num_frames = 1, + 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); -uint64_t calculate_ctb_image_size(const testCtbAcquireInfo &test_info); -void test_ctb_acquire_with_receiver(const testCtbAcquireInfo &test_info, - int64_t num_frames_to_acquire, - Detector &det, Caller &caller); +std::pair +calculate_ctb_image_size(const testCtbAcquireInfo &test_info, bool isXilinxCtb); } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp index c56e79634..54bba5ce2 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp @@ -17,69 +17,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("gotthard2_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::GOTTHARD2) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - get_common_acquire_config_state(det); - - // save previous specific det type config - auto exptime = det.getExptime().tsquash("inconsistent exptime to test"); - auto burst_mode = - det.getBurstMode().tsquash("inconsistent burst mode to test"); - auto number_of_bursts = det.getNumberOfBursts().tsquash( - "inconsistent number of bursts to test"); - auto burst_period = - det.getBurstPeriod().tsquash("inconsistent burst period to test"); - - // defaults - int num_frames_to_acquire = 2; - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set default specific det type config - det.setExptime(std::chrono::microseconds{200}); - det.setBurstMode(defs::CONTINUOUS_EXTERNAL); - det.setNumberOfBursts(1); - det.setBurstPeriod(std::chrono::milliseconds{0}); - - // acquire - test_acquire_with_receiver(caller, det); - - // check frames caught - test_frames_caught(det, 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; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - - // restore previous specific det type config - det.setExptime(exptime); - det.setBurstMode(burst_mode); - det.setNumberOfBursts(number_of_bursts); - det.setBurstPeriod(burst_period); - } -} - // time specific measurements for gotthard2 TEST_CASE("timegotthard2", "[.cmdcall]") { Detector det; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp index 07cabaa04..4563cd32f 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp @@ -15,65 +15,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("jungfrau_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::JUNGFRAU) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - get_common_acquire_config_state(det); - - // save previous specific det type config - auto exptime = det.getExptime().tsquash("inconsistent exptime to test"); - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - auto n_rows = - det.getReadNRows().tsquash("inconsistent number of rows to test"); - - // defaults - int num_frames_to_acquire = 2; - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set default specific det type config - det.setExptime(std::chrono::microseconds{200}); - det.setReadNRows(512); - - // acquire - test_acquire_with_receiver(caller, det); - - // check frames caught - test_frames_caught(det, 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; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - - // restore previous specific det type config - det.setExptime(exptime); - det.setReadNRows(n_rows); - } -} - /* dacs */ TEST_CASE("Setting and reading back Jungfrau dacs", "[.cmdcall][.dacs]") { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp new file mode 100644 index 000000000..be1ed74b0 --- /dev/null +++ b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp @@ -0,0 +1,1072 @@ +// 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 "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 +#include +#include +#include +#include +#include + +#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"); + 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"); + 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}; + 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"); + } + + 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; + } + + 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"); + + 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"); + + 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")); + + 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"); + + 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); + + 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; + } + } + } + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + + 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) { + 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"); + + 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"); + + 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")); + + 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"); + + 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"); + + 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; + + 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")); + + 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()); + + 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")); + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + + 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; + } + + 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)); + + 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; + + 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); + + 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; + + 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; + + 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; + + 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); + } + + 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; + + 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); + + 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; + 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) { + test_master_file_common_metadata(det, doc); + // Jungfrau specific metadata + test_master_file_rois(det, doc); + test_master_file_exptime(det, doc); + test_master_file_period(det, doc); + test_master_file_num_udp_interfaces(det, doc); + test_master_file_read_n_rows(det, doc); + test_master_file_readout_speed(det, doc); +} + +void test_master_file_eiger_metadata(const Detector &det, + const std::optional &doc) { + test_master_file_common_metadata(det, doc); + // Eiger specific metadata + test_master_file_rois(det, doc); + test_master_file_dynamic_range(det, doc); + test_master_file_ten_giga(det, doc); + test_master_file_exptime(det, doc); + test_master_file_period(det, doc); + test_master_file_threshold_energy(det, doc); + test_master_file_sub_exptime(det, doc); + test_master_file_sub_period(det, doc); + test_master_file_quad(det, doc); + test_master_file_read_n_rows(det, doc); + test_master_file_rate_corrections(det, doc); + test_master_file_readout_speed(det, doc); +} + +void test_master_file_moench_metadata(const Detector &det, + const std::optional &doc) { + test_master_file_common_metadata(det, doc); + // Moench specific metadata + test_master_file_rois(det, doc); + test_master_file_exptime(det, doc); + test_master_file_period(det, doc); + test_master_file_num_udp_interfaces(det, doc); + test_master_file_read_n_rows(det, doc); + test_master_file_readout_speed(det, doc); +} + +void test_master_file_mythen3_metadata(const Detector &det, + const std::optional &doc) { + test_master_file_common_metadata(det, doc); + // Mythen3 specific metadata + test_master_file_rois(det, doc); + test_master_file_dynamic_range(det, doc); + test_master_file_ten_giga(det, doc); + test_master_file_period(det, doc); + test_master_file_counter_mask(det, doc); + test_master_file_exptimes(det, doc); + test_master_file_gate_delays(det, doc); + test_master_file_gates(det, doc); + test_master_file_threadhold_energies(det, doc); + test_master_file_readout_speed(det, doc); +} + +void test_master_file_gotthard2_metadata(const Detector &det, + const std::optional &doc) { + test_master_file_common_metadata(det, doc); + // Gotthard2 specific metadata + test_master_file_exptime(det, doc); + test_master_file_period(det, doc); + test_master_file_burst_mode(det, doc); + 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(); + test_master_file_common_metadata(det, doc); + // Ctb specific metadata + test_master_file_exptime(det, doc); + test_master_file_period(det, doc); + if (det_type == defs::CHIPTESTBOARD) + test_master_file_ten_giga(det, doc); + test_master_file_adc_mask(det, doc); + test_master_file_analog_flag(det, doc); + test_master_file_analog_samples(det, doc); + test_master_file_digital_flag(det, doc); + test_master_file_digital_samples(det, doc); + test_master_file_dbit_offset(det, doc); + test_master_file_dbit_reorder(det, doc); + test_master_file_dbit_bitset(det, doc); + test_master_file_transceiver_mask(det, doc); + test_master_file_transceiver_flag(det, doc); + 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 == 0) { + 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 != 0); + 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 + +TEST_CASE("check_master_file_attributes", "[.cmdcall][.cmdacquire][.cmdattr]") { + + Detector det; + Caller caller(&det); + auto det_type = + det.getDetectorType().tsquash("Inconsistent detector types to test"); + + int64_t num_frames = 1; + switch (det_type) { + case defs::EIGER: + case defs::JUNGFRAU: + 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"); + } + + testFileInfo file_info; + std::string master_file_prefix = file_info.getMasterFileNamePrefix(); + + // 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); + + // 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); + } catch (H5::Exception &e) { + LOG(logERROR) << "HDF5 error: " << e.getDetailMsg(); + throw; + } +#endif +} + +} // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp index 9c79a51ab..56353f44b 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp @@ -15,66 +15,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("moench_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::MOENCH) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - get_common_acquire_config_state(det); - - // save previous specific det type config - auto exptime = det.getExptime().tsquash("inconsistent exptime to test"); - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - auto n_rows = - det.getReadNRows().tsquash("inconsistent number of rows to test"); - - // defaults - int num_frames_to_acquire = 2; - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set default specific det type config - det.setExptime(std::chrono::microseconds{200}); - det.setReadNRows(400); - - // acquire - test_acquire_with_receiver(caller, det); - - // check frames caught - test_frames_caught(det, 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; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - - // restore previous specific det type config - det.setExptime(exptime); - det.setReadNRows(n_rows); - } -} - /* dacs */ TEST_CASE("Setting and reading back moench dacs", "[.cmdcall][.dacs]") { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp index c5d644b3d..16295a3e1 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp @@ -17,74 +17,6 @@ namespace sls { using test::GET; using test::PUT; -TEST_CASE("mythen3_acquire_check_file_size", "[.cmdcall]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::MYTHEN3) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - testCommonDetAcquireInfo prev_det_config_info = - get_common_acquire_config_state(det); - - // save previous specific det type config - auto exptime = - det.getExptimeForAllGates().tsquash("inconsistent exptime to test"); - auto dynamic_range = - det.getDynamicRange().tsquash("inconsistent dynamic range to test"); - uint32_t counter_mask = - det.getCounterMask().tsquash("inconsistent counter mask to test"); - - // defaults - int num_frames_to_acquire = 2; - testFileInfo test_file_info; - set_file_state(det, test_file_info); - testCommonDetAcquireInfo det_config; - det_config.num_frames_to_acquire = num_frames_to_acquire; - set_common_acquire_config_state(det, det_config); - - // set default specific det type config - det.setExptime(-1, std::chrono::microseconds{200}); - int test_dynamic_range = 16; - det.setDynamicRange(test_dynamic_range); - int test_counter_mask = 0x3; - int num_counters = __builtin_popcount(test_counter_mask); - det.setCounterMask(test_counter_mask); - - // acquire - test_acquire_with_receiver(caller, det); - - // check frames caught - test_frames_caught(det, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = test_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; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - - // restore previous state - set_file_state(det, prev_file_info); - set_common_acquire_config_state(det, prev_det_config_info); - - // restore previous specific det type config - for (int iGate = 0; iGate < 3; ++iGate) { - det.setExptime(iGate, exptime[iGate]); - } - det.setDynamicRange(dynamic_range); - det.setCounterMask(counter_mask); - } -} - /* dacs */ TEST_CASE("Setting and reading back MYTHEN3 dacs", "[.cmdcall][.dacs]") { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index 51fe04899..de1b795d8 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -738,43 +738,19 @@ TEST_CASE("rx_roi", "[.cmdcall]") { // check master file creation // TODO: check roi in master file { - auto prev_write = det.getFileWrite().tsquash( - "inconsistent file write values in test"); - auto prev_path = det.getFilePath().tsquash( - "inconsistent file path values in test"); - auto prev_format = det.getFileFormat().tsquash( - "inconsistent file format values in test"); - auto prev_index = det.getAcquisitionIndex().tsquash( - "inconsistent file index values in test"); - auto prev_fname = det.getFileNamePrefix().tsquash( - "inconsistent file name prefix values in test"); - - det.setFileWrite(true); - det.setFilePath("/tmp"); - det.setFileNamePrefix("test"); - - det.setAcquisitionIndex(0); - det.setFileFormat(defs::BINARY); - REQUIRE_NOTHROW(caller.call("acquire", {}, -1, PUT)); - std::string file_path = "/tmp/test_master_0.json"; - REQUIRE(std::filesystem::exists(file_path) == true); + create_files_for_acquire(det, caller); + testFileInfo file_info; + std::string master_file_prefix = + file_info.getMasterFileNamePrefix(); + std::string fname = master_file_prefix + ".json"; + REQUIRE(std::filesystem::exists(fname) == true); #ifdef HDF5C - det.setAcquisitionIndex(0); - det.setFileFormat(defs::HDF5); - REQUIRE_NOTHROW(caller.call("acquire", {}, -1, PUT)); - file_path = "/tmp/test_master_0.h5"; - REQUIRE(std::filesystem::exists(file_path) == true); - file_path = "/tmp/test_virtual_0.h5"; - REQUIRE(std::filesystem::exists(file_path) == true); + fname = master_file_prefix + ".h5"; + REQUIRE(std::filesystem::exists(fname) == true); + fname = file_info.getVirtualFileName(); + REQUIRE(std::filesystem::exists(fname) == true); #endif - - det.setFileWrite(prev_write); - if (!prev_path.empty()) - det.setFilePath(prev_path); - det.setFileFormat(prev_format); - det.setAcquisitionIndex(prev_index); - det.setFileNamePrefix(prev_fname); } for (int i = 0; i != det.size(); ++i) { @@ -801,7 +777,11 @@ TEST_CASE("rx_clearroi", "[.cmdcall]") { REQUIRE(oss.str() == "rx_clearroi successful\n"); } for (int i = 0; i != det.size(); ++i) { - det.setRxROI(prev_val); + if (prev_val.size() == 1 && prev_val[0].completeRoi()) { + det.clearRxROI(); + } else { + det.setRxROI(prev_val); + } } } } diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index 902870a8f..41c54e2e5 100644 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -221,7 +221,7 @@ int ClientInterface::functionTable(){ flist[F_GET_RECEIVER_DBIT_REORDER] = &ClientInterface::get_dbit_reorder; flist[F_SET_RECEIVER_DBIT_REORDER] = &ClientInterface::set_dbit_reorder; flist[F_RECEIVER_GET_ROI_METADATA] = &ClientInterface::get_roi_metadata; - + flist[F_SET_RECEIVER_READOUT_SPEED] = &ClientInterface::set_readout_speed; for (int i = NUM_DET_FUNCTIONS + 1; i < NUM_REC_FUNCTIONS ; i++) { LOG(logDEBUG1) << "function fnum: " << i << " (" << @@ -411,6 +411,8 @@ int ClientInterface::setup_receiver(Interface &socket) { impl()->setReadoutMode(arg.roMode); impl()->setTenGigaADCEnableMask(arg.adc10gMask); impl()->setTransceiverEnableMask(arg.transceiverMask); + } else { + impl()->setReadoutSpeed(arg.readoutSpeed); } if (detType == CHIPTESTBOARD) { impl()->setADCEnableMask(arg.adcMask); @@ -1851,4 +1853,33 @@ int ClientInterface::get_roi_metadata(Interface &socket) { return OK; } +int ClientInterface::set_readout_speed(Interface &socket) { + auto value = socket.Receive(); + verifyIdle(socket); + switch (detType) { + case GOTTHARD2: + if (value != G2_108MHZ && value != G2_144MHZ) + throw RuntimeError("Invalid readout speed for GOTTHARD2: " + + std::to_string(value)); + break; + + case EIGER: + case JUNGFRAU: + case MYTHEN3: + case MOENCH: + if (value < 0 || value > QUARTER_SPEED) { + throw RuntimeError("Invalid readout speed: " + + std::to_string(value)); + } + break; + + default: + functionNotImplemented(); + } + + LOG(logDEBUG1) << "Setting readout speed to " << value; + impl()->setReadoutSpeed(static_cast(value)); + return socket.Send(OK); +} + } // namespace sls diff --git a/slsReceiverSoftware/src/ClientInterface.h b/slsReceiverSoftware/src/ClientInterface.h index e17a3ab85..f2dbaa764 100644 --- a/slsReceiverSoftware/src/ClientInterface.h +++ b/slsReceiverSoftware/src/ClientInterface.h @@ -167,6 +167,7 @@ class ClientInterface : private virtual slsDetectorDefs { int get_dbit_reorder(ServerInterface &socket); int set_dbit_reorder(ServerInterface &socket); int get_roi_metadata(ServerInterface &socket); + int set_readout_speed(ServerInterface &socket); Implementation *impl() { if (receiver != nullptr) { diff --git a/slsReceiverSoftware/src/GeneralData.h b/slsReceiverSoftware/src/GeneralData.h index 34a329542..d60c3b726 100644 --- a/slsReceiverSoftware/src/GeneralData.h +++ b/slsReceiverSoftware/src/GeneralData.h @@ -18,6 +18,110 @@ namespace sls { +struct CtbImageInputs { + slsDetectorDefs::readoutMode mode{slsDetectorDefs::ANALOG_ONLY}; + + int nAnalogSamples{}; + uint32_t adcMask{}; + int nTransceiverSamples{}; + uint32_t transceiverMask{}; + int nDigitalSamples{}; + int dbitOffset{}; + bool dbitReorder{}; + std::vector dbitList{}; +}; + +struct CtbImageOutputs { + int nAnalogBytes{}; + int nDigitalBytes{}; + int nDigitalBytesReserved{}; // including dbit offset and for 64 bits + int nTransceiverBytes{}; + int nPixelsX{}; +}; + +inline CtbImageOutputs computeCtbImageSize(const CtbImageInputs &in) { + CtbImageOutputs out{}; + + constexpr int num_bytes_per_analog_channel = 2; + constexpr int num_bytes_per_transceiver_channel = 8; + constexpr int max_digital_channels = 64; + + // analog channels (normal, analog/digital readout) + if (in.mode == slsDetectorDefs::ANALOG_ONLY || + in.mode == slsDetectorDefs::ANALOG_AND_DIGITAL) { + int nAnalogChans = __builtin_popcount(in.adcMask); + + out.nPixelsX += nAnalogChans; + out.nAnalogBytes = + nAnalogChans * num_bytes_per_analog_channel * in.nAnalogSamples; + LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans + << " Databytes: " << out.nAnalogBytes; + } + + // digital channels + if (in.mode == slsDetectorDefs::DIGITAL_ONLY || + in.mode == slsDetectorDefs::ANALOG_AND_DIGITAL || + in.mode == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { + + int nSamples = in.nDigitalSamples; + { + // allocate enought for 64 bits and dbit offset for now + // TODO: to be replaced in the future with the actual reserved and + // used + int32_t num_bytes_per_bit = + (nSamples % 8 == 0) ? (nSamples / 8) : (nSamples / 8 + 1); + out.nDigitalBytesReserved = + max_digital_channels * num_bytes_per_bit; + LOG(logDEBUG1) << "Number of Digital Channels:" + << max_digital_channels << " Databytes reserved: " + << out.nDigitalBytesReserved; + } + + // remove offset + if (in.dbitOffset > 0) { + int nBytesReserved = out.nDigitalBytesReserved - in.dbitOffset; + nSamples = nBytesReserved / sizeof(uint64_t); + } + // calculate channels + int nChans = in.dbitList.size(); + if (nChans == 0) { + nChans = max_digital_channels; + } + out.nPixelsX += nChans; + + // calculate actual bytes + if (!in.dbitReorder) { + uint32_t nBitsPerSample = nChans; + if (nBitsPerSample % 8 != 0) { + nBitsPerSample += (8 - (nBitsPerSample % 8)); + } + out.nDigitalBytes = (nBitsPerSample / 8) * nSamples; + } else { + uint32_t nBitsPerSignal = nSamples; + if (nBitsPerSignal % 8 != 0) { + nBitsPerSignal += (8 - (nBitsPerSignal % 8)); + } + out.nDigitalBytes = nChans * (nBitsPerSignal / 8); + } + LOG(logDEBUG1) << "Number of Actual Digital Channels:" << nChans + << " Databytes: " << out.nDigitalBytes; + } + + // transceiver channels + if (in.mode == slsDetectorDefs::TRANSCEIVER_ONLY || + in.mode == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { + int nTransceiverChans = __builtin_popcount(in.transceiverMask); + + out.nPixelsX += nTransceiverChans; + out.nTransceiverBytes = nTransceiverChans * + num_bytes_per_transceiver_channel * + in.nTransceiverSamples; + LOG(logDEBUG1) << "Number of Transceiver Channels:" << nTransceiverChans + << " Databytes: " << out.nTransceiverBytes; + } + return out; +} + class GeneralData { public: @@ -62,7 +166,8 @@ class GeneralData { uint32_t transceiverMask{0}; slsDetectorDefs::frameDiscardPolicy frameDiscardMode{ slsDetectorDefs::NO_DISCARD}; - + /* actual image size after ctboffset and ctbreorder */ + uint32_t actualImageSize{0}; GeneralData(){}; virtual ~GeneralData(){}; @@ -196,6 +301,7 @@ class EigerData : public GeneralData { dataSize = (tengigaEnable ? 4096 : 1024); packetSize = headerSizeinPacket + dataSize; imageSize = int(nPixelsX * nPixelsY * GetPixelDepth()); + actualImageSize = imageSize; packetsPerFrame = imageSize / dataSize; fifoDepth = (dynamicRange == 32 ? 100 : 1000); }; @@ -226,6 +332,7 @@ class JungfrauData : public GeneralData { nPixelsX = (256 * 4); nPixelsY = (256 * 2) / numUDPInterfaces; imageSize = int(nPixelsX * nPixelsY * GetPixelDepth()); + actualImageSize = imageSize; packetsPerFrame = imageSize / dataSize; udpSocketBufferSize = (1000 * 1024 * 1024) / numUDPInterfaces; }; @@ -257,6 +364,7 @@ class MoenchData : public GeneralData { nPixelsX = (400); nPixelsY = (400) / numUDPInterfaces; imageSize = int(nPixelsX * nPixelsY * GetPixelDepth()); + actualImageSize = imageSize; packetsPerFrame = imageSize / dataSize; udpSocketBufferSize = (1000 * 1024 * 1024) / numUDPInterfaces; }; @@ -308,6 +416,7 @@ class Mythen3Data : public GeneralData { nPixelsX = (NCHAN * ncounters); // max 1280 channels x 3 counters LOG(logINFO) << "nPixelsX: " << nPixelsX; imageSize = nPixelsX * nPixelsY * GetPixelDepth(); + actualImageSize = imageSize; // 10g if (tengigaEnable) { @@ -374,6 +483,7 @@ class Gotthard2Data : public GeneralData { void UpdateImageSize() { packetSize = headerSizeinPacket + dataSize; imageSize = int(nPixelsX * nPixelsY * GetPixelDepth()); + actualImageSize = imageSize; packetsPerFrame = imageSize / dataSize; vetoPacketSize = vetoHsize + vetoDataSize; vetoImageSize = vetoDataSize * packetsPerFrame; @@ -384,8 +494,6 @@ class Gotthard2Data : public GeneralData { class ChipTestBoardData : public GeneralData { private: const int NCHAN_DIGITAL = 64; - const int NUM_BYTES_PER_ANALOG_CHANNEL = 2; - const int NUM_BYTES_PER_TRANSCEIVER_CHANNEL = 8; int nAnalogBytes = 0; int nDigitalBytes = 0; int nTransceiverBytes = 0; @@ -394,7 +502,7 @@ class ChipTestBoardData : public GeneralData { /** Constructor */ ChipTestBoardData() { detType = slsDetectorDefs::CHIPTESTBOARD; - nPixelsY = 1; // number of samples + nPixelsY = 1; headerSizeinPacket = sizeof(slsDetectorDefs::sls_detector_header); frameIndexMask = 0xFFFFFF; // 10g frameIndexOffset = 8; // 10g @@ -404,29 +512,29 @@ class ChipTestBoardData : public GeneralData { standardheader = true; ctbDbitReorder = true; UpdateImageSize(); - }; + } public: - int GetNumberOfAnalogDatabytes() { return nAnalogBytes; }; + int GetNumberOfAnalogDatabytes() { return nAnalogBytes; } - int GetNumberOfDigitalDatabytes() { return nDigitalBytes; }; + int GetNumberOfDigitalDatabytes() { return nDigitalBytes; } - int GetNumberOfTransceiverDatabytes() { return nTransceiverBytes; }; + int GetNumberOfTransceiverDatabytes() { return nTransceiverBytes; } void SetNumberOfAnalogSamples(int n) { nAnalogSamples = n; UpdateImageSize(); - }; + } void SetNumberOfDigitalSamples(int n) { nDigitalSamples = n; UpdateImageSize(); - }; + } void SetNumberOfTransceiverSamples(int n) { nTransceiverSamples = n; UpdateImageSize(); - }; + } void SetctbDbitOffset(const int value) { ctbDbitOffset = value; } @@ -437,83 +545,60 @@ class ChipTestBoardData : public GeneralData { void SetOneGigaAdcEnableMask(int n) { adcEnableMaskOneGiga = n; UpdateImageSize(); - }; + } void SetTenGigaAdcEnableMask(int n) { adcEnableMaskTenGiga = n; UpdateImageSize(); - }; + } void SetTransceiverEnableMask(int n) { transceiverMask = n; UpdateImageSize(); - }; + } void SetReadoutMode(slsDetectorDefs::readoutMode r) { readoutType = r; UpdateImageSize(); - }; + } void SetTenGigaEnable(bool tg) { tengigaEnable = tg; UpdateImageSize(); - }; + } private: void UpdateImageSize() { - nAnalogBytes = 0; - nDigitalBytes = 0; - nTransceiverBytes = 0; - int nAnalogChans = 0, nDigitalChans = 0, nTransceiverChans = 0; - uint64_t digital_bytes_reserved = 0; + // used in calculations so cant remove now - TODO: remove later + nDigitalBytes = sizeof(uint64_t) * nDigitalSamples; - // analog channels (normal, analog/digital readout) - if (readoutType == slsDetectorDefs::ANALOG_ONLY || - readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL) { - uint32_t adcEnableMask = - (tengigaEnable ? adcEnableMaskTenGiga : adcEnableMaskOneGiga); - nAnalogChans = __builtin_popcount(adcEnableMask); + // calculate image size + CtbImageInputs inputs{}; + inputs.nAnalogSamples = nAnalogSamples; + inputs.adcMask = + tengigaEnable ? adcEnableMaskTenGiga : adcEnableMaskOneGiga; + inputs.nTransceiverSamples = nTransceiverSamples; + inputs.transceiverMask = transceiverMask; + inputs.nDigitalSamples = nDigitalSamples; + inputs.dbitOffset = ctbDbitOffset; + inputs.dbitList = ctbDbitList; + inputs.dbitReorder = ctbDbitReorder; + auto out = computeCtbImageSize(inputs); + nPixelsX = out.nPixelsX; + imageSize = out.nAnalogBytes + out.nDigitalBytesReserved + + out.nTransceiverBytes; + // to write to file: after ctb offset and reorder + actualImageSize = + out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes; + LOG(logDEBUG1) << "Actual image size: " << actualImageSize; - nAnalogBytes = - nAnalogChans * NUM_BYTES_PER_ANALOG_CHANNEL * nAnalogSamples; - LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans - << " Databytes: " << nAnalogBytes; - } - // digital channels - if (readoutType == slsDetectorDefs::DIGITAL_ONLY || - readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL || - readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { - nDigitalChans = NCHAN_DIGITAL; - // allocate enough memory to support reordering of digital bits - uint32_t num_bytes_per_bit = (nDigitalSamples % 8 == 0) - ? nDigitalSamples / 8 - : nDigitalSamples / 8 + 1; - digital_bytes_reserved = 64 * num_bytes_per_bit; - nDigitalBytes = sizeof(uint64_t) * nDigitalSamples; - LOG(logDEBUG1) << "Number of Digital Channels:" << nDigitalChans - << " Databytes: " << nDigitalBytes; - } - // transceiver channels - if (readoutType == slsDetectorDefs::TRANSCEIVER_ONLY || - readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { - nTransceiverChans = __builtin_popcount(transceiverMask); - ; - nTransceiverBytes = nTransceiverChans * - NUM_BYTES_PER_TRANSCEIVER_CHANNEL * - nTransceiverSamples; - LOG(logDEBUG1) << "Number of Transceiver Channels:" - << nTransceiverChans - << " Databytes: " << nTransceiverBytes; - } - nPixelsX = nAnalogChans + nDigitalChans + nTransceiverChans; + // calculate network parameters dataSize = tengigaEnable ? 8144 : UDP_PACKET_DATA_BYTES; packetSize = headerSizeinPacket + dataSize; - imageSize = nAnalogBytes + digital_bytes_reserved + nTransceiverBytes; packetsPerFrame = ceil((double)imageSize / (double)dataSize); - LOG(logDEBUG1) << "Total Number of Channels:" << nPixelsX << " Databytes: " << imageSize; - }; + } }; class XilinxChipTestBoardData : public GeneralData { @@ -572,11 +657,6 @@ class XilinxChipTestBoardData : public GeneralData { void SetctbDbitReorder(const bool value) { ctbDbitReorder = value; } - void SetOneGigaAdcEnableMask(int n) { - adcEnableMaskOneGiga = n; - UpdateImageSize(); - }; - void SetTenGigaAdcEnableMask(int n) { adcEnableMaskTenGiga = n; UpdateImageSize(); @@ -594,53 +674,30 @@ class XilinxChipTestBoardData : public GeneralData { private: void UpdateImageSize() { - nAnalogBytes = 0; - nDigitalBytes = 0; - nTransceiverBytes = 0; - int nAnalogChans = 0, nDigitalChans = 0, nTransceiverChans = 0; - uint64_t digital_bytes_reserved = 0; + // used in calculations so cant remove now - TODO: remove later + nDigitalBytes = sizeof(uint64_t) * nDigitalSamples; - // analog channels (normal, analog/digital readout) - if (readoutType == slsDetectorDefs::ANALOG_ONLY || - readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL) { - uint32_t adcEnableMask = adcEnableMaskTenGiga; - nAnalogChans = __builtin_popcount(adcEnableMask); + // calculate image size + CtbImageInputs inputs{}; + inputs.nAnalogSamples = nAnalogSamples; + inputs.adcMask = adcEnableMaskTenGiga; + inputs.nTransceiverSamples = nTransceiverSamples; + inputs.transceiverMask = transceiverMask; + inputs.nDigitalSamples = nDigitalSamples; + inputs.dbitOffset = ctbDbitOffset; + inputs.dbitList = ctbDbitList; + inputs.dbitReorder = ctbDbitReorder; + auto out = computeCtbImageSize(inputs); + nPixelsX = out.nPixelsX; + imageSize = out.nAnalogBytes + out.nDigitalBytesReserved + + out.nTransceiverBytes; + // to write to file: after ctb offset and reorder + actualImageSize = + out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes; + LOG(logDEBUG1) << "Actual image size: " << actualImageSize; - nAnalogBytes = - nAnalogChans * NUM_BYTES_PER_ANALOG_CHANNEL * nAnalogSamples; - LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans - << " Databytes: " << nAnalogBytes; - } - // digital channels - if (readoutType == slsDetectorDefs::DIGITAL_ONLY || - readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL || - readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { - nDigitalChans = NCHAN_DIGITAL; - uint32_t num_bytes_per_bit = (nDigitalSamples % 8 == 0) - ? nDigitalSamples / 8 - : nDigitalSamples / 8 + 1; - digital_bytes_reserved = 64 * num_bytes_per_bit; - nDigitalBytes = sizeof(uint64_t) * nDigitalSamples; - LOG(logDEBUG1) << "Number of Digital Channels:" << nDigitalChans - << " Databytes: " << nDigitalBytes; - } - // transceiver channels - if (readoutType == slsDetectorDefs::TRANSCEIVER_ONLY || - readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) { - nTransceiverChans = __builtin_popcount(transceiverMask); - ; - nTransceiverBytes = nTransceiverChans * - NUM_BYTES_PER_TRANSCEIVER_CHANNEL * - nTransceiverSamples; - LOG(logDEBUG1) << "Number of Transceiver Channels:" - << nTransceiverChans - << " Databytes: " << nTransceiverBytes; - } - nPixelsX = nAnalogChans + nDigitalChans + nTransceiverChans; - - imageSize = nAnalogBytes + digital_bytes_reserved + nTransceiverBytes; + // calculate network parameters packetsPerFrame = ceil((double)imageSize / (double)dataSize); - LOG(logDEBUG1) << "Total Number of Channels:" << nPixelsX << " Databytes: " << imageSize; }; diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index faf3bf031..0a9690519 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -908,7 +908,7 @@ void Implementation::StartMasterWriter() { masterAttributes.detType = generalData->detType; masterAttributes.timingMode = timingMode; masterAttributes.geometry = numPorts; - masterAttributes.imageSize = generalData->imageSize; + masterAttributes.imageSize = generalData->actualImageSize; masterAttributes.nPixels = xy(generalData->nPixelsX, generalData->nPixelsY); masterAttributes.maxFramesPerFile = generalData->framesPerFile; @@ -943,7 +943,7 @@ void Implementation::StartMasterWriter() { masterAttributes.quad = quadEnable; masterAttributes.readNRows = readNRows; masterAttributes.ratecorr = rateCorrections; - masterAttributes.adcmask = generalData->tengigaEnable + masterAttributes.adcMask = generalData->tengigaEnable ? generalData->adcEnableMaskTenGiga : generalData->adcEnableMaskOneGiga; masterAttributes.analog = @@ -959,12 +959,12 @@ void Implementation::StartMasterWriter() { ? 1 : 0; masterAttributes.digitalSamples = generalData->nDigitalSamples; - masterAttributes.dbitoffset = generalData->ctbDbitOffset; - masterAttributes.dbitreorder = generalData->ctbDbitReorder; - masterAttributes.dbitlist = 0; + masterAttributes.dbitOffset = generalData->ctbDbitOffset; + masterAttributes.dbitReorder = generalData->ctbDbitReorder; + masterAttributes.dbitList = 0; for (auto &i : generalData->ctbDbitList) { - masterAttributes.dbitlist |= (static_cast(1) << i); + masterAttributes.dbitList |= (static_cast(1) << i); } masterAttributes.transceiverSamples = generalData->nTransceiverSamples; @@ -983,6 +983,7 @@ void Implementation::StartMasterWriter() { masterAttributes.gateDelayArray[2] = gateDelay3; masterAttributes.gates = numberOfGates; masterAttributes.additionalJsonHeader = additionalJsonHeader; + masterAttributes.readoutSpeed = readoutSpeed; // create master file masterFileName = dataProcessor[0]->CreateMasterFile( @@ -1781,6 +1782,15 @@ void Implementation::setTransceiverEnableMask(uint32_t mask) { LOG(logINFO) << "Packets per Frame: " << (generalData->packetsPerFrame); } +slsDetectorDefs::speedLevel Implementation::getReadoutSpeed() const { + return readoutSpeed; +} + +void Implementation::setReadoutSpeed(const slsDetectorDefs::speedLevel i) { + readoutSpeed = i; + LOG(logINFO) << "Readout Speed: " << ToString(readoutSpeed); +} + /************************************************** * * * Callbacks * diff --git a/slsReceiverSoftware/src/Implementation.h b/slsReceiverSoftware/src/Implementation.h index 31359930f..2699bbfc4 100644 --- a/slsReceiverSoftware/src/Implementation.h +++ b/slsReceiverSoftware/src/Implementation.h @@ -256,10 +256,12 @@ class Implementation : private virtual slsDetectorDefs { bool getDbitReorder() const; /* [Ctb] */ void setDbitReorder(const bool reorder); - uint32_t getTransceiverEnableMask() const; /* [Ctb] */ void setTransceiverEnableMask(const uint32_t mask); + speedLevel getReadoutSpeed() const; + /* [Eiger][Jungfrau][Moench][Mythen3][Gotthard2]*/ + void setReadoutSpeed(const speedLevel i); /************************************************** * * @@ -369,6 +371,7 @@ class Implementation : private virtual slsDetectorDefs { int thresholdEnergyeV{-1}; std::array thresholdAllEnergyeV = {{-1, -1, -1}}; std::vector rateCorrections; + speedLevel readoutSpeed{FULL_SPEED}; // callbacks void (*startAcquisitionCallBack)(const startCallbackHeader, diff --git a/slsReceiverSoftware/src/MasterAttributes.cpp b/slsReceiverSoftware/src/MasterAttributes.cpp index 211b6f439..76907ea04 100644 --- a/slsReceiverSoftware/src/MasterAttributes.cpp +++ b/slsReceiverSoftware/src/MasterAttributes.cpp @@ -5,8 +5,238 @@ namespace sls { -void MasterAttributes::GetBinaryAttributes( - rapidjson::PrettyWriter *w) { +void MasterAttributes::GetCommonBinaryAttributes(writer *w) { + WriteBinaryVersion(w); + WriteBinaryTimestamp(w); + WriteBinaryDetectorType(w); + WriteBinaryTimingMode(w); + WriteBinaryGeometry(w); + WriteBinaryImageSize(w); + WriteBinaryPixels(w); + WriteBinaryMaxFramesPerFile(w); + WriteBinaryFrameDiscardPolicy(w); + WriteBinaryFramePadding(w); + WriteBinaryScanParameters(w); + WriteBinaryTotalFrames(w); +} + +void MasterAttributes::GetFinalBinaryAttributes(writer *w) { + WriteBinaryFramesInFile(w); + WriteBinaryJsonHeader(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteCommonHDF5Attributes(H5::H5File *fd, + H5::Group *group) { + WriteHDF5Version(fd); + WriteHDF5Timestamp(group); + WriteHDF5DetectorType(group); + WriteHDF5TimingMode(group); + WriteHDF5Geometry(group); + WriteHDF5ImageSize(group); + WriteHDF5Pixels(group); + WriteHDF5MaxFramesPerFile(group); + WriteHDF5FrameDiscardPolicy(group); + WriteHDF5FramePadding(group); + WriteHDF5ScanParameters(group); + WriteHDF5TotalFrames(group); +} + +void MasterAttributes::WriteFinalHDF5Attributes(H5::Group *group) { + WriteHDF5FramesInFile(group); + WriteHDF5JsonHeader(group); +} +#endif + +void MasterAttributes::GetJungfrauBinaryAttributes(writer *w) { + WriteBinaryRois(w); + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryNumberOfUDPInterfaces(w); + WriteBinaryNumberOfRows(w); + WriteBinaryReadoutSpeed(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteJungfrauHDF5Attributes(H5::Group *group) { + WriteHDF5ROIs(group); + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5NumberOfUDPInterfaces(group); + WriteHDF5NumberOfRows(group); + WriteHDF5ReadoutSpeed(group); +} +#endif + +void MasterAttributes::GetMoenchBinaryAttributes(writer *w) { + WriteBinaryRois(w); + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryNumberOfUDPInterfaces(w); + WriteBinaryNumberOfRows(w); + WriteBinaryReadoutSpeed(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteMoenchHDF5Attributes(H5::Group *group) { + WriteHDF5ROIs(group); + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5NumberOfUDPInterfaces(group); + WriteHDF5NumberOfRows(group); + WriteHDF5ReadoutSpeed(group); +} +#endif + +void MasterAttributes::GetEigerBinaryAttributes(writer *w) { + WriteBinaryRois(w); + WriteBinaryDynamicRange(w); + WriteBinaryTenGiga(w); + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryThresholdEnergy(w); + WriteBinarySubExposureTime(w); + WriteBinarySubAcquisitionPeriod(w); + WriteBinaryQuad(w); + WriteBinaryNumberOfRows(w); + WriteBinaryRateCorrections(w); + WriteBinaryReadoutSpeed(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteEigerHDF5Attributes(H5::Group *group) { + WriteHDF5ROIs(group); + WriteHDF5DynamicRange(group); + WriteHDF5TenGiga(group); + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5ThresholdEnergy(group); + WriteHDF5SubExposureTime(group); + WriteHDF5SubAcquisitionPeriod(group); + WriteHDF5Quad(group); + WriteHDF5NumberOfRows(group); + WriteHDF5RateCorrections(group); + WriteHDF5ReadoutSpeed(group); +} +#endif + +void MasterAttributes::GetMythen3BinaryAttributes(writer *w) { + WriteBinaryRois(w); + WriteBinaryDynamicRange(w); + WriteBinaryTenGiga(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryCounterMask(w); + WriteBinaryExptimeArray(w); + WriteBinaryGateDelayArray(w); + WriteBinaryGates(w); + WriteBinaryThresholdAllEnergy(w); + WriteBinaryReadoutSpeed(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteMythen3HDF5Attributes(H5::Group *group) { + WriteHDF5ROIs(group); + WriteHDF5DynamicRange(group); + WriteHDF5TenGiga(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5CounterMask(group); + WriteHDF5ExptimeArray(group); + WriteHDF5GateDelayArray(group); + WriteHDF5Gates(group); + WriteHDF5ThresholdAllEnergy(group); + WriteHDF5ReadoutSpeed(group); +} +#endif + +void MasterAttributes::GetGotthard2BinaryAttributes(writer *w) { + WriteBinaryRois(w); + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryBurstMode(w); + WriteBinaryReadoutSpeed(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteGotthard2HDF5Attributes(H5::Group *group) { + WriteHDF5ROIs(group); + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5BurstMode(group); + WriteHDF5ReadoutSpeed(group); +} +#endif + +void MasterAttributes::GetCtbBinaryAttributes(writer *w) { + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryTenGiga(w); + WriteBinaryAdcMask(w); + WriteBinaryAnalogFlag(w); + WriteBinaryAnalogSamples(w); + WriteBinaryDigitalFlag(w); + WriteBinaryDigitalSamples(w); + WriteBinaryDBitOffset(w); + WriteBinaryDBitReorder(w); + WriteBinaryDBitBitset(w); + WriteBinaryTransceiverMask(w); + WriteBinaryTransceiverFlag(w); + WriteBinaryTransceiverSamples(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteCtbHDF5Attributes(H5::Group *group) { + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5TenGiga(group); + WriteHDF5AdcMask(group); + WriteHDF5AnalogFlag(group); + WriteHDF5AnalogSamples(group); + WriteHDF5DigitalFlag(group); + WriteHDF5DigitalSamples(group); + WriteHDF5DBitOffset(group); + WriteHDF5DBitReorder(group); + WriteHDF5DBitBitset(group); + WriteHDF5TransceiverMask(group); + WriteHDF5TransceiverFlag(group); + WriteHDF5TransceiverSamples(group); +} +#endif + +void MasterAttributes::GetXilinxCtbBinaryAttributes(writer *w) { + WriteBinaryExposureTme(w); + WriteBinaryAcquisitionPeriod(w); + WriteBinaryAdcMask(w); + WriteBinaryAnalogFlag(w); + WriteBinaryAnalogSamples(w); + WriteBinaryDigitalFlag(w); + WriteBinaryDigitalSamples(w); + WriteBinaryDBitOffset(w); + WriteBinaryDBitReorder(w); + WriteBinaryDBitBitset(w); + WriteBinaryTransceiverMask(w); + WriteBinaryTransceiverFlag(w); + WriteBinaryTransceiverSamples(w); +} + +#ifdef HDF5C +void MasterAttributes::WriteXilinxCtbHDF5Attributes(H5::Group *group) { + WriteHDF5ExposureTime(group); + WriteHDF5AcquisitionPeriod(group); + WriteHDF5AdcMask(group); + WriteHDF5AnalogFlag(group); + WriteHDF5AnalogSamples(group); + WriteHDF5DigitalFlag(group); + WriteHDF5DigitalSamples(group); + WriteHDF5DBitOffset(group); + WriteHDF5DBitReorder(group); + WriteHDF5DBitBitset(group); + WriteHDF5TransceiverMask(group); + WriteHDF5TransceiverFlag(group); + WriteHDF5TransceiverSamples(group); +} +#endif + +void MasterAttributes::GetBinaryAttributes(writer *w) { w->StartObject(); GetCommonBinaryAttributes(w); switch (detType) { @@ -70,57 +300,597 @@ void MasterAttributes::WriteHDF5Attributes(H5::H5File *fd, H5::Group *group) { } #endif -void MasterAttributes::GetCommonBinaryAttributes( - rapidjson::PrettyWriter *w) { - w->Key("Version"); +void MasterAttributes::WriteBinaryDetectorType(writer *w) { + WriteBinary(w, N_DETECTOR_TYPE.data(), ToString(detType)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DetectorType(H5::Group *group) { + WriteHDF5String(group, N_DETECTOR_TYPE.data(), ToString(detType)); +} +#endif + +void MasterAttributes::WriteBinaryTimingMode(writer *w) { + WriteBinary(w, N_TIMING_MODE.data(), ToString(timingMode)); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5TimingMode(H5::Group *group) { + WriteHDF5String(group, N_TIMING_MODE.data(), ToString(timingMode)); +} +#endif + +void MasterAttributes::WriteBinaryGeometry(writer *w) { + WriteBinaryXY(w, N_GEOMETRY.data(), geometry); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5Geometry(H5::Group *group) { + WriteHDF5XY(group, N_GEOMETRY.data(), geometry); +} +#endif + +void MasterAttributes::WriteBinaryImageSize(writer *w) { + WriteBinary(w, N_IMAGE_SIZE.data(), imageSize); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5ImageSize(H5::Group *group) { + WriteHDF5Int(group, N_IMAGE_SIZE.data(), imageSize); +} +#endif +void MasterAttributes::WriteBinaryPixels(writer *w) { + WriteBinaryXY(w, N_PIXELS.data(), nPixels); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5Pixels(H5::Group *group) { + WriteHDF5XY(group, N_PIXELS.data(), nPixels); +} +#endif + +void MasterAttributes::WriteBinaryMaxFramesPerFile(writer *w) { + WriteBinary(w, N_MAX_FRAMES_PER_FILE.data(), maxFramesPerFile); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5MaxFramesPerFile(H5::Group *group) { + WriteHDF5Int(group, N_MAX_FRAMES_PER_FILE.data(), maxFramesPerFile); +} +#endif +void MasterAttributes::WriteBinaryFrameDiscardPolicy(writer *w) { + WriteBinary(w, N_FRAME_DISCARD_POLICY.data(), ToString(frameDiscardMode)); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5FrameDiscardPolicy(H5::Group *group) { + WriteHDF5String(group, N_FRAME_DISCARD_POLICY.data(), + ToString(frameDiscardMode)); +} +#endif +void MasterAttributes::WriteBinaryFramePadding(writer *w) { + WriteBinary(w, N_FRAME_PADDING.data(), framePadding); +} +#ifdef HDF5C +void MasterAttributes::WriteHDF5FramePadding(H5::Group *group) { + WriteHDF5Int(group, N_FRAME_PADDING.data(), framePadding); +} +#endif + +void MasterAttributes::WriteBinaryTotalFrames(writer *w) { + WriteBinary(w, N_TOTAL_FRAMES.data(), totalFrames); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5TotalFrames(H5::Group *group) { + WriteHDF5Int(group, N_TOTAL_FRAMES.data(), totalFrames); +} +#endif + +void MasterAttributes::WriteBinaryFramesInFile(writer *w) { + WriteBinary(w, N_FRAMES_IN_FILE.data(), framesInFile); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5FramesInFile(H5::Group *group) { + WriteHDF5Int(group, N_FRAMES_IN_FILE.data(), framesInFile); +} +#endif + +void MasterAttributes::WriteBinaryExposureTme(writer *w) { + WriteBinary(w, N_EXPOSURE_TIME.data(), ToString(exptime)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ExposureTime(H5::Group *group) { + WriteHDF5String(group, N_EXPOSURE_TIME.data(), ToString(exptime)); +} +#endif + +void MasterAttributes::WriteBinaryAcquisitionPeriod(writer *w) { + WriteBinary(w, N_ACQUISITION_PERIOD.data(), ToString(period)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5AcquisitionPeriod(H5::Group *group) { + WriteHDF5String(group, N_ACQUISITION_PERIOD.data(), ToString(period)); +} +#endif + +void MasterAttributes::WriteBinaryNumberOfUDPInterfaces(writer *w) { + WriteBinary(w, N_NUM_UDP_INTERFACES.data(), numUDPInterfaces); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5NumberOfUDPInterfaces(H5::Group *group) { + WriteHDF5Int(group, N_NUM_UDP_INTERFACES.data(), numUDPInterfaces); +} +#endif + +void MasterAttributes::WriteBinaryNumberOfRows(writer *w) { + WriteBinary(w, N_NUMBER_OF_ROWS.data(), readNRows); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5NumberOfRows(H5::Group *group) { + WriteHDF5Int(group, N_NUMBER_OF_ROWS.data(), readNRows); +} +#endif + +void MasterAttributes::WriteBinaryReadoutSpeed(writer *w) { + WriteBinary(w, N_READOUT_SPEED.data(), ToString(readoutSpeed)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ReadoutSpeed(H5::Group *group) { + WriteHDF5String(group, N_READOUT_SPEED.data(), ToString(readoutSpeed)); +} +#endif + +void MasterAttributes::WriteBinaryDynamicRange(writer *w) { + WriteBinary(w, N_DYNAMIC_RANGE.data(), dynamicRange); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DynamicRange(H5::Group *group) { + WriteHDF5Int(group, N_DYNAMIC_RANGE.data(), dynamicRange); +} +#endif + +void MasterAttributes::WriteBinaryTenGiga(writer *w) { + WriteBinary(w, N_TEN_GIGA.data(), tenGiga); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5TenGiga(H5::Group *group) { + WriteHDF5Int(group, N_TEN_GIGA.data(), tenGiga); +} +#endif + +void MasterAttributes::WriteBinaryThresholdEnergy(writer *w) { + WriteBinary(w, N_THRESHOLD_ENERGY.data(), thresholdEnergyeV); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ThresholdEnergy(H5::Group *group) { + WriteHDF5Int(group, N_THRESHOLD_ENERGY.data(), thresholdEnergyeV); +} +#endif + +void MasterAttributes::WriteBinarySubExposureTime(writer *w) { + WriteBinary(w, N_SUB_EXPOSURE_TIME.data(), ToString(subExptime)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5SubExposureTime(H5::Group *group) { + WriteHDF5String(group, N_SUB_EXPOSURE_TIME.data(), ToString(subExptime)); +} +#endif + +void MasterAttributes::WriteBinarySubAcquisitionPeriod(writer *w) { + WriteBinary(w, N_SUB_ACQUISITION_PERIOD.data(), ToString(subPeriod)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5SubAcquisitionPeriod(H5::Group *group) { + WriteHDF5String(group, N_SUB_ACQUISITION_PERIOD.data(), + ToString(subPeriod)); +} +#endif + +void MasterAttributes::WriteBinaryQuad(writer *w) { + WriteBinary(w, N_QUAD.data(), quad); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5Quad(H5::Group *group) { + WriteHDF5Int(group, N_QUAD.data(), quad); +} +#endif + +void MasterAttributes::WriteBinaryRateCorrections(writer *w) { + WriteBinary(w, N_RATE_CORRECTIONS.data(), ratecorr); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5RateCorrections(H5::Group *group) { + WriteHDF5Int(group, N_RATE_CORRECTIONS.data(), ratecorr); +} +#endif + +void MasterAttributes::WriteBinaryCounterMask(writer *w) { + WriteBinary(w, N_COUNTER_MASK.data(), counterMask); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5CounterMask(H5::Group *group) { + WriteHDF5Int(group, N_COUNTER_MASK.data(), counterMask); +} +#endif + +void MasterAttributes::WriteBinaryExptimeArray(writer *w) { + WriteBinary(w, N_EXPOSURE_TIMES.data(), exptimeArray); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ExptimeArray(H5::Group *group) { + std::vector timeStrings; + for (auto &e : exptimeArray) { + timeStrings.push_back(ToString(e)); + } + WriteHDF5StringArray(group, N_EXPOSURE_TIMES.data(), timeStrings); +} +#endif + +void MasterAttributes::WriteBinaryGateDelayArray(writer *w) { + WriteBinary(w, N_GATE_DELAYS.data(), gateDelayArray); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5GateDelayArray(H5::Group *group) { + std::vector timeStrings; + for (auto &g : gateDelayArray) { + timeStrings.push_back(ToString(g)); + } + WriteHDF5StringArray(group, N_GATE_DELAYS.data(), timeStrings); +} +#endif + +void MasterAttributes::WriteBinaryGates(writer *w) { + WriteBinary(w, N_GATES.data(), gates); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5Gates(H5::Group *group) { + WriteHDF5Int(group, N_GATES.data(), gates); +} +#endif + +void MasterAttributes::WriteBinaryThresholdAllEnergy(writer *w) { + WriteBinary(w, N_THRESHOLD_ENERGIES.data(), thresholdAllEnergyeV); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ThresholdAllEnergy(H5::Group *group) { + WriteHDF5Int(group, N_THRESHOLD_ENERGIES.data(), thresholdAllEnergyeV); +} +#endif + +void MasterAttributes::WriteBinaryBurstMode(writer *w) { + WriteBinary(w, N_BURST_MODE.data(), ToString(burstMode)); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5BurstMode(H5::Group *group) { + WriteHDF5String(group, N_BURST_MODE.data(), ToString(burstMode)); +} +#endif + +void MasterAttributes::WriteBinaryAdcMask(writer *w) { + WriteBinary(w, N_ADC_MASK.data(), adcMask); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5AdcMask(H5::Group *group) { + WriteHDF5Int(group, N_ADC_MASK.data(), adcMask); +} +#endif + +void MasterAttributes::WriteBinaryAnalogFlag(writer *w) { + WriteBinary(w, N_ANALOG.data(), analog); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5AnalogFlag(H5::Group *group) { + WriteHDF5Int(group, N_ANALOG.data(), analog); +} +#endif + +void MasterAttributes::WriteBinaryAnalogSamples(writer *w) { + WriteBinary(w, N_ANALOG_SAMPLES.data(), analogSamples); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5AnalogSamples(H5::Group *group) { + WriteHDF5Int(group, N_ANALOG_SAMPLES.data(), analogSamples); +} +#endif + +void MasterAttributes::WriteBinaryDigitalFlag(writer *w) { + WriteBinary(w, N_DIGITAL.data(), digital); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DigitalFlag(H5::Group *group) { + WriteHDF5Int(group, N_DIGITAL.data(), digital); +} +#endif + +void MasterAttributes::WriteBinaryDigitalSamples(writer *w) { + WriteBinary(w, N_DIGITAL_SAMPLES.data(), digitalSamples); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DigitalSamples(H5::Group *group) { + WriteHDF5Int(group, N_DIGITAL_SAMPLES.data(), digitalSamples); +} +#endif + +void MasterAttributes::WriteBinaryDBitOffset(writer *w) { + WriteBinary(w, N_DBIT_OFFSET.data(), dbitOffset); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DBitOffset(H5::Group *group) { + WriteHDF5Int(group, N_DBIT_OFFSET.data(), dbitOffset); +} +#endif + +void MasterAttributes::WriteBinaryDBitReorder(writer *w) { + WriteBinary(w, N_DBIT_REORDER.data(), dbitReorder); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DBitReorder(H5::Group *group) { + WriteHDF5Int(group, N_DBIT_REORDER.data(), dbitReorder); +} +#endif + +void MasterAttributes::WriteBinaryDBitBitset(writer *w) { + WriteBinary(w, N_DBIT_BITSET.data(), dbitList); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5DBitBitset(H5::Group *group) { + WriteHDF5Int(group, N_DBIT_BITSET.data(), dbitList); +} +#endif + +void MasterAttributes::WriteBinaryTransceiverMask(writer *w) { + WriteBinary(w, N_TRANSCEIVER_MASK.data(), transceiverMask); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5TransceiverMask(H5::Group *group) { + WriteHDF5Int(group, N_TRANSCEIVER_MASK.data(), transceiverMask); +} +#endif + +void MasterAttributes::WriteBinaryTransceiverFlag(writer *w) { + WriteBinary(w, N_TRANSCEIVER.data(), transceiver); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5TransceiverFlag(H5::Group *group) { + WriteHDF5Int(group, N_TRANSCEIVER.data(), transceiver); +} +#endif + +void MasterAttributes::WriteBinaryTransceiverSamples(writer *w) { + WriteBinary(w, N_TRANSCEIVER_SAMPLES.data(), transceiverSamples); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5TransceiverSamples(H5::Group *group) { + WriteHDF5Int(group, N_TRANSCEIVER_SAMPLES.data(), transceiverSamples); +} +#endif + +#ifdef HDF5C +void MasterAttributes::WriteHDF5String(H5::Group *group, + const std::string &name, + const std::string &value) { + H5::DataSpace dataspace(H5S_SCALAR); + H5::StrType strdatatype(H5::PredType::C_S1, H5T_VARIABLE); + H5::DataSet dataset = group->createDataSet(name, strdatatype, dataspace); + const char *cstr = value.c_str(); + dataset.write(&cstr, strdatatype); +} + +void MasterAttributes::WriteHDF5StringArray( + H5::Group *group, const std::string &name, + const std::vector &value) { + std::vector c; + for (auto &s : value) { + c.push_back(s.c_str()); + } + hsize_t dims[1] = {c.size()}; + H5::DataSpace dataspace(1, dims); + H5::StrType strdatatype(H5::PredType::C_S1, H5T_VARIABLE); + H5::DataSet dataset = group->createDataSet(name, strdatatype, dataspace); + dataset.write(c.data(), strdatatype); +} +#endif + +void MasterAttributes::WriteBinaryXY(writer *w, const std::string &name, + const defs::xy &xy) { + w->Key(name.c_str()); + w->StartObject(); + w->Key("x"); + w->Uint(xy.x); + w->Key("y"); + w->Uint(xy.y); + w->EndObject(); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5XY(H5::Group *group, const std::string &name, + const defs::xy &xy) { + H5::CompType c(sizeof(defs::xy)); + c.insertMember("x", HOFFSET(defs::xy, x), H5::PredType::NATIVE_INT); + c.insertMember("y", HOFFSET(defs::xy, y), H5::PredType::NATIVE_INT); + H5::DataSpace dataspace(H5S_SCALAR); + H5::DataSet dataset = group->createDataSet(name, c, dataspace); + dataset.write(&xy, c); +} +#endif + +void MasterAttributes::WriteBinaryVersion(writer *w) { + w->Key(N_VERSION.data()); w->SetMaxDecimalPlaces(2); w->Double(BINARY_WRITER_VERSION); - w->Key("Timestamp"); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5Version(H5::H5File *fd) { + H5::DataSpace dataspace(H5S_SCALAR); + H5::Attribute attribute = fd->createAttribute( + N_VERSION.data(), H5::PredType::NATIVE_DOUBLE, dataspace); + double version = HDF5_WRITER_VERSION; + attribute.write(H5::PredType::NATIVE_DOUBLE, &version); +} +#endif + +void MasterAttributes::WriteBinaryTimestamp(writer *w) { time_t t = std::time(nullptr); std::string sTime(ctime(&t)); std::replace(sTime.begin(), sTime.end(), '\n', '\0'); - w->String(sTime.c_str()); - w->Key("Detector Type"); - w->String(ToString(detType).c_str()); - w->Key("Timing Mode"); - w->String(ToString(timingMode).c_str()); - w->Key("Geometry"); - w->StartObject(); - w->Key("x"); - w->Uint(geometry.x); - w->Key("y"); - w->Uint(geometry.y); - w->EndObject(); - w->Key("Image Size in bytes"); - w->Uint(imageSize); - w->Key("Pixels"); - w->StartObject(); - w->Key("x"); - w->Uint(nPixels.x); - w->Key("y"); - w->Uint(nPixels.y); - w->EndObject(); - w->Key("Max Frames Per File"); - w->Uint(maxFramesPerFile); - w->Key("Frame Discard Policy"); - w->String(ToString(frameDiscardMode).c_str()); - w->Key("Frame Padding"); - w->Uint(framePadding); - w->Key("Scan Parameters"); - w->String(ToString(scanParams).c_str()); - w->Key("Total Frames"); - w->Uint64(totalFrames); + WriteBinary(w, N_TIMESTAMP.data(), sTime); } -void MasterAttributes::GetFinalBinaryAttributes( - rapidjson::PrettyWriter *w) { - // adding few common parameters to the end - if (!additionalJsonHeader.empty()) { - w->Key("Additional Json Header"); - w->String(ToString(additionalJsonHeader).c_str()); +#ifdef HDF5C +void MasterAttributes::WriteHDF5Timestamp(H5::Group *group) { + time_t t = std::time(nullptr); + std::string sTime(ctime(&t)); + WriteHDF5String(group, N_TIMESTAMP.data(), sTime); +} +#endif + +void MasterAttributes::WriteBinaryRois(writer *w) { + w->Key(N_RECEIVER_ROIS.data()); + w->StartArray(); + for (const slsDetectorDefs::ROI &roi : rois) { + auto roiArray = roi.getIntArray(); + w->StartObject(); + w->Key("xmin"); + w->Int(roiArray[0]); + w->Key("xmax"); + w->Int(roiArray[1]); + w->Key("ymin"); + w->Int(roiArray[2]); + w->Key("ymax"); + w->Int(roiArray[3]); + w->EndObject(); } - w->Key("Frames in File"); - w->Uint64(framesInFile); + w->EndArray(); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ROIs(H5::Group *group) { + H5::CompType c(sizeof(defs::ROI)); + c.insertMember("xmin", HOFFSET(defs::ROI, xmin), H5::PredType::NATIVE_INT); + c.insertMember("xmax", HOFFSET(defs::ROI, xmax), H5::PredType::NATIVE_INT); + c.insertMember("ymin", HOFFSET(defs::ROI, ymin), H5::PredType::NATIVE_INT); + c.insertMember("ymax", HOFFSET(defs::ROI, ymax), H5::PredType::NATIVE_INT); + hsize_t dims[1] = {rois.size()}; // 1d dataspace with size of roi elements + H5::DataSpace dataspace(1, dims); + H5::DataSet dataset = + group->createDataSet(N_RECEIVER_ROIS.data(), c, dataspace); + dataset.write(rois.data(), c); +} + +#endif + +void MasterAttributes::WriteBinaryScanParameters(writer *w) { + w->Key(N_SCAN_PARAMETERS.data()); + w->StartObject(); + w->Key("enable"); + w->Int(scanParams.enable); + w->Key("dacInd"); + w->Int(scanParams.dacInd); + w->Key("start offset"); + w->Int(scanParams.startOffset); + w->Key("stop offset"); + w->Int(scanParams.stopOffset); + w->Key("step size"); + w->Int(scanParams.stepSize); + w->Key("dac settle time ns"); + w->Int64(scanParams.dacSettleTime_ns); + w->EndObject(); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5ScanParameters(H5::Group *group) { + H5::CompType c(sizeof(defs::scanParameters)); + c.insertMember("enable", HOFFSET(defs::scanParameters, enable), + H5::PredType::NATIVE_INT); + c.insertMember("dacInd", HOFFSET(defs::scanParameters, dacInd), + H5::PredType::NATIVE_INT); + c.insertMember("startOffset", HOFFSET(defs::scanParameters, startOffset), + H5::PredType::NATIVE_INT); + c.insertMember("stopOffset", HOFFSET(defs::scanParameters, stopOffset), + H5::PredType::NATIVE_INT); + c.insertMember("stepSize", HOFFSET(defs::scanParameters, stepSize), + H5::PredType::NATIVE_INT); + c.insertMember("dacSettleTime_ns", + HOFFSET(defs::scanParameters, dacSettleTime_ns), + H5::PredType::STD_I64LE); + H5::DataSpace dataspace(H5S_SCALAR); + H5::DataSet dataset = + group->createDataSet(N_SCAN_PARAMETERS.data(), c, dataspace); + dataset.write(&scanParams, c); +} +#endif + +void MasterAttributes::WriteBinaryJsonHeader(writer *w) { + w->Key(N_ADDITIONAL_JSON_HEADER.data()); + w->StartObject(); + for (const auto &pair : additionalJsonHeader) { + w->Key(pair.first.c_str()); + w->String(pair.second.c_str()); + } + w->EndObject(); +} + +#ifdef HDF5C +void MasterAttributes::WriteHDF5JsonHeader(H5::Group *group) { + H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE); + H5::CompType mapType(sizeof(char *) * 2); + mapType.insertMember("Key", 0, strType); + mapType.insertMember("Value", sizeof(char *), strType); + // create string struct just so its not dangling pointer + // with push_back + struct KeyValue { + std::string key; + std::string value; + }; + struct KVRaw { + const char *key; + const char *value; + }; + std::vector raw; + std::vector value; + value.reserve(additionalJsonHeader.size()); + raw.reserve(additionalJsonHeader.size()); + for (const auto &pair : additionalJsonHeader) { + value.push_back({pair.first, pair.second}); + } + for (const auto &item : value) { + raw.push_back({item.key.c_str(), item.value.c_str()}); + } + hsize_t dims[1] = {value.size()}; + H5::DataSpace dataspace(1, dims); + H5::DataSet dataset = group->createDataSet(N_ADDITIONAL_JSON_HEADER.data(), + mapType, dataspace); + dataset.write(raw.data(), mapType); +} +#endif + +void MasterAttributes::WriteBinaryFrameHeaderFormat(writer *w) { w->Key("Frame Header Format"); w->StartObject(); w->Key("Frame Number"); @@ -154,658 +924,4 @@ void MasterAttributes::GetFinalBinaryAttributes( w->EndObject(); } -void MasterAttributes::GetBinaryRois( - rapidjson::PrettyWriter *w) { - w->Key("Receiver Rois"); - w->StartArray(); - for (const slsDetectorDefs::ROI &roi : rois) { - std::string roi_str = ToString(roi); - w->String(roi_str.c_str()); - } - w->EndArray(); -} - -#ifdef HDF5C -void MasterAttributes::WriteCommonHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - char c[1024]{}; - // version - { - double version = BINARY_WRITER_VERSION; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::Attribute attribute = fd->createAttribute( - "Version", H5::PredType::NATIVE_DOUBLE, dataspace); - attribute.write(H5::PredType::NATIVE_DOUBLE, &version); - } - // timestamp - { - time_t t = std::time(nullptr); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = - group->createDataSet("Timestamp", strdatatype, dataspace); - strcpy_safe(c, std::string(ctime(&t))); - dataset.write(c, strdatatype); - } - // detector type - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Detector Type", strdatatype, dataspace); - strcpy_safe(c, ToString(detType)); - dataset.write(c, strdatatype); - } - // timing mode - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Timing Mode", strdatatype, dataspace); - strcpy_safe(c, ToString(timingMode)); - dataset.write(c, strdatatype); - } - // TODO: make this into an array? - // geometry x - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Geometry in x axis", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&geometry.x, H5::PredType::NATIVE_INT); - } - // geometry y - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Geometry in y axis", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&geometry.y, H5::PredType::NATIVE_INT); - } - // Image Size - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Image Size", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&imageSize, H5::PredType::NATIVE_INT); - H5::DataSpace dataspaceAttr = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::Attribute attribute = - dataset.createAttribute("Unit", strdatatype, dataspaceAttr); - strcpy_safe(c, "bytes"); - attribute.write(strdatatype, c); - } - // TODO: make this into an array? - // npixels x - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Number of pixels in x axis", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&nPixels.x, H5::PredType::NATIVE_INT); - } - // npixels y - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Number of pixels in y axis", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&nPixels.y, H5::PredType::NATIVE_INT); - } - // Maximum frames per file - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Maximum frames per file", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&maxFramesPerFile, H5::PredType::NATIVE_INT); - } - // Frame Discard Policy - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = group->createDataSet("Frame Discard Policy", - strdatatype, dataspace); - strcpy_safe(c, ToString(frameDiscardMode)); - dataset.write(c, strdatatype); - } - // Frame Padding - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Frame Padding", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&framePadding, H5::PredType::NATIVE_INT); - } - // Scan Parameters - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Scan Parameters", strdatatype, dataspace); - strcpy_safe(c, ToString(scanParams)); - dataset.write(c, strdatatype); - } - // Total Frames - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Total Frames", H5::PredType::STD_U64LE, dataspace); - dataset.write(&totalFrames, H5::PredType::STD_U64LE); - } -} - -void MasterAttributes::WriteFinalHDF5Attributes(H5::Group *group) { - char c[1024]{}; - // Total Frames in file - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Frames in File", H5::PredType::STD_U64LE, dataspace); - dataset.write(&framesInFile, H5::PredType::STD_U64LE); - } - // additional json header - if (!additionalJsonHeader.empty()) { - std::string json = ToString(additionalJsonHeader); - H5::StrType strdatatype(H5::PredType::C_S1, json.length()); - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet("Additional JSON Header", - strdatatype, dataspace); - strcpy_safe(c, ToString(additionalJsonHeader)); - dataset.write(c, strdatatype); - } -} - -void MasterAttributes::WriteHDF5ROIs(H5::Group *group) { - hsize_t dims[1] = {rois.size()}; - H5::DataSpace dataspace(1, dims); - H5::StrType strdatatype(H5::PredType::C_S1, 1024); - H5::DataSet dataset = - group->createDataSet("Receiver Rois", strdatatype, dataspace); - std::vector cRois(rois.size()); - for (size_t i = 0; i < rois.size(); ++i) { - strcpy_safe(cRois[i], ToString(rois[i])); - } - dataset.write(cRois.data(), strdatatype); -} - -void MasterAttributes::WriteHDF5Exptime(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Exposure Time", strdatatype, dataspace); - char c[1024]{}; - strcpy_safe(c, ToString(exptime)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5Period(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Acquisition Period", strdatatype, dataspace); - char c[1024]{}; - strcpy_safe(c, ToString(period)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5DynamicRange(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Dynamic Range", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&dynamicRange, H5::PredType::NATIVE_INT); - H5::DataSpace dataspaceAttr = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::Attribute attribute = - dataset.createAttribute("Unit", strdatatype, dataspaceAttr); - char c[1024] = "bits"; - attribute.write(strdatatype, c); -} - -void MasterAttributes::WriteHDF5TenGiga(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Ten Giga Enable", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&tenGiga, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5NumUDPInterfaces(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Number of UDP Interfaces", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&numUDPInterfaces, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5ReadNRows(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Number of rows", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&readNRows, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5ThresholdEnergy(H5::Group *group) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Threshold Energy", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&thresholdEnergyeV, H5::PredType::NATIVE_INT); - H5::DataSpace dataspaceAttr = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::Attribute attribute = - dataset.createAttribute("Unit", strdatatype, dataspaceAttr); - strcpy_safe(c, "eV"); - attribute.write(strdatatype, c); -} - -void MasterAttributes::WriteHDF5ThresholdEnergies(H5::Group *group) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 1024); - H5::DataSet dataset = - group->createDataSet("Threshold Energies", strdatatype, dataspace); - strcpy_safe(c, ToString(thresholdAllEnergyeV)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5SubExpTime(H5::Group *group) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Sub Exposure Time", strdatatype, dataspace); - strcpy_safe(c, ToString(subExptime)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5SubPeriod(H5::Group *group) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Sub Period", strdatatype, dataspace); - strcpy_safe(c, ToString(subPeriod)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5SubQuad(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = - group->createDataSet("Quad", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&quad, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5RateCorrections(H5::Group *group) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 1024); - H5::DataSet dataset = - group->createDataSet("Rate Corrections", strdatatype, dataspace); - strcpy_safe(c, ToString(ratecorr)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5CounterMask(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Counter Mask", H5::PredType::STD_U32LE, dataspace); - dataset.write(&counterMask, H5::PredType::STD_U32LE); -} - -void MasterAttributes::WriteHDF5ExptimeArray(H5::Group *group) { - for (int i = 0; i != 3; ++i) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Exposure Time1", strdatatype, dataspace); - strcpy_safe(c, ToString(exptimeArray[i])); - dataset.write(c, strdatatype); - } -} - -void MasterAttributes::WriteHDF5GateDelayArray(H5::Group *group) { - for (int i = 0; i != 3; ++i) { - char c[1024]{}; - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Gate Delay1", strdatatype, dataspace); - strcpy_safe(c, ToString(gateDelayArray[i])); - dataset.write(c, strdatatype); - } -} - -void MasterAttributes::WriteHDF5Gates(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = - group->createDataSet("Gates", H5::PredType::STD_U32LE, dataspace); - dataset.write(&gates, H5::PredType::STD_U32LE); -} - -void MasterAttributes::WriteHDF5BurstMode(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::StrType strdatatype(H5::PredType::C_S1, 256); - H5::DataSet dataset = - group->createDataSet("Burst Mode", strdatatype, dataspace); - char c[1024]{}; - strcpy_safe(c, ToString(burstMode)); - dataset.write(c, strdatatype); -} - -void MasterAttributes::WriteHDF5AdcMask(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = - group->createDataSet("ADC Mask", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&adcmask, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5AnalogFlag(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Analog Flag", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&analog, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5AnalogSamples(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Analog Samples", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&analogSamples, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5DigitalFlag(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Digital Flag", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&digital, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5DigitalSamples(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Digital Samples", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&digitalSamples, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5DbitOffset(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Dbit Offset", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&dbitoffset, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5DbitReorder(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Dbit Reorder", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&dbitreorder, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5DbitList(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Dbit Bitset List", H5::PredType::STD_U64LE, dataspace); - dataset.write(&dbitlist, H5::PredType::STD_U64LE); -} - -void MasterAttributes::WriteHDF5TransceiverMask(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Transceiver Mask", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&transceiverMask, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5TransceiverFlag(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Transceiver Flag", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&transceiver, H5::PredType::NATIVE_INT); -} - -void MasterAttributes::WriteHDF5TransceiverSamples(H5::Group *group) { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "Transceiver Samples", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&transceiverSamples, H5::PredType::NATIVE_INT); -} -#endif - -void MasterAttributes::GetJungfrauBinaryAttributes( - rapidjson::PrettyWriter *w) { - GetBinaryRois(w); - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Number of UDP Interfaces"); - w->Uint(numUDPInterfaces); - w->Key("Number of rows"); - w->Uint(readNRows); -} - -#ifdef HDF5C -void MasterAttributes::WriteJungfrauHDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5ROIs(group); - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5NumUDPInterfaces(group); - MasterAttributes::WriteHDF5ReadNRows(group); -} -#endif - -void MasterAttributes::GetMoenchBinaryAttributes( - rapidjson::PrettyWriter *w) { - GetBinaryRois(w); - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Number of UDP Interfaces"); - w->Uint(numUDPInterfaces); - w->Key("Number of rows"); - w->Uint(readNRows); -} - -#ifdef HDF5C -void MasterAttributes::WriteMoenchHDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5ROIs(group); - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5NumUDPInterfaces(group); - MasterAttributes::WriteHDF5ReadNRows(group); -} -#endif - -void MasterAttributes::GetEigerBinaryAttributes( - rapidjson::PrettyWriter *w) { - GetBinaryRois(w); - w->Key("Dynamic Range"); - w->Uint(dynamicRange); - w->Key("Ten Giga"); - w->Uint(tenGiga); - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Threshold Energy"); - w->Int(thresholdEnergyeV); - w->Key("Sub Exptime"); - w->String(ToString(subExptime).c_str()); - w->Key("Sub Period"); - w->String(ToString(subPeriod).c_str()); - w->Key("Quad"); - w->Int(quad); - w->Key("Number of rows"); - w->Int(readNRows); - w->Key("Rate Corrections"); - w->String(ToString(ratecorr).c_str()); -} - -#ifdef HDF5C -void MasterAttributes::WriteEigerHDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5ROIs(group); - MasterAttributes::WriteHDF5DynamicRange(group); - MasterAttributes::WriteHDF5TenGiga(group); - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5ThresholdEnergy(group); - MasterAttributes::WriteHDF5SubExpTime(group); - MasterAttributes::WriteHDF5SubPeriod(group); - MasterAttributes::WriteHDF5SubQuad(group); - MasterAttributes::WriteHDF5ReadNRows(group); - MasterAttributes::WriteHDF5RateCorrections(group); -} -#endif - -void MasterAttributes::GetMythen3BinaryAttributes( - rapidjson::PrettyWriter *w) { - GetBinaryRois(w); - w->Key("Dynamic Range"); - w->Uint(dynamicRange); - w->Key("Ten Giga"); - w->Uint(tenGiga); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Counter Mask"); - w->String(ToStringHex(counterMask).c_str()); - for (int i = 0; i != 3; ++i) { - w->Key((std::string("Exptime") + std::to_string(i + 1)).c_str()); - w->String(ToString(exptimeArray[i]).c_str()); - } - for (int i = 0; i != 3; ++i) { - w->Key((std::string("GateDelay") + std::to_string(i + 1)).c_str()); - w->String(ToString(gateDelayArray[i]).c_str()); - } - w->Key("Gates"); - w->Uint(gates); - w->Key("Threshold Energies"); - w->String(ToString(thresholdAllEnergyeV).c_str()); -} - -#ifdef HDF5C -void MasterAttributes::WriteMythen3HDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5ROIs(group); - MasterAttributes::WriteHDF5DynamicRange(group); - MasterAttributes::WriteHDF5TenGiga(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5CounterMask(group); - MasterAttributes::WriteHDF5ExptimeArray(group); - MasterAttributes::WriteHDF5GateDelayArray(group); - MasterAttributes::WriteHDF5Gates(group); - MasterAttributes::WriteHDF5ThresholdEnergies(group); -} -#endif - -void MasterAttributes::GetGotthard2BinaryAttributes( - rapidjson::PrettyWriter *w) { - GetBinaryRois(w); - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Burst Mode"); - w->String(ToString(burstMode).c_str()); -} - -#ifdef HDF5C -void MasterAttributes::WriteGotthard2HDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5ROIs(group); - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5BurstMode(group); -} -#endif - -void MasterAttributes::GetCtbBinaryAttributes( - rapidjson::PrettyWriter *w) { - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("Ten Giga"); - w->Uint(tenGiga); - w->Key("ADC Mask"); - w->String(ToStringHex(adcmask).c_str()); - w->Key("Analog Flag"); - w->Uint(analog); - w->Key("Analog Samples"); - w->Uint(analogSamples); - w->Key("Digital Flag"); - w->Uint(digital); - w->Key("Digital Samples"); - w->Uint(digitalSamples); - w->Key("Dbit Offset"); - w->Uint(dbitoffset); - w->Key("Dbit Reorder"); - w->Uint(dbitreorder); - w->Key("Dbit Bitset"); - w->Uint64(dbitlist); - w->Key("Transceiver Mask"); - w->String(ToStringHex(transceiverMask).c_str()); - w->Key("Transceiver Flag"); - w->Uint(transceiver); - w->Key("Transceiver Samples"); - w->Uint(transceiverSamples); -} - -#ifdef HDF5C -void MasterAttributes::WriteCtbHDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5TenGiga(group); - MasterAttributes::WriteHDF5AdcMask(group); - MasterAttributes::WriteHDF5AnalogFlag(group); - MasterAttributes::WriteHDF5AnalogSamples(group); - MasterAttributes::WriteHDF5DigitalFlag(group); - MasterAttributes::WriteHDF5DigitalSamples(group); - MasterAttributes::WriteHDF5DbitOffset(group); - MasterAttributes::WriteHDF5DbitReorder(group); - MasterAttributes::WriteHDF5DbitList(group); - MasterAttributes::WriteHDF5TransceiverMask(group); - MasterAttributes::WriteHDF5TransceiverFlag(group); - MasterAttributes::WriteHDF5TransceiverSamples(group); -} -#endif - -void MasterAttributes::GetXilinxCtbBinaryAttributes( - rapidjson::PrettyWriter *w) { - w->Key("Exptime"); - w->String(ToString(exptime).c_str()); - w->Key("Period"); - w->String(ToString(period).c_str()); - w->Key("ADC Mask"); - w->String(ToStringHex(adcmask).c_str()); - w->Key("Analog Flag"); - w->Uint(analog); - w->Key("Analog Samples"); - w->Uint(analogSamples); - w->Key("Digital Flag"); - w->Uint(digital); - w->Key("Digital Samples"); - w->Uint(digitalSamples); - w->Key("Dbit Offset"); - w->Uint(dbitoffset); - w->Key("Dbit Reorder"); - w->Uint(dbitreorder); - w->Key("Dbit Bitset"); - w->Uint64(dbitlist); - w->Key("Transceiver Mask"); - w->String(ToStringHex(transceiverMask).c_str()); - w->Key("Transceiver Flag"); - w->Uint(transceiver); - w->Key("Transceiver Samples"); - w->Uint(transceiverSamples); -} - -#ifdef HDF5C -void MasterAttributes::WriteXilinxCtbHDF5Attributes(H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(group); - MasterAttributes::WriteHDF5Period(group); - MasterAttributes::WriteHDF5AdcMask(group); - MasterAttributes::WriteHDF5AnalogFlag(group); - MasterAttributes::WriteHDF5AnalogSamples(group); - MasterAttributes::WriteHDF5DigitalFlag(group); - MasterAttributes::WriteHDF5DigitalSamples(group); - MasterAttributes::WriteHDF5DbitOffset(group); - MasterAttributes::WriteHDF5DbitReorder(group); - MasterAttributes::WriteHDF5DbitList(group); - MasterAttributes::WriteHDF5TransceiverMask(group); - MasterAttributes::WriteHDF5TransceiverFlag(group); - MasterAttributes::WriteHDF5TransceiverSamples(group); -} -#endif } // namespace sls diff --git a/slsReceiverSoftware/src/MasterAttributes.h b/slsReceiverSoftware/src/MasterAttributes.h index f195ee70a..d8a188671 100644 --- a/slsReceiverSoftware/src/MasterAttributes.h +++ b/slsReceiverSoftware/src/MasterAttributes.h @@ -4,12 +4,14 @@ #include "receiver_defs.h" #include "sls/ToString.h" +#include "sls/TypeTraits.h" #include "sls/logger.h" #include "sls/sls_detector_defs.h" #include #include #include +#include #ifdef HDF5C #include "H5Cpp.h" @@ -18,6 +20,7 @@ namespace sls { using ns = std::chrono::nanoseconds; +using writer = rapidjson::PrettyWriter; class MasterAttributes { public: @@ -25,7 +28,7 @@ class MasterAttributes { slsDetectorDefs::detectorType detType{slsDetectorDefs::GENERIC}; slsDetectorDefs::timingMode timingMode{slsDetectorDefs::AUTO_TIMING}; slsDetectorDefs::xy geometry{}; - uint32_t imageSize{0}; + int imageSize{0}; slsDetectorDefs::xy nPixels{}; uint32_t maxFramesPerFile{0}; slsDetectorDefs::frameDiscardPolicy frameDiscardMode{ @@ -37,123 +40,413 @@ class MasterAttributes { ns period{0}; slsDetectorDefs::burstMode burstMode{slsDetectorDefs::BURST_INTERNAL}; int numUDPInterfaces{0}; - uint32_t dynamicRange{0}; - uint32_t tenGiga{0}; + int dynamicRange{0}; + int tenGiga{0}; int thresholdEnergyeV{0}; std::array thresholdAllEnergyeV = {{0, 0, 0}}; ns subExptime{0}; ns subPeriod{0}; - uint32_t quad{0}; - uint32_t readNRows; + int quad{0}; + int readNRows; std::vector ratecorr; - uint32_t adcmask{0}; - uint32_t analog{0}; - uint32_t analogSamples{0}; - uint32_t digital{0}; - uint32_t digitalSamples{0}; - uint32_t dbitreorder{1}; - uint32_t dbitoffset{0}; - uint64_t dbitlist{0}; - uint32_t transceiverMask{0}; - uint32_t transceiver{0}; - uint32_t transceiverSamples{0}; + uint32_t adcMask{0}; + int analog{0}; + int analogSamples{0}; + int digital{0}; + int digitalSamples{0}; + int dbitReorder{1}; + int dbitOffset{0}; + uint64_t dbitList{0}; + int transceiverMask{0}; + int transceiver{0}; + int transceiverSamples{0}; std::vector rois{}; - uint32_t counterMask{0}; + int counterMask{0}; std::array exptimeArray{}; std::array gateDelayArray{}; - uint32_t gates; + int gates; std::map additionalJsonHeader; uint64_t framesInFile{0}; + slsDetectorDefs::speedLevel readoutSpeed{slsDetectorDefs::FULL_SPEED}; + + inline static const std::string_view N_DETECTOR_TYPE = "Detector Type"; + inline static const std::string_view N_TIMING_MODE = "Timing Mode"; + inline static const std::string_view N_GEOMETRY = "Geometry"; + inline static const std::string_view N_IMAGE_SIZE = "Image Size"; + inline static const std::string_view N_PIXELS = "Pixels"; + inline static const std::string_view N_MAX_FRAMES_PER_FILE = + "Max Frames Per File"; + inline static const std::string_view N_FRAME_DISCARD_POLICY = + "Frame Discard Policy"; + inline static const std::string_view N_FRAME_PADDING = "Frame Padding"; + inline static const std::string_view N_TOTAL_FRAMES = "Total Frames"; + inline static const std::string_view N_FRAMES_IN_FILE = "Frames in File"; + inline static const std::string_view N_EXPOSURE_TIME = "Exposure Time"; + inline static const std::string_view N_ACQUISITION_PERIOD = + "Acquisition Period"; + inline static const std::string_view N_NUM_UDP_INTERFACES = + "Number of UDP Interfaces"; + inline static const std::string_view N_NUMBER_OF_ROWS = "Number of Rows"; + inline static const std::string_view N_READOUT_SPEED = "Readout Speed"; + inline static const std::string_view N_DYNAMIC_RANGE = "Dynamic Range"; + inline static const std::string_view N_TEN_GIGA = "Ten Giga"; + inline static const std::string_view N_THRESHOLD_ENERGY = + "Threshold Energy"; + inline static const std::string_view N_SUB_EXPOSURE_TIME = + "Sub Exposure Time"; + inline static const std::string_view N_SUB_ACQUISITION_PERIOD = + "Sub Acquisition Period"; + inline static const std::string_view N_QUAD = "Quad"; + inline static const std::string_view N_RATE_CORRECTIONS = + "Rate Corrections"; + inline static const std::string_view N_COUNTER_MASK = "Counter Mask"; + inline static const std::string_view N_EXPOSURE_TIMES = "Exposure Times"; + inline static const std::string_view N_GATE_DELAYS = "Gate Delays"; + inline static const std::string_view N_GATES = "Gates"; + inline static const std::string_view N_THRESHOLD_ENERGIES = + "Threshold Energies"; + inline static const std::string_view N_BURST_MODE = "Burst Mode"; + inline static const std::string_view N_ADC_MASK = "ADC Mask"; + inline static const std::string_view N_ANALOG = "Analog Flag"; + inline static const std::string_view N_ANALOG_SAMPLES = "Analog Samples"; + inline static const std::string_view N_DIGITAL = "Digital Flag"; + inline static const std::string_view N_DIGITAL_SAMPLES = "Digital Samples"; + inline static const std::string_view N_DBIT_REORDER = "Dbit Reorder"; + inline static const std::string_view N_DBIT_OFFSET = "Dbit Offset"; + inline static const std::string_view N_DBIT_BITSET = "Dbit Bitset"; + inline static const std::string_view N_TRANSCEIVER_MASK = + "Transceiver Mask"; + inline static const std::string_view N_TRANSCEIVER = "Transceiver Flag"; + inline static const std::string_view N_TRANSCEIVER_SAMPLES = + "Transceiver Samples"; + inline static const std::string_view N_VERSION = "Version"; + inline static const std::string_view N_TIMESTAMP = "Timestamp"; + inline static const std::string_view N_RECEIVER_ROIS = "Receiver Rois"; + inline static const std::string_view N_SCAN_PARAMETERS = "Scan Parameters"; + inline static const std::string_view N_ADDITIONAL_JSON_HEADER = + "Additional JSON Header"; MasterAttributes() = default; ~MasterAttributes() = default; - void - GetBinaryAttributes(rapidjson::PrettyWriter *w); + void GetBinaryAttributes(writer *w); #ifdef HDF5C void WriteHDF5Attributes(H5::H5File *fd, H5::Group *group); #endif - void GetCommonBinaryAttributes( - rapidjson::PrettyWriter *w); - void GetFinalBinaryAttributes( - rapidjson::PrettyWriter *w); - void GetBinaryRois(rapidjson::PrettyWriter *w); + void GetCommonBinaryAttributes(writer *w); + void GetFinalBinaryAttributes(writer *w); + #ifdef HDF5C void WriteCommonHDF5Attributes(H5::H5File *fd, H5::Group *group); void WriteFinalHDF5Attributes(H5::Group *group); - void WriteHDF5ROIs(H5::Group *group); - void WriteHDF5Exptime(H5::Group *group); - void WriteHDF5Period(H5::Group *group); - void WriteHDF5DynamicRange(H5::Group *group); - void WriteHDF5TenGiga(H5::Group *group); - void WriteHDF5NumUDPInterfaces(H5::Group *group); - void WriteHDF5ReadNRows(H5::Group *group); - void WriteHDF5ThresholdEnergy(H5::Group *group); - void WriteHDF5ThresholdEnergies(H5::Group *group); - void WriteHDF5SubExpTime(H5::Group *group); - void WriteHDF5SubPeriod(H5::Group *group); - void WriteHDF5SubQuad(H5::Group *group); - void WriteHDF5RateCorrections(H5::Group *group); - void WriteHDF5CounterMask(H5::Group *group); - void WriteHDF5ExptimeArray(H5::Group *group); - void WriteHDF5GateDelayArray(H5::Group *group); - void WriteHDF5Gates(H5::Group *group); - void WriteHDF5BurstMode(H5::Group *group); - void WriteHDF5AdcMask(H5::Group *group); - void WriteHDF5AnalogFlag(H5::Group *group); - void WriteHDF5AnalogSamples(H5::Group *group); - void WriteHDF5DigitalFlag(H5::Group *group); - void WriteHDF5DigitalSamples(H5::Group *group); - void WriteHDF5DbitOffset(H5::Group *group); - void WriteHDF5DbitList(H5::Group *group); - void WriteHDF5DbitReorder(H5::Group *group); - void WriteHDF5TransceiverMask(H5::Group *group); - void WriteHDF5TransceiverFlag(H5::Group *group); - void WriteHDF5TransceiverSamples(H5::Group *group); #endif - void GetJungfrauBinaryAttributes( - rapidjson::PrettyWriter *w); + void GetJungfrauBinaryAttributes(writer *w); #ifdef HDF5C void WriteJungfrauHDF5Attributes(H5::Group *group); #endif - void GetEigerBinaryAttributes( - rapidjson::PrettyWriter *w); + void GetEigerBinaryAttributes(writer *w); #ifdef HDF5C void WriteEigerHDF5Attributes(H5::Group *group); #endif - void GetMythen3BinaryAttributes( - rapidjson::PrettyWriter *w); + void GetMythen3BinaryAttributes(writer *w); #ifdef HDF5C void WriteMythen3HDF5Attributes(H5::Group *group); #endif - void GetGotthard2BinaryAttributes( - rapidjson::PrettyWriter *w); + void GetGotthard2BinaryAttributes(writer *w); #ifdef HDF5C void WriteGotthard2HDF5Attributes(H5::Group *group); #endif - void GetMoenchBinaryAttributes( - rapidjson::PrettyWriter *w); + void GetMoenchBinaryAttributes(writer *w); #ifdef HDF5C void WriteMoenchHDF5Attributes(H5::Group *group); #endif - void - GetCtbBinaryAttributes(rapidjson::PrettyWriter *w); + void GetCtbBinaryAttributes(writer *w); #ifdef HDF5C void WriteCtbHDF5Attributes(H5::Group *group); #endif - void GetXilinxCtbBinaryAttributes( - rapidjson::PrettyWriter *w); + void GetXilinxCtbBinaryAttributes(writer *w); #ifdef HDF5C void WriteXilinxCtbHDF5Attributes(H5::Group *group); #endif + + void WriteBinaryDetectorType(writer *w); +#ifdef HDF5C + void WriteHDF5DetectorType(H5::Group *group); +#endif + void WriteBinaryTimingMode(writer *w); +#ifdef HDF5C + void WriteHDF5TimingMode(H5::Group *group); +#endif + void WriteBinaryGeometry(writer *w); +#ifdef HDF5C + void WriteHDF5Geometry(H5::Group *group); +#endif + void WriteBinaryImageSize(writer *w); +#ifdef HDF5C + void WriteHDF5ImageSize(H5::Group *group); +#endif + void WriteBinaryPixels(writer *w); +#ifdef HDF5C + void WriteHDF5Pixels(H5::Group *group); +#endif + void WriteBinaryMaxFramesPerFile(writer *w); +#ifdef HDF5C + void WriteHDF5MaxFramesPerFile(H5::Group *group); +#endif + void WriteBinaryFrameDiscardPolicy(writer *w); +#ifdef HDF5C + void WriteHDF5FrameDiscardPolicy(H5::Group *group); +#endif + void WriteBinaryFramePadding(writer *w); +#ifdef HDF5C + void WriteHDF5FramePadding(H5::Group *group); +#endif + void WriteBinaryTotalFrames(writer *w); +#ifdef HDF5C + void WriteHDF5TotalFrames(H5::Group *group); +#endif + void WriteBinaryFramesInFile(writer *w); +#ifdef HDF5C + void WriteHDF5FramesInFile(H5::Group *group); +#endif + + void WriteBinaryExposureTme(writer *w); +#ifdef HDF5C + void WriteHDF5ExposureTime(H5::Group *group); +#endif + void WriteBinaryAcquisitionPeriod(writer *w); +#ifdef HDF5C + void WriteHDF5AcquisitionPeriod(H5::Group *group); +#endif + void WriteBinaryNumberOfUDPInterfaces(writer *w); +#ifdef HDF5C + void WriteHDF5NumberOfUDPInterfaces(H5::Group *group); +#endif + void WriteBinaryNumberOfRows(writer *w); +#ifdef HDF5C + void WriteHDF5NumberOfRows(H5::Group *group); +#endif + void WriteBinaryReadoutSpeed(writer *w); +#ifdef HDF5C + void WriteHDF5ReadoutSpeed(H5::Group *group); +#endif + void WriteBinaryDynamicRange(writer *w); +#ifdef HDF5C + void WriteHDF5DynamicRange(H5::Group *group); +#endif + void WriteBinaryTenGiga(writer *w); +#ifdef HDF5C + void WriteHDF5TenGiga(H5::Group *group); +#endif + void WriteBinaryThresholdEnergy(writer *w); +#ifdef HDF5C + void WriteHDF5ThresholdEnergy(H5::Group *group); +#endif + void WriteBinarySubExposureTime(writer *w); +#ifdef HDF5C + void WriteHDF5SubExposureTime(H5::Group *group); +#endif + void WriteBinarySubAcquisitionPeriod(writer *w); +#ifdef HDF5C + void WriteHDF5SubAcquisitionPeriod(H5::Group *group); +#endif + void WriteBinaryQuad(writer *w); +#ifdef HDF5C + void WriteHDF5Quad(H5::Group *group); +#endif + void WriteBinaryRateCorrections(writer *w); +#ifdef HDF5C + void WriteHDF5RateCorrections(H5::Group *group); +#endif + void WriteBinaryCounterMask(writer *w); +#ifdef HDF5C + void WriteHDF5CounterMask(H5::Group *group); +#endif + void WriteBinaryExptimeArray(writer *w); +#ifdef HDF5C + void WriteHDF5ExptimeArray(H5::Group *group); +#endif + void WriteBinaryGateDelayArray(writer *w); +#ifdef HDF5C + void WriteHDF5GateDelayArray(H5::Group *group); +#endif + void WriteBinaryGates(writer *w); +#ifdef HDF5C + void WriteHDF5Gates(H5::Group *group); +#endif + void WriteBinaryThresholdAllEnergy(writer *w); +#ifdef HDF5C + void WriteHDF5ThresholdAllEnergy(H5::Group *group); +#endif + void WriteBinaryBurstMode(writer *w); +#ifdef HDF5C + void WriteHDF5BurstMode(H5::Group *group); +#endif + void WriteBinaryAdcMask(writer *w); +#ifdef HDF5C + void WriteHDF5AdcMask(H5::Group *group); +#endif + void WriteBinaryAnalogFlag(writer *w); +#ifdef HDF5C + void WriteHDF5AnalogFlag(H5::Group *group); +#endif + void WriteBinaryAnalogSamples(writer *w); +#ifdef HDF5C + void WriteHDF5AnalogSamples(H5::Group *group); +#endif + void WriteBinaryDigitalFlag(writer *w); +#ifdef HDF5C + void WriteHDF5DigitalFlag(H5::Group *group); +#endif + void WriteBinaryDigitalSamples(writer *w); +#ifdef HDF5C + void WriteHDF5DigitalSamples(H5::Group *group); +#endif + void WriteBinaryDBitReorder(writer *w); +#ifdef HDF5C + void WriteHDF5DBitReorder(H5::Group *group); +#endif + void WriteBinaryDBitOffset(writer *w); +#ifdef HDF5C + void WriteHDF5DBitOffset(H5::Group *group); +#endif + void WriteBinaryDBitBitset(writer *w); +#ifdef HDF5C + void WriteHDF5DBitBitset(H5::Group *group); +#endif + void WriteBinaryTransceiverMask(writer *w); +#ifdef HDF5C + void WriteHDF5TransceiverMask(H5::Group *group); +#endif + void WriteBinaryTransceiverFlag(writer *w); +#ifdef HDF5C + void WriteHDF5TransceiverFlag(H5::Group *group); +#endif + void WriteBinaryTransceiverSamples(writer *w); +#ifdef HDF5C + void WriteHDF5TransceiverSamples(H5::Group *group); +#endif + + /** writes according to type */ + template void WriteBinaryValue(writer *w, const T &value) { + if constexpr (std::is_same_v) { + w->Int(value); + } else if constexpr (std::is_same_v) { + w->Uint64(value); + } else if constexpr (std::is_same_v) { + w->Int64(value); + } else if constexpr (std::is_same_v) { + w->Uint(value); + } else if constexpr (std::is_same_v) { + w->String(value.c_str()); + } else if constexpr (is_duration::value) { + w->String(ToString(value).c_str()); + } else { + throw RuntimeError("Unsupported type for Binary write: " + + std::string(typeid(T).name())); + } + } + + /** For non-arrays */ + template + std::enable_if_t<(!std::is_class_v || std::is_same_v), + void> + WriteBinary(writer *w, const std::string &name, const T &value) { + w->Key(name.c_str()); + WriteBinaryValue(w, value); + } + + /** For arrays */ + template + std::enable_if_t<(std::is_class_v && !std::is_same_v), + void> + WriteBinary(writer *w, const std::string &name, const T &value) { + w->Key(name.c_str()); + w->StartArray(); + for (const auto &v : value) { + WriteBinaryValue(w, v); + } + w->EndArray(); + } + +#ifdef HDF5C + void WriteHDF5String(H5::Group *group, const std::string &name, + const std::string &value); + void WriteHDF5StringArray(H5::Group *group, const std::string &name, + const std::vector &value); + + /** get type */ + template H5::PredType const *GetHDF5Type() { + if constexpr (std::is_same_v) { + return &H5::PredType::NATIVE_INT; + } else if constexpr (std::is_same_v) { + return &H5::PredType::STD_U64LE; + } else if constexpr (std::is_same_v) { + return &H5::PredType::STD_I64LE; + } else if constexpr (std::is_same_v) { + return &H5::PredType::STD_U32LE; + } else { + throw RuntimeError("Unsupported type for HDF5"); + } + } + + /** For non-arrays */ + template + typename std::enable_if::value, void>::type + WriteHDF5Int(H5::Group *group, const std::string &name, const T &value) { + H5::DataSpace dataspace(H5S_SCALAR); + auto h5type = GetHDF5Type(); + H5::DataSet dataset = group->createDataSet(name, *h5type, dataspace); + dataset.write(&value, *h5type); + } + + /** For arrays */ + template + typename std::enable_if::value, void>::type + WriteHDF5Int(H5::Group *group, const std::string &name, const T &value) { + using ElemT = typename T::value_type; + auto h5type = GetHDF5Type(); + hsize_t dims[1] = {value.size()}; + H5::DataSpace dataspace(1, dims); + H5::DataSet dataset = group->createDataSet(name, *h5type, dataspace); + dataset.write(value.data(), *h5type); + } + +#endif + void WriteBinaryXY(writer *w, const std::string &name, const defs::xy &xy); +#ifdef HDF5C + void WriteHDF5XY(H5::Group *group, const std::string &name, + const defs::xy &xy); +#endif + void WriteBinaryVersion(writer *w); +#ifdef HDF5C + void WriteHDF5Version(H5::H5File *fd); +#endif + void WriteBinaryTimestamp(writer *w); +#ifdef HDF5C + void WriteHDF5Timestamp(H5::Group *group); +#endif + void WriteBinaryRois(writer *w); +#ifdef HDF5C + void WriteHDF5ROIs(H5::Group *group); +#endif + void WriteBinaryScanParameters(writer *w); +#ifdef HDF5C + void WriteHDF5ScanParameters(H5::Group *group); +#endif + void WriteBinaryJsonHeader(writer *w); +#ifdef HDF5C + void WriteHDF5JsonHeader(H5::Group *group); +#endif + void WriteBinaryFrameHeaderFormat(writer *w); }; } // namespace sls diff --git a/slsReceiverSoftware/src/MasterFileUtility.cpp b/slsReceiverSoftware/src/MasterFileUtility.cpp index 08412ace9..a4e782744 100644 --- a/slsReceiverSoftware/src/MasterFileUtility.cpp +++ b/slsReceiverSoftware/src/MasterFileUtility.cpp @@ -140,13 +140,6 @@ std::string CreateMasterHDF5File(const std::string &filePath, fd = make_unique(fileName.c_str(), createFlags, H5::FileCreatPropList::DEFAULT, flist); - // attributes - version - double dValue = HDF5_WRITER_VERSION; - H5::DataSpace dataspace_attr = H5::DataSpace(H5S_SCALAR); - H5::Attribute attribute = fd->createAttribute( - "version", H5::PredType::NATIVE_DOUBLE, dataspace_attr); - attribute.write(H5::PredType::NATIVE_DOUBLE, &dValue); - // Create a group in the file H5::Group group1(fd->createGroup("entry")); H5::Group group2(group1.createGroup("data")); @@ -230,13 +223,6 @@ std::string CreateVirtualHDF5File( fd = make_unique(fileName.c_str(), H5F_ACC_TRUNC, H5::FileCreatPropList::DEFAULT, fapl); - // attributes - version - double dValue = HDF5_WRITER_VERSION; - H5::DataSpace dataspace_attr = H5::DataSpace(H5S_SCALAR); - H5::Attribute attribute = fd->createAttribute( - "version", H5::PredType::NATIVE_DOUBLE, dataspace_attr); - attribute.write(H5::PredType::NATIVE_DOUBLE, &dValue); - for (size_t iRoi = 0; iRoi != multiRoi.size(); ++iRoi) { auto currentRoi = multiRoi[iRoi]; diff --git a/slsSupportLib/include/sls/sls_detector_defs.h b/slsSupportLib/include/sls/sls_detector_defs.h index 942774bbf..d028d4238 100644 --- a/slsSupportLib/include/sls/sls_detector_defs.h +++ b/slsSupportLib/include/sls/sls_detector_defs.h @@ -126,6 +126,9 @@ class slsDetectorDefs { int y{0}; xy() = default; xy(int x, int y) : x(x), y(y){}; + constexpr bool operator==(const xy &other) const { + return ((x == other.x) && (y == other.y)); + } } __attribute__((packed)); #endif @@ -548,7 +551,7 @@ enum streamingInterface { stepSize(step) { dacSettleTime_ns = t.count(); } - bool operator==(const scanParameters &other) const { + constexpr bool operator==(const scanParameters &other) const { return ((enable == other.enable) && (dacInd == other.dacInd) && (startOffset == other.startOffset) && (stopOffset == other.stopOffset) && @@ -663,6 +666,7 @@ enum streamingInterface { scanParameters scanParams{}; int transceiverSamples{0}; uint32_t transceiverMask{0}; + speedLevel readoutSpeed{FULL_SPEED}; } __attribute__((packed)); #endif diff --git a/slsSupportLib/include/sls/sls_detector_funcs.h b/slsSupportLib/include/sls/sls_detector_funcs.h index 20688941c..6d49fee7e 100755 --- a/slsSupportLib/include/sls/sls_detector_funcs.h +++ b/slsSupportLib/include/sls/sls_detector_funcs.h @@ -413,6 +413,7 @@ enum detFuncs { F_GET_RECEIVER_DBIT_REORDER, F_SET_RECEIVER_DBIT_REORDER, F_RECEIVER_GET_ROI_METADATA, + F_SET_RECEIVER_READOUT_SPEED, NUM_REC_FUNCTIONS }; @@ -822,6 +823,7 @@ const char* getFunctionNameFromEnum(enum detFuncs func) { case F_GET_RECEIVER_DBIT_REORDER: return "F_GET_RECEIVER_DBIT_REORDER"; case F_SET_RECEIVER_DBIT_REORDER: return "F_SET_RECEIVER_DBIT_REORDER"; case F_RECEIVER_GET_ROI_METADATA: return "F_RECEIVER_GET_ROI_METADATA"; + case F_SET_RECEIVER_READOUT_SPEED: return "F_SET_RECEIVER_READOUT_SPEED"; case NUM_REC_FUNCTIONS: return "NUM_REC_FUNCTIONS"; default: return "Unknown Function"; diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index d88a71ba8..cd6f8473c 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -3,10 +3,10 @@ /** API versions */ #define APILIB "0.0.0 0x250523" #define APIRECEIVER "0.0.0 0x250523" -#define APICTB "0.0.0 0x250523" -#define APIGOTTHARD2 "0.0.0 0x250523" -#define APIMOENCH "0.0.0 0x250523" -#define APIEIGER "0.0.0 0x250523" -#define APIXILINXCTB "0.0.0 0x250523" -#define APIJUNGFRAU "0.0.0 0x250523" -#define APIMYTHEN3 "0.0.0 0x250523" +#define APICTB "0.0.0 0x250714" +#define APIGOTTHARD2 "0.0.0 0x250714" +#define APIMOENCH "0.0.0 0x250714" +#define APIEIGER "0.0.0 0x250714" +#define APIXILINXCTB "0.0.0 0x250714" +#define APIJUNGFRAU "0.0.0 0x250714" +#define APIMYTHEN3 "0.0.0 0x250714" diff --git a/slsSupportLib/src/ToString.cpp b/slsSupportLib/src/ToString.cpp index 212514f6f..98790f7cd 100644 --- a/slsSupportLib/src/ToString.cpp +++ b/slsSupportLib/src/ToString.cpp @@ -89,6 +89,7 @@ std::string ToString(const slsDetectorDefs::rxParameters &r) { << "scanParams:" << ToString(r.scanParams) << std::endl << "transceiverSamples:" << r.transceiverSamples << std::endl << "transceiverMask:" << r.transceiverMask << std::endl + << "readoutSpeed:" << ToString(r.readoutSpeed) << std::endl << ']'; return oss.str(); } diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index 8d2915f0a..ba9bd7e67 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -23,6 +23,7 @@ from utils_for_test import ( checkLogForErrors, startDetectorVirtualServer, loadConfig, + loadBasicSettings, ParseArguments ) @@ -113,6 +114,7 @@ def startTestsForAll(args, fp): startFrameSynchronizerPullSocket(server, fp) startFrameSynchronizer(args.num_mods, fp) d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=args.num_mods, num_frames=args.num_frames) + loadBasicSettings(name=server, d=d, fp=fp) acquire(fp, d) testFramesCaught(server, d, args.num_frames) testZmqHeadetTypeCount(server, d, args.num_mods, args.num_frames, fp) diff --git a/tests/scripts/test_roi.py b/tests/scripts/test_roi.py index 2f56f31b1..ffb0ef686 100644 --- a/tests/scripts/test_roi.py +++ b/tests/scripts/test_roi.py @@ -7,8 +7,9 @@ This file is used to start up simulators, receivers and test roi for every detec import sys, time import traceback -from slsdet import Detector +from slsdet import Detector, burstMode from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO +from datetime import timedelta from utils_for_test import ( @@ -19,6 +20,7 @@ from utils_for_test import ( startProcessInBackground, startDetectorVirtualServer, connectToVirtualServers, + loadBasicSettings, runProcessWithLogFile ) @@ -88,6 +90,7 @@ def startTestsForAll(fp): startDetectorVirtualServer(server, nmods, fp) startReceiver(nmods, fp) d = loadConfigForRoi(name=server, fp=fp, num_mods=nmods, num_interfaces=ninterfaces) + loadBasicSettings(name=server, d=d, fp=fp) fname = ROI_TEST_FNAME + server + '.txt' cmd = ['tests', 'rx_roi', '--abort', '-s'] diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index d6f4371d9..3b5dbdd4a 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -21,6 +21,7 @@ from utils_for_test import ( runProcessWithLogFile, startDetectorVirtualServer, loadConfig, + loadBasicSettings, ParseArguments ) @@ -56,14 +57,15 @@ def startCmdTestsForAll(args, fp): try: num_mods = 2 if server == 'eiger' else 1 fname = CMD_TEST_LOG_PREFIX_FNAME + server + '.txt' - cmd = ['tests', '--abort', '[.cmdcall]', '-s'] + cmd = ['tests', '--abort', args.markers, '-s'] Log(LogLevel.INFOBLUE, f'Starting Cmd Tests for {server}') cleanup(fp) startDetectorVirtualServer(name=server, num_mods=num_mods, fp=fp) startReceiver(num_mods, fp) - loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=num_mods) - runProcessWithLogFile('Cmd Tests for ' + server, cmd, fp, fname) + d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=num_mods) + loadBasicSettings(name=server, d=d, fp=fp) + runProcessWithLogFile('Cmd Tests (' + args.markers + ') for ' + server, cmd, fp, fname) except Exception as e: raise RuntimeException(f'Cmd Tests failed for {server}.') from e @@ -71,7 +73,7 @@ def startCmdTestsForAll(args, fp): if __name__ == '__main__': - args = ParseArguments('Automated tests with the virtual detector servers') + args = ParseArguments('Automated tests with the virtual detector servers', 1, 1) if args.num_mods > 1: raise RuntimeException(f'Cannot support multiple modules at the moment (except Eiger).') diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 1d64129cf..5f2a22eff 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -7,8 +7,9 @@ This file is used for common utils used for integration tests between simulators import sys, subprocess, time, argparse from enum import Enum from colorama import Fore, Style, init +from datetime import timedelta -from slsdet import Detector, detectorSettings +from slsdet import Detector, detectorSettings, burstMode from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO SERVER_START_PORTNO=1900 @@ -156,7 +157,7 @@ def startDetectorVirtualServer(name :str, num_mods, fp): for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) cmd = [name + 'DetectorServer_virtual', '-p', str(port_no)] - startProcessInBackgroundWithLogFile(cmd, fp, "/tmp/virtual_det_" + name + str(i) + ".txt") + startProcessInBackgroundWithLogFile(cmd, fp, "/tmp/virtual_det_" + name + "_" + str(i) + ".txt") match name: case 'jungfrau': time.sleep(7) @@ -215,13 +216,43 @@ def loadConfig(name, rx_hostname, settingsdir, fp, num_mods = 1, num_frames = 1) d.setThresholdEnergy(4500, detectorSettings.STANDARD) d.frames = num_frames + except Exception as e: raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e return d +# for easy acquire +def loadBasicSettings(name, d, fp): + Log(LogLevel.INFO, 'Loading basic settings for ' + name) + Log(LogLevel.INFO, 'Loading basic settings for ' + name, fp) + try: + # basic settings for easy acquire + if name == "jungfrau": + d.exptime = timedelta(microseconds = 200) + d.readnrows = 512 + elif name == "moench": + d.exptime = timedelta(microseconds = 200) + d.readnrows = 400 + elif name == "eiger": + d.exptime = timedelta(microseconds = 200) + d.readnrows = 256 + d.dr = 16 + elif name == "mythen3": + d.setExptime(-1, timedelta(microseconds = 200)) + d.dr = 16 + d.counters = [0, 1] + elif name == "gotthard2": + d.exptime = timedelta(microseconds = 200) + d.burstmode = burstMode.CONTINUOUS_EXTERNAL + d.bursts = 1 + d.burstperiod = 0 + d.period = timedelta(milliseconds = 2) -def ParseArguments(description, default_num_mods=1): + except Exception as e: + raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e + +def ParseArguments(description, default_num_mods=1, markers=0): parser = argparse.ArgumentParser(description) parser.add_argument('rx_hostname', nargs='?', default='localhost', @@ -234,6 +265,9 @@ def ParseArguments(description, default_num_mods=1): help='Number of frames to test with') parser.add_argument('-s', '--servers', nargs='*', help='Detector servers to run') + if markers == 1: + parser.add_argument('-m', '--markers', nargs='?', default ='[.cmdcall]', + help = 'Markers to use for cmd tests, default: [.cmdcall]') args = parser.parse_args() @@ -249,11 +283,17 @@ def ParseArguments(description, default_num_mods=1): 'xilinx_ctb' ] - Log(LogLevel.INFO, 'Arguments:\n' + - 'rx_hostname: ' + args.rx_hostname + - '\nsettingspath: \'' + args.settingspath + - '\nservers: \'' + ' '.join(args.servers) + - '\nnum_mods: \'' + str(args.num_mods) + - '\nnum_frames: \'' + str(args.num_frames) + '\'') + msg = ( + 'Arguments:\n' + f'rx_hostname: {args.rx_hostname}\n' + f"settingspath: '{args.settingspath}'\n" + f"servers: '{' '.join(args.servers)}'\n" + f"num_mods: '{args.num_mods}'\n" + f"num_frames: '{args.num_frames}'" + ) + if markers == 1: + msg += f"\nmarkers: '{args.markers}'" + Log(LogLevel.INFO, msg) + return args