From 6746ab3a8befcfa75efe3af271a90ec71aa1395c Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 14 Jul 2026 13:21:50 -0500 Subject: [PATCH 01/15] JCF: Issue #59: update variable names to conform to Style Guide; all-caps is reserved for reference to #defines and macros --- .../trgdataformats/TriggerActivityData.hpp | 17 ++++----- .../trgdataformats/TriggerCandidateData.hpp | 6 ++-- include/trgdataformats/TriggerPrimitive.hpp | 15 ++++---- include/trgdataformats/Types.hpp | 35 +++++++------------ 4 files changed, 34 insertions(+), 39 deletions(-) diff --git a/include/trgdataformats/TriggerActivityData.hpp b/include/trgdataformats/TriggerActivityData.hpp index 3befff1..9690042 100644 --- a/include/trgdataformats/TriggerActivityData.hpp +++ b/include/trgdataformats/TriggerActivityData.hpp @@ -41,18 +41,19 @@ struct TriggerActivityData // Update this version number if there are any changes to the in-memory representation of this class! static constexpr version_t s_trigger_activity_version = 2; // NOLINT(build/unsigned) + static constexpr channel_t s_invalid_channel = std::numeric_limits::max(); version_t version = s_trigger_activity_version; // NOLINT(build/unsigned) - timestamp_t time_start = INVALID_TIMESTAMP; - timestamp_t time_end = INVALID_TIMESTAMP; - timestamp_t time_peak = INVALID_TIMESTAMP; - timestamp_t time_activity = INVALID_TIMESTAMP; - channel_t channel_start = INVALID_CHANNEL; // NOLINT(build/unsigned) - channel_t channel_end = INVALID_CHANNEL; // NOLINT(build/unsigned) - channel_t channel_peak = INVALID_CHANNEL; // NOLINT(build/unsigned) + timestamp_t time_start = TypeDefaults::s_invalid_timestamp; + timestamp_t time_end = TypeDefaults::s_invalid_timestamp; + timestamp_t time_peak = TypeDefaults::s_invalid_timestamp; + timestamp_t time_activity = TypeDefaults::s_invalid_timestamp; + channel_t channel_start = s_invalid_channel; // NOLINT(build/unsigned) + channel_t channel_end = s_invalid_channel; // NOLINT(build/unsigned) + channel_t channel_peak = s_invalid_channel; // NOLINT(build/unsigned) uint64_t adc_integral = 0; // NOLINT(build/unsigned) uint16_t adc_peak = 0; // NOLINT(build/unsigned) - detid_t detid = INVALID_DETID; // NOLINT(build/unsigned) + detid_t detid = TypeDefaults::s_invalid_detid; // NOLINT(build/unsigned) Type type = Type::kUnknown; // NOLINT(build/unsigned) Algorithm algorithm = Algorithm::kUnknown; // NOLINT(build/unsigned) }; diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index a129fd9..a3900ed 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -83,9 +83,9 @@ struct TriggerCandidateData static constexpr version_t s_trigger_candidate_version = 3; // NOLINT(build/unsigned) version_t version = s_trigger_candidate_version; // NOLINT(build/unsigned) - timestamp_t time_start = INVALID_TIMESTAMP; - timestamp_t time_end = INVALID_TIMESTAMP; - timestamp_t time_candidate = INVALID_TIMESTAMP; + timestamp_t time_start = TypeDefaults::s_invalid_timestamp; + timestamp_t time_end = TypeDefaults::s_invalid_timestamp; + timestamp_t time_candidate = TypeDefaults::s_invalid_timestamp; // TODO P. Rodrigues 2021-01-06: This was originally a // std::vector but that messes up the overlay scheme, so // I've changed it for now to be just a detid_t. Need to work out diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index af28c29..439fafc 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -26,7 +26,10 @@ namespace dunedaq::trgdataformats { struct TriggerPrimitive { static constexpr uint8_t s_trigger_primitive_version = 2; - + static constexpr uint16_t s_invalid_samples_over_threshold = std::numeric_limits::max(); + static constexpr uint16_t s_invalid_samples_to_peak = std::numeric_limits::max(); + static constexpr channel_t s_invalid_tp_channel = 0xFFFFFF; // TP channel limit is at 24 b + // Metadata. uint64_t version : 8; uint64_t flag : 8; @@ -45,11 +48,11 @@ struct TriggerPrimitive TriggerPrimitive() : version(s_trigger_primitive_version) , flag(0) - , detid(INVALID_DETID) - , channel(INVALID_TP_CHANNEL) - , samples_over_threshold(INVALID_SAMPLES_OVER_THRESHOLD) - , time_start(INVALID_TIMESTAMP) - , samples_to_peak(INVALID_SAMPLES_TO_PEAK) + , detid(TypeDefaults::s_invalid_detid) + , channel(s_invalid_tp_channel) + , samples_over_threshold(s_invalid_samples_over_threshold) + , time_start(TypeDefaults::s_invalid_timestamp) + , samples_to_peak(s_invalid_samples_to_peak) , adc_integral(0) , adc_peak(0) {} diff --git a/include/trgdataformats/Types.hpp b/include/trgdataformats/Types.hpp index c265998..35c8ddc 100644 --- a/include/trgdataformats/Types.hpp +++ b/include/trgdataformats/Types.hpp @@ -18,15 +18,8 @@ namespace dunedaq::trgdataformats { // ProtoDUNE-I, 62.5 MHz for ProtoDUNE-II and DUNE FD). If/when we // depend on daqdataformats, we can get these from there using timestamp_t = uint64_t; // NOLINT - -constexpr timestamp_t INVALID_TIMESTAMP = std::numeric_limits::max(); - using timestamp_diff_t = int64_t; -constexpr uint16_t INVALID_SAMPLES_OVER_THRESHOLD = std::numeric_limits::max(); - -constexpr uint16_t INVALID_SAMPLES_TO_PEAK = std::numeric_limits::max(); - // A logical region of the detector from which DS objects may be // formed, eg an APA or a module // @@ -35,34 +28,32 @@ constexpr uint16_t INVALID_SAMPLES_TO_PEAK = std::numeric_limits::max( // package using detid_t = uint8_t; -constexpr detid_t INVALID_DETID = std::numeric_limits::max(); - -// A detid representing a request to read out the whole detector -constexpr detid_t WHOLE_DETECTOR = INVALID_DETID - 1; - // A trigger number // TODO P. Rodrigues 2021-06-14: it would be nice to have this be just // daqdataformats::trigger_number_t, if/when we can depend on the daqdataformats // package using trigger_number_t = uint64_t; -constexpr trigger_number_t INVALID_TRIGGER_NUMBER = std::numeric_limits::max(); - // A channel number using channel_t = uint32_t; - -constexpr channel_t INVALID_CHANNEL = std::numeric_limits::max(); - -// TP channel limit is at 24 b. -constexpr uint32_t INVALID_TP_CHANNEL = 0xFFFFFF; - using channel_diff_t = int32_t; -// A version number of an object + // A version number of an object using version_t = uint8_t; -constexpr version_t INVALID_VERSION = std::numeric_limits::max(); +//constexpr version_t INVALID_VERSION = std::numeric_limits::max(); + +struct TypeDefaults +{ + static constexpr timestamp_t s_invalid_timestamp { std::numeric_limits::max() }; + static constexpr detid_t s_invalid_detid { std::numeric_limits::max() }; + static constexpr trigger_number_t s_invalid_trigger_number { std::numeric_limits::max() }; +}; + +// A detid representing a request to read out the whole detector + constexpr detid_t g_whole_detector {std::numeric_limits::max() - 1}; + } // namespace dunedaq::trgdataformats #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TYPES_HPP_ From 5f4497e5018ccd8414442205d26142cb7da8bc72 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 14 Jul 2026 13:26:21 -0500 Subject: [PATCH 02/15] JCF: Issue #59: protect against the whole-detector det_id value from equaling the invalid det_id value --- include/trgdataformats/Types.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/trgdataformats/Types.hpp b/include/trgdataformats/Types.hpp index 35c8ddc..0b68f55 100644 --- a/include/trgdataformats/Types.hpp +++ b/include/trgdataformats/Types.hpp @@ -56,4 +56,6 @@ struct TypeDefaults } // namespace dunedaq::trgdataformats +static_assert(dunedaq::trgdataformats::g_whole_detector != dunedaq::trgdataformats::TypeDefaults::s_invalid_detid); + #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TYPES_HPP_ From 90d351f96b33823d938a319d9339dfa507a1e37e Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 16 Jul 2026 12:46:50 -0500 Subject: [PATCH 03/15] JCF: Issue #59: various refactors, listed below * A more systematic approach to dealing with unsigned values when it comes to our linter, including removal of NOLINT directives when the actual type is unsigned but the linter wouldn't be able to determine this * static_assert-ions for the developers benefit meant to remind them to bump the version if they add or remove variables from TriggerCandidateData or TriggerActivityData. Not bulletproof, but much better than nothing. * static declaration of map in get_trigger_candidate_type_names so that it doesn't get constructed every time the function is called * Removal of exception throwing as flow-control mechanism in trigger_candidate_type_to_string; exceptions are costly and should generally just be used for problems which are exceptional and can't be handled locally. --- .../trgdataformats/TriggerActivityData.hpp | 26 +++++++++++------ .../trgdataformats/TriggerCandidateData.hpp | 29 ++++++++++++------- .../trgdataformats/TriggerObjectOverlay.hpp | 2 +- include/trgdataformats/TriggerPrimitive.hpp | 4 +++ include/trgdataformats/Types.hpp | 10 +++---- pybindsrc/trigger_activity.cpp | 4 +++ pybindsrc/trigger_candidate.cpp | 4 +++ pybindsrc/trigger_primitive.cpp | 4 +++ 8 files changed, 57 insertions(+), 26 deletions(-) diff --git a/include/trgdataformats/TriggerActivityData.hpp b/include/trgdataformats/TriggerActivityData.hpp index 9690042..a4edd1e 100644 --- a/include/trgdataformats/TriggerActivityData.hpp +++ b/include/trgdataformats/TriggerActivityData.hpp @@ -40,24 +40,32 @@ struct TriggerActivityData }; // Update this version number if there are any changes to the in-memory representation of this class! - static constexpr version_t s_trigger_activity_version = 2; // NOLINT(build/unsigned) + static constexpr version_t s_trigger_activity_version = 2; static constexpr channel_t s_invalid_channel = std::numeric_limits::max(); - version_t version = s_trigger_activity_version; // NOLINT(build/unsigned) + version_t version = s_trigger_activity_version; timestamp_t time_start = TypeDefaults::s_invalid_timestamp; timestamp_t time_end = TypeDefaults::s_invalid_timestamp; timestamp_t time_peak = TypeDefaults::s_invalid_timestamp; timestamp_t time_activity = TypeDefaults::s_invalid_timestamp; - channel_t channel_start = s_invalid_channel; // NOLINT(build/unsigned) - channel_t channel_end = s_invalid_channel; // NOLINT(build/unsigned) - channel_t channel_peak = s_invalid_channel; // NOLINT(build/unsigned) + channel_t channel_start = s_invalid_channel; + channel_t channel_end = s_invalid_channel; + channel_t channel_peak = s_invalid_channel; uint64_t adc_integral = 0; // NOLINT(build/unsigned) uint16_t adc_peak = 0; // NOLINT(build/unsigned) - detid_t detid = TypeDefaults::s_invalid_detid; // NOLINT(build/unsigned) - Type type = Type::kUnknown; // NOLINT(build/unsigned) - Algorithm algorithm = Algorithm::kUnknown; // NOLINT(build/unsigned) + detid_t detid = TypeDefaults::s_invalid_detid; + Type type = Type::kUnknown; + Algorithm algorithm = Algorithm::kUnknown; }; - + } // namespace dunedaq::trgdataformats +// This static_assert is meant to alert the developer to bump the +// version if variables are added or removed +static_assert( + dunedaq::trgdataformats::TriggerActivityData::s_trigger_activity_version == 2 && + sizeof(dunedaq::trgdataformats::TriggerActivityData) == 80 + ); + + #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TRIGGERACTIVITYDATA_HPP_ diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index a3900ed..9dbbbb5 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -80,9 +80,9 @@ struct TriggerCandidateData }; // Update this version number if there are any changes to the in-memory representation of this class! - static constexpr version_t s_trigger_candidate_version = 3; // NOLINT(build/unsigned) + static constexpr version_t s_trigger_candidate_version = 3; - version_t version = s_trigger_candidate_version; // NOLINT(build/unsigned) + version_t version = s_trigger_candidate_version; timestamp_t time_start = TypeDefaults::s_invalid_timestamp; timestamp_t time_end = TypeDefaults::s_invalid_timestamp; timestamp_t time_candidate = TypeDefaults::s_invalid_timestamp; @@ -90,9 +90,9 @@ struct TriggerCandidateData // std::vector but that messes up the overlay scheme, so // I've changed it for now to be just a detid_t. Need to work out // what to do longer term - detid_t detid; // NOLINT(build/unsigned) + detid_t detid; Type type = Type::kUnknown; - Algorithm algorithm = Algorithm::kUnknown; // NOLINT(build/unsigned) + Algorithm algorithm = Algorithm::kUnknown; }; // This map needs to be updated for each new TC type, as this is used when configuring Trigger Bitwords, affecting @@ -100,7 +100,7 @@ struct TriggerCandidateData inline std::map get_trigger_candidate_type_names() { - return { + static const std::map map { { TriggerCandidateData::Type::kUnknown, "kUnknown" }, { TriggerCandidateData::Type::kTiming, "kTiming" }, { TriggerCandidateData::Type::kTPCLowE, "kTPCLowE" }, @@ -141,6 +141,8 @@ get_trigger_candidate_type_names() { TriggerCandidateData::Type::kDTSCosmic, "kDTSCosmic" }, { TriggerCandidateData::Type::kSSPLEDCalibration, "kSSPLEDCalibration" }, }; + + return map; } inline int @@ -156,14 +158,19 @@ string_to_trigger_candidate_type(const std::string& name) inline std::string trigger_candidate_type_to_string(const TriggerCandidateData::Type& type) { - try { - return get_trigger_candidate_type_names().at(type); - } - catch(std::exception &e) { - } - return "kUnknown"; + const auto& map { get_trigger_candidate_type_names() }; + auto elem { map.find(type) }; + + return elem != map.end() ? elem->second : "kUnknown"; } } // namespace dunedaq::trgdataformats +// This static_assert is meant to alert the developer to bump the +// version if variables are added or removed +static_assert( + dunedaq::trgdataformats::TriggerCandidateData::s_trigger_candidate_version == 3 && + sizeof(dunedaq::trgdataformats::TriggerCandidateData) == 48 + ); + #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TRIGGERCANDIDATEDATA_HPP_ diff --git a/include/trgdataformats/TriggerObjectOverlay.hpp b/include/trgdataformats/TriggerObjectOverlay.hpp index 0f909fb..567d75a 100644 --- a/include/trgdataformats/TriggerObjectOverlay.hpp +++ b/include/trgdataformats/TriggerObjectOverlay.hpp @@ -21,7 +21,7 @@ struct TriggerObjectOverlay using data_t = DataType; using input_t = InputType; data_t data; - uint64_t n_inputs; + uint64_t n_inputs; // NOLINT(build/unsigned) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" input_t inputs[]; // Non-standard flexible array member, but alternatives are worse diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 439fafc..86b6df2 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -18,6 +18,8 @@ #include #include +// NOLINTBEGIN(build/unsigned) + namespace dunedaq::trgdataformats { /** @@ -60,4 +62,6 @@ struct TriggerPrimitive } // namespace dunedaq::trgdataformats +// NOLINTEND(build/unsigned) + #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TRIGGERPRIMITIVE_HPP_ diff --git a/include/trgdataformats/Types.hpp b/include/trgdataformats/Types.hpp index 0b68f55..ee7d1c3 100644 --- a/include/trgdataformats/Types.hpp +++ b/include/trgdataformats/Types.hpp @@ -12,12 +12,14 @@ #include #include +// NOLINTBEGIN(build/unsigned) + namespace dunedaq::trgdataformats { // A data timestamp in timing system clock ticks (50 MHz for // ProtoDUNE-I, 62.5 MHz for ProtoDUNE-II and DUNE FD). If/when we // depend on daqdataformats, we can get these from there -using timestamp_t = uint64_t; // NOLINT +using timestamp_t = uint64_t; using timestamp_diff_t = int64_t; // A logical region of the detector from which DS objects may be @@ -28,7 +30,6 @@ using timestamp_diff_t = int64_t; // package using detid_t = uint8_t; -// A trigger number // TODO P. Rodrigues 2021-06-14: it would be nice to have this be just // daqdataformats::trigger_number_t, if/when we can depend on the daqdataformats // package @@ -38,11 +39,8 @@ using trigger_number_t = uint64_t; using channel_t = uint32_t; using channel_diff_t = int32_t; - // A version number of an object using version_t = uint8_t; -//constexpr version_t INVALID_VERSION = std::numeric_limits::max(); - struct TypeDefaults { static constexpr timestamp_t s_invalid_timestamp { std::numeric_limits::max() }; @@ -58,4 +56,6 @@ struct TypeDefaults static_assert(dunedaq::trgdataformats::g_whole_detector != dunedaq::trgdataformats::TypeDefaults::s_invalid_detid); +// NOLINTEND(build/unsigned) + #endif // TRGDATAFORMATS_INCLUDE_TRGDATAFORMATS_TYPES_HPP_ diff --git a/pybindsrc/trigger_activity.cpp b/pybindsrc/trigger_activity.cpp index 5275844..8116cd0 100644 --- a/pybindsrc/trigger_activity.cpp +++ b/pybindsrc/trigger_activity.cpp @@ -14,6 +14,8 @@ namespace py = pybind11; +// NOLINTBEGIN(build/unsigned) + namespace dunedaq { namespace trgdataformats { namespace python { @@ -139,3 +141,5 @@ register_trigger_activity(py::module& m) } // namespace python } // namespace trgdataformats } // namespace dunedaq + +// NOLINTEND(build/unsigned) diff --git a/pybindsrc/trigger_candidate.cpp b/pybindsrc/trigger_candidate.cpp index 7841e25..2f82ea0 100644 --- a/pybindsrc/trigger_candidate.cpp +++ b/pybindsrc/trigger_candidate.cpp @@ -14,6 +14,8 @@ namespace py = pybind11; +// NOLINTBEGIN(build/unsigned) + namespace dunedaq { namespace trgdataformats { namespace python { @@ -169,3 +171,5 @@ register_trigger_candidate(py::module& m) } // namespace python } // namespace trgdataformats } // namespace dunedaq + +// NOLINTEND(build/unsigned) diff --git a/pybindsrc/trigger_primitive.cpp b/pybindsrc/trigger_primitive.cpp index b147560..089bfa4 100644 --- a/pybindsrc/trigger_primitive.cpp +++ b/pybindsrc/trigger_primitive.cpp @@ -11,6 +11,8 @@ #include #include +// NOLINTBEGIN(build/unsigned) + namespace py = pybind11; namespace dunedaq::trgdataformats::python { @@ -43,3 +45,5 @@ register_trigger_primitive(py::module& m) } } // namespace dunedaq::trgdataformats::python + +// NOLINTEND(build/unsigned) From 88fde61a9547a4d7e06438e1dc6b17420a1b2ef7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 20 Jul 2026 11:44:04 -0500 Subject: [PATCH 04/15] JCF: Issue #59: a few more refactors * Make the type of the enums in the structs explicit so we can explicitly reason about their size * Have string_to_trigger_candidate_type return a TriggerCandidateData::Type rather than an int * Add some static_assert checks to TriggerPrimitive to confirm the integrity of its layout * Add a TODO to Types.hpp basically pointing out that we should investigate making trgdataformats depend on daqdataformats --- .../trgdataformats/TriggerActivityData.hpp | 6 +++--- .../trgdataformats/TriggerCandidateData.hpp | 14 ++++++------- include/trgdataformats/TriggerPrimitive.hpp | 10 ++++++++++ include/trgdataformats/Types.hpp | 20 +++++++++++++++---- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/include/trgdataformats/TriggerActivityData.hpp b/include/trgdataformats/TriggerActivityData.hpp index a4edd1e..38a38a7 100644 --- a/include/trgdataformats/TriggerActivityData.hpp +++ b/include/trgdataformats/TriggerActivityData.hpp @@ -16,14 +16,14 @@ namespace dunedaq::trgdataformats { struct TriggerActivityData { - enum class Type + enum class Type : int { kUnknown = 0, kTPC = 1, kPDS = 2, }; - enum class Algorithm + enum class Algorithm : int { kUnknown = 0, kSupernova = 1, @@ -42,7 +42,7 @@ struct TriggerActivityData // Update this version number if there are any changes to the in-memory representation of this class! static constexpr version_t s_trigger_activity_version = 2; static constexpr channel_t s_invalid_channel = std::numeric_limits::max(); - + version_t version = s_trigger_activity_version; timestamp_t time_start = TypeDefaults::s_invalid_timestamp; timestamp_t time_end = TypeDefaults::s_invalid_timestamp; diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index 9dbbbb5..3d948f9 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -1,5 +1,5 @@ /** - * @file TriggerCandidate.hpp + * @file TriggerCandidateData.hpp * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have @@ -19,7 +19,7 @@ namespace dunedaq::trgdataformats { struct TriggerCandidateData { - enum class Type + enum class Type : int { kUnknown = 0, kTiming = 1, @@ -62,7 +62,7 @@ struct TriggerCandidateData kSSPLEDCalibration = 38, }; - enum class Algorithm + enum class Algorithm : int { kUnknown = 0, kSupernova = 1, @@ -145,14 +145,14 @@ get_trigger_candidate_type_names() return map; } -inline int +inline TriggerCandidateData::Type string_to_trigger_candidate_type(const std::string& name) { for (auto& it : get_trigger_candidate_type_names()) { if (it.second == name) - return static_cast(it.first); + return it.first; } - return static_cast(TriggerCandidateData::Type::kUnknown); + return TriggerCandidateData::Type::kUnknown; } inline std::string @@ -167,7 +167,7 @@ trigger_candidate_type_to_string(const TriggerCandidateData::Type& type) } // namespace dunedaq::trgdataformats // This static_assert is meant to alert the developer to bump the -// version if variables are added or removed +// version if variables are added or removed in TriggerCandidateData static_assert( dunedaq::trgdataformats::TriggerCandidateData::s_trigger_candidate_version == 3 && sizeof(dunedaq::trgdataformats::TriggerCandidateData) == 48 diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 86b6df2..66f4f81 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -31,6 +31,7 @@ struct TriggerPrimitive static constexpr uint16_t s_invalid_samples_over_threshold = std::numeric_limits::max(); static constexpr uint16_t s_invalid_samples_to_peak = std::numeric_limits::max(); static constexpr channel_t s_invalid_tp_channel = 0xFFFFFF; // TP channel limit is at 24 b + static constexpr std::size_t s_expected_bytes = 24; // Metadata. uint64_t version : 8; @@ -60,6 +61,15 @@ struct TriggerPrimitive {} }; +// Basic checks that the bits are arranged as we hope they're arranged +static_assert(sizeof(TriggerPrimitive) == TriggerPrimitive::s_expected_bytes); + +static_assert(std::endian::native == std::endian::little, + "The TriggerPrimitive bitfield layout assumes little-endian architecture"); + +static_assert(std::is_standard_layout_v); +static_asset(std::is_trivially_copyable_v); + } // namespace dunedaq::trgdataformats // NOLINTEND(build/unsigned) diff --git a/include/trgdataformats/Types.hpp b/include/trgdataformats/Types.hpp index ee7d1c3..e3422f7 100644 --- a/include/trgdataformats/Types.hpp +++ b/include/trgdataformats/Types.hpp @@ -16,9 +16,20 @@ namespace dunedaq::trgdataformats { -// A data timestamp in timing system clock ticks (50 MHz for -// ProtoDUNE-I, 62.5 MHz for ProtoDUNE-II and DUNE FD). If/when we -// depend on daqdataformats, we can get these from there +// TODO John Freeman (jcfree@fnal.gov), Jul-16-2026 + +// In the next month, determine if we can get enough buy in to add +// dependence on the relevant headers from daqdataformats, which will +// address Phil Rodrigues's concerns from five years ago. There's some +// hope for this to the extent Tom Junk emailed me in the spring +// saying this wouldn't be a problem. + + + +// timestamp_t represents a data timestamp in timing system clock +// ticks (50 MHz for ProtoDUNE-I, 62.5 MHz for ProtoDUNE-II and DUNE +// FD). If/when we depend on daqdataformats, we can get these from +// there using timestamp_t = uint64_t; using timestamp_diff_t = int64_t; @@ -28,6 +39,7 @@ using timestamp_diff_t = int64_t; // TODO P. Rodrigues 2021-06-01: it would be nice to have this be just // daqdataformats::GeoID, if/when we can depend on the daqdataformats // package + using detid_t = uint8_t; // TODO P. Rodrigues 2021-06-14: it would be nice to have this be just @@ -35,7 +47,7 @@ using detid_t = uint8_t; // package using trigger_number_t = uint64_t; -// A channel number + using channel_t = uint32_t; using channel_diff_t = int32_t; From 9fd87b61abc0f8d0c1ce58743e9fcd7b9019ffeb Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 20 Jul 2026 14:32:18 -0500 Subject: [PATCH 05/15] JCF: Issue #59: analogous to what's done with the overlay classes in fddetdataformats, add some functions which can be called directly in the implementation of the trigger type adaptor and would allow for templatization --- include/trgdataformats/TriggerPrimitive.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 66f4f81..6cf021c 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -59,6 +59,20 @@ struct TriggerPrimitive , adc_integral(0) , adc_peak(0) {} + + timestamp_t get_timestamp() const + { + return time_start; + } + + void set_timestamp(timestamp_t ts) + { + time_start = ts; + } + + bool operator<(const TriggerPrimitive& other) const { + return std::tie(time_start, channel) < std::tie(other.time_start, other.channel); + } }; // Basic checks that the bits are arranged as we hope they're arranged @@ -69,7 +83,7 @@ static_assert(std::endian::native == std::endian::little, static_assert(std::is_standard_layout_v); static_asset(std::is_trivially_copyable_v); - + } // namespace dunedaq::trgdataformats // NOLINTEND(build/unsigned) From 0a7392a79f5b213fa551382737ba833fa852b595 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 20 Jul 2026 15:23:39 -0500 Subject: [PATCH 06/15] JCF: Issue #59: in general, it's better to call static_assert than static_asset --- include/trgdataformats/TriggerPrimitive.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 6cf021c..53aa089 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -82,7 +82,7 @@ static_assert(std::endian::native == std::endian::little, "The TriggerPrimitive bitfield layout assumes little-endian architecture"); static_assert(std::is_standard_layout_v); -static_asset(std::is_trivially_copyable_v); +static_assert(std::is_trivially_copyable_v); } // namespace dunedaq::trgdataformats From 9d6114ea66f6830b76a62575a0d059bc65d6552b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Jul 2026 13:58:16 -0500 Subject: [PATCH 07/15] JCF: Issue #59: add a kind of catch-all set of unit tests called "TriggerDataStructures_test"; note that the lack of a default value for detid in the code causes a failure --- CMakeLists.txt | 1 + unittest/TriggerDataStructures_test.cxx | 152 ++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 unittest/TriggerDataStructures_test.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bb217d..af64d1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ daq_add_python_bindings(*.cpp ) daq_add_unit_test(Trgdataformats_test LINK_LIBRARIES) daq_add_unit_test(TriggerCandidateData_test LINK_LIBRARIES) +daq_add_unit_test(TriggerDataStructures_test LINK_LIBRARIES) ############################################################################## diff --git a/unittest/TriggerDataStructures_test.cxx b/unittest/TriggerDataStructures_test.cxx new file mode 100644 index 0000000..01477c5 --- /dev/null +++ b/unittest/TriggerDataStructures_test.cxx @@ -0,0 +1,152 @@ +/** + * @file TriggerDataStructures_test.cxx + * + * This is part of the DUNE DAQ Application Framework, copyright 2020. + * Licensing/copyright details are in the COPYING file that you should have + * received with this code. + */ + +#include "trgdataformats/TriggerActivityData.hpp" +#include "trgdataformats/TriggerCandidateData.hpp" +#include "trgdataformats/TriggerObjectOverlay.hpp" +#include "trgdataformats/TriggerPrimitive.hpp" +#include "trgdataformats/Types.hpp" + +#define BOOST_TEST_MODULE TriggerDataStructures_test // NOLINT + +#include "boost/test/unit_test.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace dunedaq::trgdataformats; + +BOOST_AUTO_TEST_SUITE(TriggerDataStructures_test) + +BOOST_AUTO_TEST_CASE(TriggerPrimitive_DefaultValues_And_Timestamp) +{ + TriggerPrimitive tp; + + BOOST_REQUIRE_EQUAL(tp.version, TriggerPrimitive::s_trigger_primitive_version); + BOOST_REQUIRE_EQUAL(tp.flag, 0); + BOOST_REQUIRE_EQUAL(tp.detid, TypeDefaults::s_invalid_detid); + BOOST_REQUIRE_EQUAL(tp.channel, TriggerPrimitive::s_invalid_tp_channel); + BOOST_REQUIRE_EQUAL(tp.samples_over_threshold, TriggerPrimitive::s_invalid_samples_over_threshold); + BOOST_REQUIRE_EQUAL(tp.time_start, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(tp.samples_to_peak, TriggerPrimitive::s_invalid_samples_to_peak); + BOOST_REQUIRE_EQUAL(tp.adc_integral, 0); + BOOST_REQUIRE_EQUAL(tp.adc_peak, 0); + + constexpr timestamp_t test_ts = 0x12345678ULL; + tp.set_timestamp(test_ts); + BOOST_REQUIRE_EQUAL(tp.get_timestamp(), test_ts); + BOOST_REQUIRE_EQUAL(tp.time_start, test_ts); +} + +BOOST_AUTO_TEST_CASE(TriggerPrimitive_Ordering) +{ + TriggerPrimitive a; + TriggerPrimitive b; + TriggerPrimitive c; + + a.time_start = 100; + a.channel = 10; + + b.time_start = 100; + b.channel = 8; + + c.time_start = 99; + c.channel = 20; + + std::vector tps{ a, b, c }; + std::sort(tps.begin(), tps.end()); + + BOOST_REQUIRE_EQUAL(tps[0].time_start, 99); + BOOST_REQUIRE_EQUAL(tps[0].channel, 20); + + BOOST_REQUIRE_EQUAL(tps[1].time_start, 100); + BOOST_REQUIRE_EQUAL(tps[1].channel, 8); + + BOOST_REQUIRE_EQUAL(tps[2].time_start, 100); + BOOST_REQUIRE_EQUAL(tps[2].channel, 10); +} + +BOOST_AUTO_TEST_CASE(TriggerActivityData_DefaultValues) +{ + TriggerActivityData ta; + + BOOST_REQUIRE_EQUAL(ta.version, TriggerActivityData::s_trigger_activity_version); + BOOST_REQUIRE_EQUAL(ta.time_start, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(ta.time_end, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(ta.time_peak, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(ta.time_activity, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(ta.channel_start, TriggerActivityData::s_invalid_channel); + BOOST_REQUIRE_EQUAL(ta.channel_end, TriggerActivityData::s_invalid_channel); + BOOST_REQUIRE_EQUAL(ta.channel_peak, TriggerActivityData::s_invalid_channel); + BOOST_REQUIRE_EQUAL(ta.adc_integral, 0); + BOOST_REQUIRE_EQUAL(ta.adc_peak, 0); + BOOST_REQUIRE_EQUAL(ta.detid, TypeDefaults::s_invalid_detid); + BOOST_REQUIRE_EQUAL(static_cast(ta.type), static_cast(TriggerActivityData::Type::kUnknown)); + BOOST_REQUIRE_EQUAL(static_cast(ta.algorithm), static_cast(TriggerActivityData::Algorithm::kUnknown)); +} + +BOOST_AUTO_TEST_CASE(TriggerCandidateData_DefaultValues) +{ + TriggerCandidateData tc{}; + + BOOST_REQUIRE_EQUAL(tc.version, TriggerCandidateData::s_trigger_candidate_version); + BOOST_REQUIRE_EQUAL(tc.time_start, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(tc.time_end, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(tc.time_candidate, TypeDefaults::s_invalid_timestamp); + BOOST_REQUIRE_EQUAL(static_cast(tc.type), static_cast(TriggerCandidateData::Type::kUnknown)); + BOOST_REQUIRE_EQUAL(static_cast(tc.algorithm), static_cast(TriggerCandidateData::Algorithm::kUnknown)); + + // This expectation is intentionally strict to catch missing default-initialization policy. + BOOST_REQUIRE_EQUAL(tc.detid, TypeDefaults::s_invalid_detid); +} + +BOOST_AUTO_TEST_CASE(TriggerCandidateData_TypeMapCompleteness) +{ + const auto type_map = get_trigger_candidate_type_names(); + BOOST_REQUIRE_EQUAL(type_map.size(), 39); + + std::set names; + for (const auto& kv : type_map) { + const auto inserted = names.insert(kv.second).second; + BOOST_REQUIRE(inserted); + BOOST_REQUIRE_EQUAL(trigger_candidate_type_to_string(kv.first), kv.second); + BOOST_REQUIRE_EQUAL(static_cast(string_to_trigger_candidate_type(kv.second)), static_cast(kv.first)); + } + + BOOST_REQUIRE_EQUAL(type_map.at(TriggerCandidateData::Type::kUnknown), "kUnknown"); + BOOST_REQUIRE_EQUAL(type_map.at(TriggerCandidateData::Type::kSSPLEDCalibration), "kSSPLEDCalibration"); +} + +BOOST_AUTO_TEST_CASE(Types_DefaultConstants) +{ + BOOST_REQUIRE_EQUAL(TypeDefaults::s_invalid_timestamp, std::numeric_limits::max()); + BOOST_REQUIRE_EQUAL(TypeDefaults::s_invalid_detid, std::numeric_limits::max()); + BOOST_REQUIRE_EQUAL(TypeDefaults::s_invalid_trigger_number, std::numeric_limits::max()); + BOOST_REQUIRE(g_whole_detector != TypeDefaults::s_invalid_detid); + BOOST_REQUIRE_EQUAL(g_whole_detector, + static_cast(std::numeric_limits::max() - static_cast(1))); +} + +BOOST_AUTO_TEST_CASE(TriggerObjectOverlay_AliasesAndLayout) +{ + BOOST_REQUIRE((std::is_same_v)); + BOOST_REQUIRE((std::is_same_v)); + BOOST_REQUIRE((std::is_same_v)); + BOOST_REQUIRE((std::is_same_v)); + + BOOST_REQUIRE_EQUAL(offsetof(TriggerActivity, n_inputs), sizeof(TriggerActivityData)); + BOOST_REQUIRE_EQUAL(offsetof(TriggerCandidate, n_inputs), sizeof(TriggerCandidateData)); +} + +BOOST_AUTO_TEST_SUITE_END() From c22d9b3905ca4f24309196c5086e0fc14758c529 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Jul 2026 14:06:23 -0500 Subject: [PATCH 08/15] JCF: Issue #59: fix the new failing unit test --- include/trgdataformats/TriggerCandidateData.hpp | 2 +- unittest/TriggerDataStructures_test.cxx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index 3d948f9..1502900 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -90,7 +90,7 @@ struct TriggerCandidateData // std::vector but that messes up the overlay scheme, so // I've changed it for now to be just a detid_t. Need to work out // what to do longer term - detid_t detid; + detid_t detid = TypeDefaults::s_invalid_detid; Type type = Type::kUnknown; Algorithm algorithm = Algorithm::kUnknown; }; diff --git a/unittest/TriggerDataStructures_test.cxx b/unittest/TriggerDataStructures_test.cxx index 01477c5..72d3786 100644 --- a/unittest/TriggerDataStructures_test.cxx +++ b/unittest/TriggerDataStructures_test.cxx @@ -107,7 +107,6 @@ BOOST_AUTO_TEST_CASE(TriggerCandidateData_DefaultValues) BOOST_REQUIRE_EQUAL(static_cast(tc.type), static_cast(TriggerCandidateData::Type::kUnknown)); BOOST_REQUIRE_EQUAL(static_cast(tc.algorithm), static_cast(TriggerCandidateData::Algorithm::kUnknown)); - // This expectation is intentionally strict to catch missing default-initialization policy. BOOST_REQUIRE_EQUAL(tc.detid, TypeDefaults::s_invalid_detid); } From 52194fbfaeb09eaf21cb4a5cd849fa18700af23e Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Jul 2026 13:24:18 -0500 Subject: [PATCH 09/15] JCF: Issue #59: update the Python bindings to reflect the timestamp + comparison functions added to TriggerPrimitive as well as support for what's in Types.hpp --- pybindsrc/module.cpp | 1 + pybindsrc/registrators.hpp | 1 + pybindsrc/trigger_primitive.cpp | 7 +++++++ pybindsrc/types.cpp | 28 ++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 pybindsrc/types.cpp diff --git a/pybindsrc/module.cpp b/pybindsrc/module.cpp index 8f6f021..947650f 100644 --- a/pybindsrc/module.cpp +++ b/pybindsrc/module.cpp @@ -20,6 +20,7 @@ PYBIND11_MODULE(_daq_trgdataformats_py, m) m.doc() = "C++ implementation of the trgdataformats modules"; + register_types(m); register_trigger_primitive(m); // register_trigger_bitwords(m); register_trigger_activity(m); diff --git a/pybindsrc/registrators.hpp b/pybindsrc/registrators.hpp index c2cfc41..d03a53a 100644 --- a/pybindsrc/registrators.hpp +++ b/pybindsrc/registrators.hpp @@ -16,6 +16,7 @@ namespace dunedaq::trgdataformats::python { + void register_types(pybind11::module &); void register_trigger_primitive(pybind11::module &); // void register_trigger_bitwords(pybind11::module &); void register_trigger_activity(pybind11::module &); diff --git a/pybindsrc/trigger_primitive.cpp b/pybindsrc/trigger_primitive.cpp index 089bfa4..57c2742 100644 --- a/pybindsrc/trigger_primitive.cpp +++ b/pybindsrc/trigger_primitive.cpp @@ -30,6 +30,10 @@ register_trigger_primitive(py::module& m) return tp; } )) .def_property_readonly_static("s_trigger_primitive_version", [](py::object /*self*/) {return TriggerPrimitive::s_trigger_primitive_version;}) + .def_property_readonly_static("s_invalid_samples_over_threshold", [](py::object /*self*/) {return TriggerPrimitive::s_invalid_samples_over_threshold;}) + .def_property_readonly_static("s_invalid_samples_to_peak", [](py::object /*self*/) {return TriggerPrimitive::s_invalid_samples_to_peak;}) + .def_property_readonly_static("s_invalid_tp_channel", [](py::object /*self*/) {return TriggerPrimitive::s_invalid_tp_channel;}) + .def_property_readonly_static("s_expected_bytes", [](py::object /*self*/) {return TriggerPrimitive::s_expected_bytes;}) .def_property_readonly("version", [](TriggerPrimitive& self) -> uint8_t {return self.version;}) .def_property_readonly("time_start", [](TriggerPrimitive& self) -> uint64_t {return self.time_start;}) .def_property_readonly("samples_to_peak", [](TriggerPrimitive& self) -> uint16_t {return self.samples_to_peak;}) @@ -39,6 +43,9 @@ register_trigger_primitive(py::module& m) .def_property_readonly("adc_peak", [](TriggerPrimitive& self) -> uint16_t {return self.adc_peak;}) .def_property_readonly("detid", [](TriggerPrimitive& self) -> uint8_t {return self.detid;}) .def_property_readonly("flag", [](TriggerPrimitive& self) -> uint8_t {return self.flag;}) + .def("get_timestamp", &TriggerPrimitive::get_timestamp) + .def("set_timestamp", &TriggerPrimitive::set_timestamp) + .def("__lt__", &TriggerPrimitive::operator<) .def_static("sizeof", [](){ return sizeof(TriggerPrimitive); }) ; diff --git a/pybindsrc/types.cpp b/pybindsrc/types.cpp new file mode 100644 index 0000000..0b71c98 --- /dev/null +++ b/pybindsrc/types.cpp @@ -0,0 +1,28 @@ +/** + * @file types.cpp Python bindings for common trgdataformats scalar aliases/constants + * + * This is part of the DUNE DAQ Software Suite, copyright 2020. + * Licensing/copyright details are in the COPYING file that you should have + * received with this code. + */ + +#include "trgdataformats/Types.hpp" + +#include + +namespace py = pybind11; + +namespace dunedaq::trgdataformats::python { + +void +register_types(py::module& m) +{ + py::class_(m, "TypeDefaults") + .def_property_readonly_static("s_invalid_timestamp", [](py::object /*self*/) { return TypeDefaults::s_invalid_timestamp; }) + .def_property_readonly_static("s_invalid_detid", [](py::object /*self*/) { return TypeDefaults::s_invalid_detid; }) + .def_property_readonly_static("s_invalid_trigger_number", [](py::object /*self*/) { return TypeDefaults::s_invalid_trigger_number; }); + + m.attr("g_whole_detector") = py::int_(g_whole_detector); +} + +} // namespace dunedaq::trgdataformats::python From 71942e98a065861dba3bc2144b2f0d2cf783ea5c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Jul 2026 15:40:39 -0500 Subject: [PATCH 10/15] JCF: Issue #59: add a couple of missing Python bindings, reorder their declarations to match the header, and add a static_assert meant to alert developers to update bindings when versions are changed --- pybindsrc/trigger_activity.cpp | 14 ++++++++------ pybindsrc/trigger_candidate.cpp | 5 +++++ pybindsrc/trigger_primitive.cpp | 12 ++++++++---- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/pybindsrc/trigger_activity.cpp b/pybindsrc/trigger_activity.cpp index 8116cd0..067c270 100644 --- a/pybindsrc/trigger_activity.cpp +++ b/pybindsrc/trigger_activity.cpp @@ -16,9 +16,7 @@ namespace py = pybind11; // NOLINTBEGIN(build/unsigned) -namespace dunedaq { -namespace trgdataformats { -namespace python { +namespace dunedaq::trgdataformats::python { /* Doesn't work @@ -56,6 +54,8 @@ register_trigger_activity(py::module& m) auto tp = *static_cast(info.ptr); return tp; })) + .def_property_readonly_static("s_trigger_activity_version", [](py::object /*self*/) { return TriggerActivityData::s_trigger_activity_version; }) + .def_property_readonly_static("s_invalid_channel", [](py::object /*self*/) { return TriggerActivityData::s_invalid_channel; }) .def_property_readonly("version", [](TriggerActivityData& self) -> uint16_t {return self.version;}) .def_property_readonly("time_start", [](TriggerActivityData& self) -> uint64_t {return self.time_start;}) .def_property_readonly("time_end", [](TriggerActivityData& self) -> uint64_t {return self.time_end;}) @@ -138,8 +138,10 @@ register_trigger_activity(py::module& m) ; } -} // namespace python -} // namespace trgdataformats -} // namespace dunedaq +} // namespace dunedaq::trgdataformats::python + +static_assert( + dunedaq::trgdataformats::TriggerActivityData::s_trigger_activity_version == 2, + "Version of TriggerActivityData appears to have changed; as a developer please update the Python bindings in this file before updating this static_assert"); // NOLINTEND(build/unsigned) diff --git a/pybindsrc/trigger_candidate.cpp b/pybindsrc/trigger_candidate.cpp index 2f82ea0..cabae41 100644 --- a/pybindsrc/trigger_candidate.cpp +++ b/pybindsrc/trigger_candidate.cpp @@ -58,6 +58,7 @@ register_trigger_candidate(py::module& m) auto tp = *static_cast(info.ptr); return tp; })) + .def_property_readonly_static("s_trigger_candidate_version", [](py::object /*self*/) { return TriggerCandidateData::s_trigger_candidate_version; }) .def_property_readonly("version", [](TriggerCandidateData& self) -> uint16_t {return self.version;}) .def_property_readonly("time_start", [](TriggerCandidateData& self) -> uint64_t {return self.time_start;}) .def_property_readonly("time_end", [](TriggerCandidateData& self) -> uint64_t {return self.time_end;}) @@ -172,4 +173,8 @@ register_trigger_candidate(py::module& m) } // namespace trgdataformats } // namespace dunedaq +static_assert( + dunedaq::trgdataformats::TriggerCandidateData::s_trigger_candidate_version == 3, + "Version of TriggerCandidateData appears to have changed; as a developer please update the Python bindings in this file before updating this static_assert"); + // NOLINTEND(build/unsigned) diff --git a/pybindsrc/trigger_primitive.cpp b/pybindsrc/trigger_primitive.cpp index 57c2742..26752b9 100644 --- a/pybindsrc/trigger_primitive.cpp +++ b/pybindsrc/trigger_primitive.cpp @@ -35,14 +35,14 @@ register_trigger_primitive(py::module& m) .def_property_readonly_static("s_invalid_tp_channel", [](py::object /*self*/) {return TriggerPrimitive::s_invalid_tp_channel;}) .def_property_readonly_static("s_expected_bytes", [](py::object /*self*/) {return TriggerPrimitive::s_expected_bytes;}) .def_property_readonly("version", [](TriggerPrimitive& self) -> uint8_t {return self.version;}) + .def_property_readonly("flag", [](TriggerPrimitive& self) -> uint8_t {return self.flag;}) + .def_property_readonly("detid", [](TriggerPrimitive& self) -> uint8_t {return self.detid;}) + .def_property_readonly("channel", [](TriggerPrimitive& self) -> uint32_t {return uint32_t(self.channel);}) + .def_property_readonly("samples_over_threshold", [](TriggerPrimitive& self) -> uint16_t {return self.samples_over_threshold;}) .def_property_readonly("time_start", [](TriggerPrimitive& self) -> uint64_t {return self.time_start;}) .def_property_readonly("samples_to_peak", [](TriggerPrimitive& self) -> uint16_t {return self.samples_to_peak;}) - .def_property_readonly("samples_over_threshold", [](TriggerPrimitive& self) -> uint16_t {return self.samples_over_threshold;}) - .def_property_readonly("channel", [](TriggerPrimitive& self) -> uint32_t {return uint32_t(self.channel);}) .def_property_readonly("adc_integral", [](TriggerPrimitive& self) -> uint32_t {return self.adc_integral;}) .def_property_readonly("adc_peak", [](TriggerPrimitive& self) -> uint16_t {return self.adc_peak;}) - .def_property_readonly("detid", [](TriggerPrimitive& self) -> uint8_t {return self.detid;}) - .def_property_readonly("flag", [](TriggerPrimitive& self) -> uint8_t {return self.flag;}) .def("get_timestamp", &TriggerPrimitive::get_timestamp) .def("set_timestamp", &TriggerPrimitive::set_timestamp) .def("__lt__", &TriggerPrimitive::operator<) @@ -53,4 +53,8 @@ register_trigger_primitive(py::module& m) } // namespace dunedaq::trgdataformats::python +static_assert( + dunedaq::trgdataformats::TriggerPrimitive::s_trigger_primitive_version == 2, + "Version of TriggerPrimitive appears to have changed; as a developer please update the Python bindings in this file before updating this static_assert"); + // NOLINTEND(build/unsigned) From 1f5915566813e255afbcb0474114005edcba3879 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 4 Aug 2026 16:48:29 -0500 Subject: [PATCH 11/15] JCF: Issue #59: switch the container which connects the TriggerCandidateData::Type to a name from an std::map into an std::array so we can perform compile-time checks on it --- CMakeLists.txt | 8 -- .../trgdataformats/TriggerActivityData.hpp | 1 - .../trgdataformats/TriggerCandidateData.hpp | 80 +++-------------- include/trgdataformats/TriggerPrimitive.hpp | 25 ++++-- .../detail/TriggerCandidateData.hxx | 86 +++++++++++++++++++ pybindsrc/trigger_activity.cpp | 6 +- unittest/TriggerCandidateData_test.cxx | 16 ++-- unittest/TriggerDataStructures_test.cxx | 17 ---- 8 files changed, 129 insertions(+), 110 deletions(-) create mode 100644 include/trgdataformats/detail/TriggerCandidateData.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index af64d1a..087c48a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,21 +8,13 @@ daq_setup_environment() find_package(Boost COMPONENTS unit_test_framework REQUIRED) -############################################################################## - daq_add_library( LINK_LIBRARIES ) -############################################################################## - daq_add_python_bindings(*.cpp ) -############################################################################## - daq_add_unit_test(Trgdataformats_test LINK_LIBRARIES) daq_add_unit_test(TriggerCandidateData_test LINK_LIBRARIES) daq_add_unit_test(TriggerDataStructures_test LINK_LIBRARIES) -############################################################################## - daq_install() diff --git a/include/trgdataformats/TriggerActivityData.hpp b/include/trgdataformats/TriggerActivityData.hpp index 38a38a7..97fc64b 100644 --- a/include/trgdataformats/TriggerActivityData.hpp +++ b/include/trgdataformats/TriggerActivityData.hpp @@ -39,7 +39,6 @@ struct TriggerActivityData kSWIFT = 11 }; - // Update this version number if there are any changes to the in-memory representation of this class! static constexpr version_t s_trigger_activity_version = 2; static constexpr channel_t s_invalid_channel = std::numeric_limits::max(); diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index 1502900..e3b5c90 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -11,6 +11,7 @@ #include "trgdataformats/Types.hpp" +#include #include #include #include @@ -19,6 +20,10 @@ namespace dunedaq::trgdataformats { struct TriggerCandidateData { + // If you add an enum to TriggerCandidateData::Type, declare it + // second-to-last (just above kNumEnums) and ensure its value is +1 + // greater than the enum above it + enum class Type : int { kUnknown = 0, @@ -60,6 +65,7 @@ struct TriggerCandidateData kDTSPulser = 36, kDTSCosmic = 37, kSSPLEDCalibration = 38, + kNumEnums }; enum class Algorithm : int @@ -79,7 +85,6 @@ struct TriggerCandidateData kChannelAdjacency = 12, }; - // Update this version number if there are any changes to the in-memory representation of this class! static constexpr version_t s_trigger_candidate_version = 3; version_t version = s_trigger_candidate_version; @@ -95,77 +100,16 @@ struct TriggerCandidateData Algorithm algorithm = Algorithm::kUnknown; }; -// This map needs to be updated for each new TC type, as this is used when configuring Trigger Bitwords, affecting -// trigger logic in trigger::MLT -inline std::map -get_trigger_candidate_type_names() -{ - static const std::map map { - { TriggerCandidateData::Type::kUnknown, "kUnknown" }, - { TriggerCandidateData::Type::kTiming, "kTiming" }, - { TriggerCandidateData::Type::kTPCLowE, "kTPCLowE" }, - { TriggerCandidateData::Type::kSupernova, "kSupernova" }, - { TriggerCandidateData::Type::kRandom, "kRandom" }, - { TriggerCandidateData::Type::kPrescale, "kPrescale" }, - { TriggerCandidateData::Type::kADCSimpleWindow, "kADCSimpleWindow" }, - { TriggerCandidateData::Type::kHorizontalMuon, "kHorizontalMuon" }, - { TriggerCandidateData::Type::kMichelElectron, "kMichelElectron" }, - { TriggerCandidateData::Type::kPlaneCoincidence, "kPlaneCoincidence" }, - { TriggerCandidateData::Type::kDBSCAN, "kDBSCAN" }, - { TriggerCandidateData::Type::kChannelDistance, "kChannelDistance" }, - { TriggerCandidateData::Type::kBundle, "kBundle" }, - { TriggerCandidateData::Type::kCTBFakeTrigger, "kCTBFakeTrigger" }, - { TriggerCandidateData::Type::kCTBBeam, "kCTBBeam" }, - { TriggerCandidateData::Type::kCTBBeamChkvHL, "kCTBBeamChkvHL" }, - { TriggerCandidateData::Type::kCTBCustomD, "kCTBCustomD" }, - { TriggerCandidateData::Type::kCTBCustomE, "kCTBCustomE" }, - { TriggerCandidateData::Type::kCTBCustomF, "kCTBCustomF" }, - { TriggerCandidateData::Type::kCTBCustomG, "kCTBCustomG" }, - { TriggerCandidateData::Type::kCTBBeamChkvHLx, "kCTBBeamChkvHLx" }, - { TriggerCandidateData::Type::kCTBBeamChkvHxL, "kCTBBeamChkvHxL" }, - { TriggerCandidateData::Type::kCTBBeamChkvHxLx, "kCTBBeamChkvHxLx" }, - { TriggerCandidateData::Type::kNeutronSourceCalib, "kNeutronSourceCalib" }, - { TriggerCandidateData::Type::kChannelAdjacency, "kChannelAdjacency" }, - { TriggerCandidateData::Type::kCIBFakeTrigger, "kCIBFakeTrigger" }, - { TriggerCandidateData::Type::kCIBLaserTriggerP1, "kCIBLaserTriggerP1" }, - { TriggerCandidateData::Type::kCIBLaserTriggerP2, "kCIBLaserTriggerP2" }, - { TriggerCandidateData::Type::kCIBLaserTriggerP3, "kCIBLaserTriggerP3" }, - { TriggerCandidateData::Type::kCTBOffSpillSnapshot, "kCTBOffSpillSnapshot" }, - { TriggerCandidateData::Type::kCTBOffSpillCosmicJura, "kCTBOffSpillCosmicJura" }, - { TriggerCandidateData::Type::kCTBOffSpillCRTCosmic, "kCTBOffSpillCRTCosmic" }, - { TriggerCandidateData::Type::kCTBBeamSpillStart, "kCTBBeamSpillStart" }, - { TriggerCandidateData::Type::kCTBBeamSpillSnapshot, "kCTBBeamSpillSnapshot" }, - { TriggerCandidateData::Type::kCTBCustomC, "kCTBCustomC" }, - { TriggerCandidateData::Type::kCTBCustomPulseTrain, "kCTBCustomPulseTrain" }, - { TriggerCandidateData::Type::kDTSPulser, "kDTSPulser" }, - { TriggerCandidateData::Type::kDTSCosmic, "kDTSCosmic" }, - { TriggerCandidateData::Type::kSSPLEDCalibration, "kSSPLEDCalibration" }, - }; - - return map; -} - -inline TriggerCandidateData::Type -string_to_trigger_candidate_type(const std::string& name) -{ - for (auto& it : get_trigger_candidate_type_names()) { - if (it.second == name) - return it.first; - } - return TriggerCandidateData::Type::kUnknown; -} - -inline std::string -trigger_candidate_type_to_string(const TriggerCandidateData::Type& type) -{ - const auto& map { get_trigger_candidate_type_names() }; - auto elem { map.find(type) }; + inline TriggerCandidateData::Type + string_to_trigger_candidate_type(const std::string& name); - return elem != map.end() ? elem->second : "kUnknown"; -} + inline std::string + trigger_candidate_type_to_string(const TriggerCandidateData::Type type); } // namespace dunedaq::trgdataformats +#include "detail/TriggerCandidateData.hxx" + // This static_assert is meant to alert the developer to bump the // version if variables are added or removed in TriggerCandidateData static_assert( diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 53aa089..2cc31d6 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -27,12 +27,13 @@ namespace dunedaq::trgdataformats { */ struct TriggerPrimitive { - static constexpr uint8_t s_trigger_primitive_version = 2; + static constexpr version_t s_trigger_primitive_version = 2; + static constexpr std::size_t s_expected_bytes = 24; + static constexpr uint16_t s_invalid_samples_over_threshold = std::numeric_limits::max(); static constexpr uint16_t s_invalid_samples_to_peak = std::numeric_limits::max(); - static constexpr channel_t s_invalid_tp_channel = 0xFFFFFF; // TP channel limit is at 24 b - static constexpr std::size_t s_expected_bytes = 24; - + static constexpr channel_t s_invalid_tp_channel = 0xFFFFFF; // TP channel limit is at 24 bytes + // Metadata. uint64_t version : 8; uint64_t flag : 8; @@ -60,6 +61,10 @@ struct TriggerPrimitive , adc_peak(0) {} + // Despite this being a struct, the getter and setter for timestamp + // brings the struct closer into compliance with the standard + // overlay + timestamp_t get_timestamp() const { return time_start; @@ -76,7 +81,17 @@ struct TriggerPrimitive }; // Basic checks that the bits are arranged as we hope they're arranged -static_assert(sizeof(TriggerPrimitive) == TriggerPrimitive::s_expected_bytes); + +// TODO John Freeman (jcfree@fnal.gov), Aug-1-2026 + +// In the next two months, take these static_asserts and write up a +// concept which encompasses them - something like +// SafeBitLayoutConcept - and figure out the right package to put it +// in, so that both this and the far detector-specific overlay classes +// can use them + +static_assert(sizeof(TriggerPrimitive) == TriggerPrimitive::s_expected_bytes, + "The actual size of the TriggerPrimitive isn't the expected size; the compiler is likely inserting padding"); static_assert(std::endian::native == std::endian::little, "The TriggerPrimitive bitfield layout assumes little-endian architecture"); diff --git a/include/trgdataformats/detail/TriggerCandidateData.hxx b/include/trgdataformats/detail/TriggerCandidateData.hxx new file mode 100644 index 0000000..6c6c74f --- /dev/null +++ b/include/trgdataformats/detail/TriggerCandidateData.hxx @@ -0,0 +1,86 @@ + +namespace dunedaq::trgdataformats { + + // tcinfo_t is a constexpr-declarable container of pairs, each pair + // connecting a TriggerCandidateData::Type with its name + using tcinfo_t = std::array, + static_cast(TriggerCandidateData::Type::kNumEnums)>; + + inline const tcinfo_t& + get_trigger_candidate_type_names() + { + // This container needs to be updated for each new TC type, as this is used + // when configuring Trigger Bitwords, affecting trigger logic in + // trigger::MLT + + static constexpr tcinfo_t names { { + { TriggerCandidateData::Type::kUnknown, "kUnknown" }, + { TriggerCandidateData::Type::kTiming, "kTiming" }, + { TriggerCandidateData::Type::kTPCLowE, "kTPCLowE" }, + { TriggerCandidateData::Type::kSupernova, "kSupernova" }, + { TriggerCandidateData::Type::kRandom, "kRandom" }, + { TriggerCandidateData::Type::kPrescale, "kPrescale" }, + { TriggerCandidateData::Type::kADCSimpleWindow, "kADCSimpleWindow" }, + { TriggerCandidateData::Type::kHorizontalMuon, "kHorizontalMuon" }, + { TriggerCandidateData::Type::kMichelElectron, "kMichelElectron" }, + { TriggerCandidateData::Type::kPlaneCoincidence, "kPlaneCoincidence" }, + { TriggerCandidateData::Type::kDBSCAN, "kDBSCAN" }, + { TriggerCandidateData::Type::kChannelDistance, "kChannelDistance" }, + { TriggerCandidateData::Type::kBundle, "kBundle" }, + { TriggerCandidateData::Type::kCTBFakeTrigger, "kCTBFakeTrigger" }, + { TriggerCandidateData::Type::kCTBBeam, "kCTBBeam" }, + { TriggerCandidateData::Type::kCTBBeamChkvHL, "kCTBBeamChkvHL" }, + { TriggerCandidateData::Type::kCTBCustomD, "kCTBCustomD" }, + { TriggerCandidateData::Type::kCTBCustomE, "kCTBCustomE" }, + { TriggerCandidateData::Type::kCTBCustomF, "kCTBCustomF" }, + { TriggerCandidateData::Type::kCTBCustomG, "kCTBCustomG" }, + { TriggerCandidateData::Type::kCTBBeamChkvHLx, "kCTBBeamChkvHLx" }, + { TriggerCandidateData::Type::kCTBBeamChkvHxL, "kCTBBeamChkvHxL" }, + { TriggerCandidateData::Type::kCTBBeamChkvHxLx, "kCTBBeamChkvHxLx" }, + { TriggerCandidateData::Type::kNeutronSourceCalib, "kNeutronSourceCalib" }, + { TriggerCandidateData::Type::kChannelAdjacency, "kChannelAdjacency" }, + { TriggerCandidateData::Type::kCIBFakeTrigger, "kCIBFakeTrigger" }, + { TriggerCandidateData::Type::kCIBLaserTriggerP1, "kCIBLaserTriggerP1" }, + { TriggerCandidateData::Type::kCIBLaserTriggerP2, "kCIBLaserTriggerP2" }, + { TriggerCandidateData::Type::kCIBLaserTriggerP3, "kCIBLaserTriggerP3" }, + { TriggerCandidateData::Type::kCTBOffSpillSnapshot, "kCTBOffSpillSnapshot" }, + { TriggerCandidateData::Type::kCTBOffSpillCosmicJura, "kCTBOffSpillCosmicJura" }, + { TriggerCandidateData::Type::kCTBOffSpillCRTCosmic, "kCTBOffSpillCRTCosmic" }, + { TriggerCandidateData::Type::kCTBBeamSpillStart, "kCTBBeamSpillStart" }, + { TriggerCandidateData::Type::kCTBBeamSpillSnapshot, "kCTBBeamSpillSnapshot" }, + { TriggerCandidateData::Type::kCTBCustomC, "kCTBCustomC" }, + { TriggerCandidateData::Type::kCTBCustomPulseTrain, "kCTBCustomPulseTrain" }, + { TriggerCandidateData::Type::kDTSPulser, "kDTSPulser" }, + { TriggerCandidateData::Type::kDTSCosmic, "kDTSCosmic" }, + { TriggerCandidateData::Type::kSSPLEDCalibration, "kSSPLEDCalibration" } + } + }; + + // Check to ensure that if a new TriggerCandidateData::Type is + // created, this function is updated accordingly + static_assert(names.size() == static_cast(TriggerCandidateData::Type::kNumEnums)); + + return names; + } + + inline TriggerCandidateData::Type + string_to_trigger_candidate_type(const std::string& name) { + for (const auto& [type, type_name] : get_trigger_candidate_type_names()) { + if (type_name == name) + return type; + } + return TriggerCandidateData::Type::kUnknown; + } + + inline std::string + trigger_candidate_type_to_string(const TriggerCandidateData::Type type) { + try { + const auto& [dummy, type_name] { get_trigger_candidate_type_names().at(static_cast(type)) }; + return std::string(type_name); + + } catch (std::exception &e) { + return "kUnknown"; + } + } + +} // namespace dunedaq::trgdataformats diff --git a/pybindsrc/trigger_activity.cpp b/pybindsrc/trigger_activity.cpp index 067c270..0ceab0f 100644 --- a/pybindsrc/trigger_activity.cpp +++ b/pybindsrc/trigger_activity.cpp @@ -61,9 +61,9 @@ register_trigger_activity(py::module& m) .def_property_readonly("time_end", [](TriggerActivityData& self) -> uint64_t {return self.time_end;}) .def_property_readonly("time_peak", [](TriggerActivityData& self) -> uint64_t {return self.time_peak;}) .def_property_readonly("time_activity", [](TriggerActivityData& self) -> uint64_t {return self.time_activity;}) - .def_property_readonly("channel_start", [](TriggerActivityData& self) -> int32_t {return self.channel_start;}) - .def_property_readonly("channel_end", [](TriggerActivityData& self) -> int32_t {return self.channel_end;}) - .def_property_readonly("channel_peak", [](TriggerActivityData& self) -> int32_t {return self.channel_peak;}) + .def_property_readonly("channel_start", [](TriggerActivityData& self) -> channel_t {return self.channel_start;}) + .def_property_readonly("channel_end", [](TriggerActivityData& self) -> channel_t {return self.channel_end;}) + .def_property_readonly("channel_peak", [](TriggerActivityData& self) -> channel_t {return self.channel_peak;}) .def_property_readonly("adc_integral", [](TriggerActivityData& self) -> uint32_t {return self.adc_integral;}) .def_property_readonly("adc_peak", [](TriggerActivityData& self) -> uint32_t {return self.adc_peak;}) .def_property_readonly("detid", [](TriggerActivityData& self) -> uint16_t {return self.detid;}) diff --git a/unittest/TriggerCandidateData_test.cxx b/unittest/TriggerCandidateData_test.cxx index a133246..9a3a71b 100644 --- a/unittest/TriggerCandidateData_test.cxx +++ b/unittest/TriggerCandidateData_test.cxx @@ -28,16 +28,16 @@ BOOST_AUTO_TEST_CASE(FragmentTypeConversion) static_cast(TriggerCandidateData::Type::kTiming)); BOOST_REQUIRE_EQUAL(trigger_candidate_type_to_string(TriggerCandidateData::Type::kTiming), "kTiming"); - auto type_map = get_trigger_candidate_type_names(); + auto type_container = get_trigger_candidate_type_names(); // sanity check - for (auto& type_pair : type_map) { + for (const auto& [type, type_name] : type_container) { BOOST_TEST_MESSAGE("TriggerCandidateData type " - << int(type_pair.first) << " " << type_pair.second - << " with conversions: " << int(string_to_trigger_candidate_type(type_pair.second)) << " " - << trigger_candidate_type_to_string(type_pair.first)); - BOOST_REQUIRE_EQUAL(static_cast(string_to_trigger_candidate_type(type_pair.second)), - static_cast(type_pair.first)); - BOOST_REQUIRE_EQUAL(trigger_candidate_type_to_string(type_pair.first), type_pair.second); + << int(type) << " " << type_name + << " with conversions: " << int(string_to_trigger_candidate_type(std::string(type_name))) << " " + << trigger_candidate_type_to_string(type)); + BOOST_REQUIRE_EQUAL(static_cast(string_to_trigger_candidate_type(std::string(type_name))), + static_cast(type)); + BOOST_REQUIRE_EQUAL(trigger_candidate_type_to_string(type), type_name); } BOOST_REQUIRE_EQUAL(static_cast(string_to_trigger_candidate_type("thisIsABadFragmentType")), diff --git a/unittest/TriggerDataStructures_test.cxx b/unittest/TriggerDataStructures_test.cxx index 72d3786..c16b02a 100644 --- a/unittest/TriggerDataStructures_test.cxx +++ b/unittest/TriggerDataStructures_test.cxx @@ -110,23 +110,6 @@ BOOST_AUTO_TEST_CASE(TriggerCandidateData_DefaultValues) BOOST_REQUIRE_EQUAL(tc.detid, TypeDefaults::s_invalid_detid); } -BOOST_AUTO_TEST_CASE(TriggerCandidateData_TypeMapCompleteness) -{ - const auto type_map = get_trigger_candidate_type_names(); - BOOST_REQUIRE_EQUAL(type_map.size(), 39); - - std::set names; - for (const auto& kv : type_map) { - const auto inserted = names.insert(kv.second).second; - BOOST_REQUIRE(inserted); - BOOST_REQUIRE_EQUAL(trigger_candidate_type_to_string(kv.first), kv.second); - BOOST_REQUIRE_EQUAL(static_cast(string_to_trigger_candidate_type(kv.second)), static_cast(kv.first)); - } - - BOOST_REQUIRE_EQUAL(type_map.at(TriggerCandidateData::Type::kUnknown), "kUnknown"); - BOOST_REQUIRE_EQUAL(type_map.at(TriggerCandidateData::Type::kSSPLEDCalibration), "kSSPLEDCalibration"); -} - BOOST_AUTO_TEST_CASE(Types_DefaultConstants) { BOOST_REQUIRE_EQUAL(TypeDefaults::s_invalid_timestamp, std::numeric_limits::max()); From d4f8540c7f810ff6a89b904c6ff185246e5ffdd4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 5 Aug 2026 14:42:14 -0500 Subject: [PATCH 12/15] JCF: Issue #59: fix the didn't-do-what-I-wanted-it-to-do static_assertion and give the sentinel enum an even more obvious name so developers won't add enums after it --- include/trgdataformats/TriggerCandidateData.hpp | 4 ++-- include/trgdataformats/detail/TriggerCandidateData.hxx | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index e3b5c90..b997c81 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -21,7 +21,7 @@ namespace dunedaq::trgdataformats { struct TriggerCandidateData { // If you add an enum to TriggerCandidateData::Type, declare it - // second-to-last (just above kNumEnums) and ensure its value is +1 + // second-to-last (just above kFinalEnum) and ensure its value is +1 // greater than the enum above it enum class Type : int @@ -65,7 +65,7 @@ struct TriggerCandidateData kDTSPulser = 36, kDTSCosmic = 37, kSSPLEDCalibration = 38, - kNumEnums + kFinalEnum }; enum class Algorithm : int diff --git a/include/trgdataformats/detail/TriggerCandidateData.hxx b/include/trgdataformats/detail/TriggerCandidateData.hxx index 6c6c74f..fdfa581 100644 --- a/include/trgdataformats/detail/TriggerCandidateData.hxx +++ b/include/trgdataformats/detail/TriggerCandidateData.hxx @@ -4,7 +4,7 @@ namespace dunedaq::trgdataformats { // tcinfo_t is a constexpr-declarable container of pairs, each pair // connecting a TriggerCandidateData::Type with its name using tcinfo_t = std::array, - static_cast(TriggerCandidateData::Type::kNumEnums)>; + static_cast(TriggerCandidateData::Type::kFinalEnum)>; inline const tcinfo_t& get_trigger_candidate_type_names() @@ -56,9 +56,13 @@ namespace dunedaq::trgdataformats { } }; + // Check to ensure that if a new TriggerCandidateData::Type is // created, this function is updated accordingly - static_assert(names.size() == static_cast(TriggerCandidateData::Type::kNumEnums)); + + // The string in a default-initialized {type, string} pair will + // boolean-equal "" + static_assert(names[names.size() - 1].second != ""); return names; } From 20da66149f88a36c45ca0bdd7e4729af7bc601bb Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 6 Aug 2026 11:19:14 -0500 Subject: [PATCH 13/15] JCF: Issue #59: add Python scripts which test the Python bindings, called by the top-level trgdataformats_binding_tests.py script --- scripts/TriggerActivity_binding_test.py | 165 +++++++++++++++++++++++ scripts/TriggerCandidate_binding_test.py | 139 +++++++++++++++++++ scripts/TriggerPrimitive_binding_test.py | 152 +++++++++++++++++++++ scripts/Types_binding_test.py | 58 ++++++++ scripts/trgdataformats_binding_tests.py | 33 +++++ 5 files changed, 547 insertions(+) create mode 100755 scripts/TriggerActivity_binding_test.py create mode 100755 scripts/TriggerCandidate_binding_test.py create mode 100755 scripts/TriggerPrimitive_binding_test.py create mode 100755 scripts/Types_binding_test.py create mode 100755 scripts/trgdataformats_binding_tests.py diff --git a/scripts/TriggerActivity_binding_test.py b/scripts/TriggerActivity_binding_test.py new file mode 100755 index 0000000..517ced4 --- /dev/null +++ b/scripts/TriggerActivity_binding_test.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +from trgdataformats import TriggerActivity, TriggerActivityData + + +def test_trigger_activity_data_defaults() -> int: + ta = TriggerActivityData() + + if ta is None: + print("FAIL: TriggerActivityData default construction returned None") + return 1 + + size = TriggerActivityData.sizeof() + if size != 80: + print(f"FAIL: TriggerActivityData.sizeof() returned {size}, expected 80") + return 1 + + if TriggerActivityData.s_trigger_activity_version != 2: + print( + "FAIL: TriggerActivityData.s_trigger_activity_version expected 2, " + f"got {TriggerActivityData.s_trigger_activity_version}" + ) + return 1 + + if TriggerActivityData.s_invalid_channel != (2**32 - 1): + print( + "FAIL: TriggerActivityData.s_invalid_channel expected 2**32-1, " + f"got {TriggerActivityData.s_invalid_channel}" + ) + return 1 + + if ta.version != 2: + print(f"FAIL: TriggerActivityData.version expected 2, got {ta.version}") + return 1 + + if ta.version != TriggerActivityData.s_trigger_activity_version: + print( + "FAIL: TriggerActivityData.version should match s_trigger_activity_version, " + f"got {ta.version} vs {TriggerActivityData.s_trigger_activity_version}" + ) + return 1 + + if ta.time_start != (2**64 - 1): + print(f"FAIL: TriggerActivityData.time_start expected 2**64-1, got {ta.time_start}") + return 1 + + if ta.time_end != (2**64 - 1): + print(f"FAIL: TriggerActivityData.time_end expected 2**64-1, got {ta.time_end}") + return 1 + + if ta.time_peak != (2**64 - 1): + print(f"FAIL: TriggerActivityData.time_peak expected 2**64-1, got {ta.time_peak}") + return 1 + + if ta.time_activity != (2**64 - 1): + print( + "FAIL: TriggerActivityData.time_activity expected 2**64-1, " + f"got {ta.time_activity}" + ) + return 1 + + if ta.channel_start != TriggerActivityData.s_invalid_channel: + print( + "FAIL: TriggerActivityData.channel_start expected invalid channel, " + f"got {ta.channel_start}" + ) + return 1 + + if ta.channel_end != TriggerActivityData.s_invalid_channel: + print( + "FAIL: TriggerActivityData.channel_end expected invalid channel, " + f"got {ta.channel_end}" + ) + return 1 + + if ta.channel_peak != TriggerActivityData.s_invalid_channel: + print( + "FAIL: TriggerActivityData.channel_peak expected invalid channel, " + f"got {ta.channel_peak}" + ) + return 1 + + if ta.adc_integral != 0: + print(f"FAIL: TriggerActivityData.adc_integral expected 0, got {ta.adc_integral}") + return 1 + + if ta.adc_peak != 0: + print(f"FAIL: TriggerActivityData.adc_peak expected 0, got {ta.adc_peak}") + return 1 + + if ta.detid != 2**8 - 1: + print(f"FAIL: TriggerActivityData.detid expected 2**8 - 1, got {ta.detid}") + return 1 + + if ta.type != TriggerActivityData.Type.kUnknown: + print(f"FAIL: TriggerActivityData.type expected kUnknown, got {ta.type}") + return 1 + + if ta.algorithm != TriggerActivityData.Algorithm.kUnknown: + print(f"FAIL: TriggerActivityData.algorithm expected kUnknown, got {ta.algorithm}") + return 1 + + print("PASS: TriggerActivityData default checks") + return 0 + + +def test_trigger_activity_data_enums() -> int: + # Confirm expected enum names are available through bindings. + enum_checks = [ + TriggerActivityData.Type.kUnknown, + TriggerActivityData.Type.kTPC, + TriggerActivityData.Type.kPDS, + TriggerActivityData.Algorithm.kUnknown, + TriggerActivityData.Algorithm.kSupernova, + TriggerActivityData.Algorithm.kSWIFT, + ] + + if any(value is None for value in enum_checks): + print("FAIL: TriggerActivityData enum exposure appears incomplete") + return 1 + + print("PASS: TriggerActivityData enum exposure") + return 0 + + +def test_trigger_activity_holder_from_bytes() -> int: + payload = bytes(TriggerActivityData.sizeof() + 8) + ta = TriggerActivity(payload) + + if ta.sizeof() != len(payload): + print(f"FAIL: TriggerActivity sizeof mismatch, got {ta.sizeof()}, expected {len(payload)}") + return 1 + + if len(ta) != 0: + print(f"FAIL: TriggerActivity expected length 0 for zero payload, got {len(ta)}") + return 1 + + if ta.n_inputs() != 0: + print(f"FAIL: TriggerActivity n_inputs expected 0 for zero payload, got {ta.n_inputs()}") + return 1 + + if len(ta.get_bytes()) != len(payload): + print("FAIL: TriggerActivity get_bytes length mismatch") + return 1 + + print("PASS: TriggerActivity holder bytes roundtrip with zero-input payload") + return 0 + + +def main() -> int: + failures = 0 + failures += test_trigger_activity_data_defaults() + failures += test_trigger_activity_data_enums() + failures += test_trigger_activity_holder_from_bytes() + + if failures == 0: + print("TriggerActivity binding tests passed") + else: + print(f"TriggerActivity binding tests had {failures} failure(s)") + + return failures + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/TriggerCandidate_binding_test.py b/scripts/TriggerCandidate_binding_test.py new file mode 100755 index 0000000..ebf2b62 --- /dev/null +++ b/scripts/TriggerCandidate_binding_test.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +from trgdataformats import ( + TriggerCandidate, + TriggerCandidateData, + string_to_trigger_candidate_type, + trigger_candidate_type_to_string, +) + + +def test_trigger_candidate_data_defaults() -> int: + tc = TriggerCandidateData() + + if tc is None: + print("FAIL: TriggerCandidateData default construction returned None") + return 1 + + size = TriggerCandidateData.sizeof() + if size != 48: + print(f"FAIL: TriggerCandidateData.sizeof() returned {size}, expected 48") + return 1 + + if TriggerCandidateData.s_trigger_candidate_version != 3: + print( + "FAIL: TriggerCandidateData.s_trigger_candidate_version expected 3, " + f"got {TriggerCandidateData.s_trigger_candidate_version}" + ) + return 1 + + if tc.version != 3: + print(f"FAIL: TriggerCandidateData.version expected 3, got {tc.version}") + return 1 + + if tc.version != TriggerCandidateData.s_trigger_candidate_version: + print( + "FAIL: TriggerCandidateData.version should match s_trigger_candidate_version, " + f"got {tc.version} vs {TriggerCandidateData.s_trigger_candidate_version}" + ) + return 1 + + if tc.time_start != (2**64 - 1): + print(f"FAIL: TriggerCandidateData.time_start expected 2**64-1, got {tc.time_start}") + return 1 + + if tc.time_end != (2**64 - 1): + print(f"FAIL: TriggerCandidateData.time_end expected 2**64-1, got {tc.time_end}") + return 1 + + if tc.time_candidate != (2**64 - 1): + print( + "FAIL: TriggerCandidateData.time_candidate expected 2**64-1, " + f"got {tc.time_candidate}" + ) + return 1 + + if tc.detid != 2**8 - 1: + print(f"FAIL: TriggerCandidateData.detid expected 2**8 - 1, got {tc.detid}") + return 1 + + if tc.type != TriggerCandidateData.Type.kUnknown: + print(f"FAIL: TriggerCandidateData.type expected kUnknown, got {tc.type}") + return 1 + + if tc.algorithm != TriggerCandidateData.Algorithm.kUnknown: + print(f"FAIL: TriggerCandidateData.algorithm expected kUnknown, got {tc.algorithm}") + return 1 + + print("PASS: TriggerCandidateData default checks") + return 0 + + +def test_trigger_candidate_enum_and_conversion() -> int: + if trigger_candidate_type_to_string(TriggerCandidateData.Type.kTiming) != "kTiming": + print("FAIL: trigger_candidate_type_to_string(kTiming) mismatch") + return 1 + + if string_to_trigger_candidate_type("kTiming") != TriggerCandidateData.Type.kTiming: + print("FAIL: string_to_trigger_candidate_type('kTiming') mismatch") + return 1 + + if string_to_trigger_candidate_type("notAType") != TriggerCandidateData.Type.kUnknown: + print("FAIL: string_to_trigger_candidate_type('notAType') should return kUnknown") + return 1 + + # Spot-check long-tail enum entries added recently. + for expected in [ + TriggerCandidateData.Type.kCTBOffSpillSnapshot, + TriggerCandidateData.Type.kCTBCustomPulseTrain, + TriggerCandidateData.Type.kSSPLEDCalibration, + ]: + as_str = trigger_candidate_type_to_string(expected) + if string_to_trigger_candidate_type(as_str) != expected: + print(f"FAIL: conversion roundtrip mismatch for enum value '{as_str}'") + return 1 + + print("PASS: TriggerCandidateData enum conversion checks") + return 0 + + +def test_trigger_candidate_holder_from_bytes() -> int: + payload = bytes(TriggerCandidateData.sizeof() + 8) # 8 for the uint64_t n_inputs in TriggerCandidate + tc = TriggerCandidate(payload) + + if tc.sizeof() != len(payload): + print(f"FAIL: TriggerCandidate sizeof mismatch, got {tc.sizeof()}, expected {len(payload)}") + return 1 + + if len(tc) != 0: + print(f"FAIL: TriggerCandidate expected length 0 for zero payload, got {len(tc)}") + return 1 + + if tc.n_inputs() != 0: + print(f"FAIL: TriggerCandidate n_inputs expected 0 for zero payload, got {tc.n_inputs()}") + return 1 + + if len(tc.get_bytes()) != len(payload): + print("FAIL: TriggerCandidate get_bytes length mismatch") + return 1 + + print("PASS: TriggerCandidate holder bytes roundtrip with zero-input payload") + return 0 + + +def main() -> int: + failures = 0 + failures += test_trigger_candidate_data_defaults() + failures += test_trigger_candidate_enum_and_conversion() + failures += test_trigger_candidate_holder_from_bytes() + + if failures == 0: + print("TriggerCandidate binding tests passed") + else: + print(f"TriggerCandidate binding tests had {failures} failure(s)") + + return failures + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/TriggerPrimitive_binding_test.py b/scripts/TriggerPrimitive_binding_test.py new file mode 100755 index 0000000..f6eabfd --- /dev/null +++ b/scripts/TriggerPrimitive_binding_test.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 + +from trgdataformats import TriggerPrimitive + + +def test_construction_and_sizeof() -> int: + tp = TriggerPrimitive() + if tp is None: + print("FAIL: TriggerPrimitive default construction returned None") + return 1 + + size = TriggerPrimitive.sizeof() + if size != TriggerPrimitive.s_expected_bytes: + print( + "FAIL: TriggerPrimitive.sizeof() and TriggerPrimitive.s_expected_bytes mismatch, " + f"got {size} vs {TriggerPrimitive.s_expected_bytes}" + ) + return 1 + + print("PASS: TriggerPrimitive construction and sizeof") + return 0 + + +def test_static_and_default_fields() -> int: + tp = TriggerPrimitive() + + # Providing this check will provide a nudge for developers to update these tests when the version changes + if TriggerPrimitive.s_trigger_primitive_version != 2: + print( + "FAIL: TriggerPrimitive.s_trigger_primitive_version expected 2, " + f"got {TriggerPrimitive.s_trigger_primitive_version}" + ) + return 1 + + if tp.version != TriggerPrimitive.s_trigger_primitive_version: + print(f"FAIL: version mismatch, got {tp.version}") + return 1 + + if tp.flag != 0: + print(f"FAIL: flag expected 0, got {tp.flag}") + return 1 + + if tp.detid != 2**8 - 1: + print(f"FAIL: detid expected 2**8 - 1, got {tp.detid}") + return 1 + + if tp.channel != 0xFFFFFF: + print(f"FAIL: channel expected 0xFFFFFF, got {tp.channel}") + return 1 + + if tp.samples_over_threshold != 2**16 - 1: + print( + "FAIL: samples_over_threshold expected 2**16 - 1, " + f"got {tp.samples_over_threshold}" + ) + return 1 + + if tp.samples_to_peak != 2**16 - 1: + print(f"FAIL: samples_to_peak expected 2**16 - 1, got {tp.samples_to_peak}") + return 1 + + if tp.time_start != (2**64 - 1): + print(f"FAIL: time_start expected 2**64-1, got {tp.time_start}") + return 1 + + if tp.adc_integral != 0: + print(f"FAIL: adc_integral expected 0, got {tp.adc_integral}") + return 1 + + if tp.adc_peak != 0: + print(f"FAIL: adc_peak expected 0, got {tp.adc_peak}") + return 1 + + expected_channel_invalid = 0xFFFFFF + if TriggerPrimitive.s_invalid_samples_over_threshold != 2**16 - 1: + print( + "FAIL: TriggerPrimitive.s_invalid_samples_over_threshold expected 2**16 - 1, " + f"got {TriggerPrimitive.s_invalid_samples_over_threshold}" + ) + return 1 + + if TriggerPrimitive.s_invalid_samples_to_peak != 2**16 - 1: + print( + "FAIL: TriggerPrimitive.s_invalid_samples_to_peak expected 2**16 - 1, " + f"got {TriggerPrimitive.s_invalid_samples_to_peak}" + ) + return 1 + + if TriggerPrimitive.s_invalid_tp_channel != expected_channel_invalid: + print( + "FAIL: TriggerPrimitive.s_invalid_tp_channel expected 0xFFFFFF, " + f"got {TriggerPrimitive.s_invalid_tp_channel}" + ) + return 1 + + if TriggerPrimitive.s_expected_bytes != 24: + print( + "FAIL: TriggerPrimitive.s_expected_bytes expected 24, " + f"got {TriggerPrimitive.s_expected_bytes}" + ) + return 1 + + print("PASS: TriggerPrimitive static/default field checks") + return 0 + + +def test_timestamp_methods_and_ordering() -> int: + tp_a = TriggerPrimitive() + tp_b = TriggerPrimitive() + + if tp_a.get_timestamp() != tp_a.time_start: + print( + "FAIL: TriggerPrimitive get_timestamp() is inconsistent with time_start, " + f"got {tp_a.get_timestamp()} vs {tp_a.time_start}" + ) + return 1 + + tp_a.set_timestamp(100) + if tp_a.get_timestamp() != 100: + print(f"FAIL: TriggerPrimitive get/set timestamp mismatch, got {tp_a.get_timestamp()}") + return 1 + + tp_b.set_timestamp(200) + + if not (tp_a < tp_b): + print("FAIL: TriggerPrimitive ordering expected tp_a < tp_b") + return 1 + + if tp_b < tp_a: + print("FAIL: TriggerPrimitive ordering expected tp_b !< tp_a") + return 1 + + print("PASS: TriggerPrimitive timestamp methods and ordering operator") + return 0 + + +def main() -> int: + failures = 0 + failures += test_construction_and_sizeof() + failures += test_static_and_default_fields() + failures += test_timestamp_methods_and_ordering() + + if failures == 0: + print("TriggerPrimitive binding tests passed") + else: + print(f"TriggerPrimitive binding tests had {failures} failure(s)") + + return failures + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/Types_binding_test.py b/scripts/Types_binding_test.py new file mode 100755 index 0000000..fd16c3f --- /dev/null +++ b/scripts/Types_binding_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +from trgdataformats import TypeDefaults, g_whole_detector + + +def test_type_defaults_constants() -> int: + if TypeDefaults.s_invalid_timestamp != (2**64 - 1): + print( + "FAIL: TypeDefaults.s_invalid_timestamp expected 2**64-1, " + f"got {TypeDefaults.s_invalid_timestamp}" + ) + return 1 + + if TypeDefaults.s_invalid_detid != (2**8 - 1): + print( + "FAIL: TypeDefaults.s_invalid_detid expected 2**8 - 1, " + f"got {TypeDefaults.s_invalid_detid}" + ) + return 1 + + if TypeDefaults.s_invalid_trigger_number != (2**64 - 1): + print( + "FAIL: TypeDefaults.s_invalid_trigger_number expected 2**64-1, " + f"got {TypeDefaults.s_invalid_trigger_number}" + ) + return 1 + + print("PASS: TypeDefaults constants") + return 0 + + +def test_whole_detector_constant() -> int: + if g_whole_detector != (TypeDefaults.s_invalid_detid - 1): + print( + "FAIL: g_whole_detector expected invalid_detid-1, " + f"got {g_whole_detector}" + ) + return 1 + + print("PASS: g_whole_detector constant") + return 0 + + +def main() -> int: + failures = 0 + failures += test_type_defaults_constants() + failures += test_whole_detector_constant() + + if failures == 0: + print("Types binding tests passed") + else: + print(f"Types binding tests had {failures} failure(s)") + + return failures + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/trgdataformats_binding_tests.py b/scripts/trgdataformats_binding_tests.py new file mode 100755 index 0000000..ff3aec1 --- /dev/null +++ b/scripts/trgdataformats_binding_tests.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + +import subprocess +import sys +from pathlib import Path + +def main() -> int: + script_dir = Path(__file__).resolve().parent + + scripts = [ + "Types_binding_test.py", + "TriggerPrimitive_binding_test.py", + "TriggerActivity_binding_test.py", + "TriggerCandidate_binding_test.py", + ] + + failures = 0 + for script in scripts: + result = subprocess.run([sys.executable, str(script_dir / script)], check=False) + if result.returncode != 0: + failures += 1 + + print("\n=== Summary ===") + if failures == 0: + print("All trgdataformats binding tests passed") + else: + print(f"{failures} binding test script(s) failed") + + return failures + + +if __name__ == "__main__": + sys.exit(main()) From 231f6796cad981c9637514e7ba1dbaff82490736 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 6 Aug 2026 14:28:09 -0500 Subject: [PATCH 14/15] JCF: Issue #59: add a set_inputs function to the overlay struct --- .../trgdataformats/TriggerObjectOverlay.hpp | 9 +++++++ unittest/TriggerDataStructures_test.cxx | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/trgdataformats/TriggerObjectOverlay.hpp b/include/trgdataformats/TriggerObjectOverlay.hpp index 567d75a..fc6cec7 100644 --- a/include/trgdataformats/TriggerObjectOverlay.hpp +++ b/include/trgdataformats/TriggerObjectOverlay.hpp @@ -13,6 +13,9 @@ #include "trgdataformats/TriggerCandidateData.hpp" #include "trgdataformats/TriggerPrimitive.hpp" +#include +#include + namespace dunedaq::trgdataformats { template @@ -26,6 +29,12 @@ struct TriggerObjectOverlay #pragma GCC diagnostic ignored "-Wpedantic" input_t inputs[]; // Non-standard flexible array member, but alternatives are worse #pragma GCC diagnostic pop + + void set_inputs(std::span in) + { + n_inputs = static_cast(in.size()); + std::copy(in.begin(), in.end(), inputs); + } }; using TriggerActivity = TriggerObjectOverlay; diff --git a/unittest/TriggerDataStructures_test.cxx b/unittest/TriggerDataStructures_test.cxx index c16b02a..2b81587 100644 --- a/unittest/TriggerDataStructures_test.cxx +++ b/unittest/TriggerDataStructures_test.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -131,4 +132,28 @@ BOOST_AUTO_TEST_CASE(TriggerObjectOverlay_AliasesAndLayout) BOOST_REQUIRE_EQUAL(offsetof(TriggerCandidate, n_inputs), sizeof(TriggerCandidateData)); } +BOOST_AUTO_TEST_CASE(TriggerObjectOverlay_SetInputs) +{ + std::vector tps(3); + tps[0].time_start = 100; + tps[0].channel = 10; + tps[1].time_start = 101; + tps[1].channel = 11; + tps[2].time_start = 102; + tps[2].channel = 12; + + // Recall that the "inputs" in the struct are a flexible array member which needs space allocated for it + const std::size_t nbytes = sizeof(TriggerActivity) + tps.size() * sizeof(TriggerPrimitive); + void* storage_for_ta_overlay = ::operator new(nbytes); + auto* ta = new (storage_for_ta_overlay) TriggerActivity{}; + + ta->set_inputs(tps); + BOOST_REQUIRE_EQUAL(ta->n_inputs, tps.size()); + + for (size_t i = 0; i < tps.size(); ++i) { + BOOST_REQUIRE_EQUAL(tps[i].time_start, ta->inputs[i].time_start); + BOOST_REQUIRE_EQUAL(tps[i].channel, ta->inputs[i].channel); + } +} + BOOST_AUTO_TEST_SUITE_END() From 2254600c3b75543efeb69a8a91b823b8e6378aa3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 6 Aug 2026 15:15:45 -0500 Subject: [PATCH 15/15] JCF: Issue #59: add brief overview comments at the beginning of the headers --- include/trgdataformats/TriggerActivityData.hpp | 6 ++++++ include/trgdataformats/TriggerCandidateData.hpp | 7 +++++++ include/trgdataformats/TriggerObjectOverlay.hpp | 10 ++++++++++ include/trgdataformats/TriggerPrimitive.hpp | 3 +++ include/trgdataformats/Types.hpp | 5 +++++ 5 files changed, 31 insertions(+) diff --git a/include/trgdataformats/TriggerActivityData.hpp b/include/trgdataformats/TriggerActivityData.hpp index 97fc64b..bc419fa 100644 --- a/include/trgdataformats/TriggerActivityData.hpp +++ b/include/trgdataformats/TriggerActivityData.hpp @@ -1,6 +1,12 @@ /** * @file TriggerActivityData.hpp * + * This header defines the TriggerActivityData struct, which + * aggregates information about a found set of associate trigger + * primitives (algorithm used, channels and times involved, etc.). It + * does *not* include per-trigger-primitive information, which need to + * be associated with TriggerActivityData in a higher level object. + * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have * received with this code. diff --git a/include/trgdataformats/TriggerCandidateData.hpp b/include/trgdataformats/TriggerCandidateData.hpp index b997c81..b09c831 100644 --- a/include/trgdataformats/TriggerCandidateData.hpp +++ b/include/trgdataformats/TriggerCandidateData.hpp @@ -1,6 +1,13 @@ /** * @file TriggerCandidateData.hpp * + * This header defines the TriggerCandidateData struct, which + * aggregates information about a found set of associated trigger + * activities (general type of candidate, algorithm used, times + * involved, etc.). It does *not* include per-trigger-activity + * information, which need to be associated with TriggerCandidateData + * in a higher level object. + * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have * received with this code. diff --git a/include/trgdataformats/TriggerObjectOverlay.hpp b/include/trgdataformats/TriggerObjectOverlay.hpp index fc6cec7..88b2789 100644 --- a/include/trgdataformats/TriggerObjectOverlay.hpp +++ b/include/trgdataformats/TriggerObjectOverlay.hpp @@ -1,6 +1,16 @@ /** * @file TriggerObjectOverlay.hpp * + * This header defines the "TriggerActivity" and "TriggerCandidate" + * structs, which are instantiations of the TriggerObjectOverlay + * template. Continguous in memory, a TriggerActivity instance is an + * instance of TriggerActivityData followed by n_inputs + * TriggerPrimitive instances. Likewise, a TriggerCandidate instance + * is an instance of TriggerCandidateData followed by n_inputs + * TriggerActivityData (not to be confused with TriggerActivity) + * instances. + * + * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have * received with this code. diff --git a/include/trgdataformats/TriggerPrimitive.hpp b/include/trgdataformats/TriggerPrimitive.hpp index 2cc31d6..1574b8c 100644 --- a/include/trgdataformats/TriggerPrimitive.hpp +++ b/include/trgdataformats/TriggerPrimitive.hpp @@ -1,6 +1,9 @@ /** * @file TriggerPrimitive.hpp * + * This header defines an overlay struct for trigger primitives, a + * single energy deposition on a TPC or PDS channel. + * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have * received with this code. diff --git a/include/trgdataformats/Types.hpp b/include/trgdataformats/Types.hpp index e3422f7..c81d46d 100644 --- a/include/trgdataformats/Types.hpp +++ b/include/trgdataformats/Types.hpp @@ -1,6 +1,11 @@ /** * @file Types.hpp * + * This header defines the types used in this package for various + * kinds of data (timestamps, channels, etc.). It also contains some + * predefined constants representing invalid values as well as a "read + * out the whole detector" value. + * * This is part of the DUNE DAQ Application Framework, copyright 2020. * Licensing/copyright details are in the COPYING file that you should have * received with this code.