diff --git a/CMakeLists.txt b/CMakeLists.txt index c56dd2de..4cc85903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ daq_codegen( tpsetbuffercreator.jsonnet tasetsink.jsonnet tpchannelfilter.jsonnet + cibtriggercandidatemaker.jsonnet TEMPLATES Structs.hpp.j2 Nljs.hpp.j2 ) daq_codegen( @@ -71,6 +72,7 @@ daq_codegen( *info.jsonnet DEP_PKGS opmonlib TEMPLATES opmonlib/InfoStructs.hpp. daq_add_plugin(TimingTriggerCandidateMaker duneDAQModule SCHEMA LINK_LIBRARIES trigger) daq_add_plugin(CTBTriggerCandidateMaker duneDAQModule SCHEMA LINK_LIBRARIES trigger) +daq_add_plugin(CIBTriggerCandidateMaker duneDAQModule SCHEMA LINK_LIBRARIES trigger) daq_add_plugin(TriggerPrimitiveMaker duneDAQModule LINK_LIBRARIES trigger) daq_add_plugin(TriggerActivityMaker duneDAQModule LINK_LIBRARIES trigger) daq_add_plugin(TriggerCandidateMaker duneDAQModule LINK_LIBRARIES trigger) diff --git a/include/trigger/Issues.hpp b/include/trigger/Issues.hpp index cbee2bbb..50129784 100644 --- a/include/trigger/Issues.hpp +++ b/include/trigger/Issues.hpp @@ -193,6 +193,16 @@ ERS_DECLARE_ISSUE_BASE(trigger, ((std::bitset<32>)bits) ((size_t)map_size)) +ERS_DECLARE_ISSUE_BASE(trigger, + InvalidCIBSignal, + appfwk::GeneralDAQModuleIssue, + "An invalid CIB signal was received, signal: " << signal << ", bits: " << bits + << ", configured CIB bits: " << map_size, + ((std::string)name), + ((uint32_t)signal) + ((std::bitset<32>)bits) + ((size_t)map_size)) + ERS_DECLARE_ISSUE(trigger, MissingFactoryItemError, "Factory could not find requested item " << item << ".", diff --git a/plugins/CIBTriggerCandidateMaker.cpp b/plugins/CIBTriggerCandidateMaker.cpp new file mode 100644 index 00000000..39e12791 --- /dev/null +++ b/plugins/CIBTriggerCandidateMaker.cpp @@ -0,0 +1,204 @@ +/** + * @file CIBTriggerCandidateMaker.cpp + * + * 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 "CIBTriggerCandidateMaker.hpp" +#include "trigger/Logging.hpp" + +#include "appfwk/DAQModuleHelper.hpp" +#include "iomanager/IOManager.hpp" +#include "rcif/cmd/Nljs.hpp" +#include "trgdataformats/Types.hpp" +#include "trigger/TriggerCandidate_serialization.hpp" + +#include +#include +#include + +using dunedaq::trigger::logging::TLVL_DEBUG_ALL; +using dunedaq::trigger::logging::TLVL_DEBUG_HIGH; +using dunedaq::trigger::logging::TLVL_DEBUG_MEDIUM; +using dunedaq::trigger::logging::TLVL_GENERAL; +using dunedaq::trigger::logging::TLVL_VERY_IMPORTANT; + +namespace dunedaq { +namespace trigger { + +CIBTriggerCandidateMaker::CIBTriggerCandidateMaker(const std::string& name) + : DAQModule(name) + , m_output_queue(nullptr) + , m_queue_timeout(100) +{ + + register_command("conf", &CIBTriggerCandidateMaker::do_conf); + register_command("start", &CIBTriggerCandidateMaker::do_start); + register_command("stop", &CIBTriggerCandidateMaker::do_stop); + register_command("scrap", &CIBTriggerCandidateMaker::do_scrap); +} + +std::vector +CIBTriggerCandidateMaker::HSIEventToTriggerCandidate(const dfmessages::HSIEvent& data) +{ + TLOG_DEBUG(TLVL_DEBUG_MEDIUM) << "[CIB] Converting HSI event, signal: " << data.signal_map; + + std::vector candidates; + std::bitset<32> bits(data.signal_map); + TLOG_DEBUG(TLVL_DEBUG_HIGH) << "[CIB] BITS: " << bits; + + for (size_t i = 0; i < bits.size(); ++i) { + if (bits.test(i)) { + + TLOG_DEBUG(TLVL_DEBUG_ALL) << "[CIB] this bit: " << i; + + if (m_CIB_TC_map.count(i)) { + TLOG_DEBUG(TLVL_DEBUG_ALL) << "[CIB] TC type: " << static_cast(m_CIB_TC_map[i]); + + triggeralgs::TriggerCandidate candidate; + candidate.time_candidate = data.timestamp; + candidate.time_start = data.timestamp - m_time_before; + candidate.time_end = data.timestamp + m_time_after; + // candidate.detid = 1; + candidate.detid = data.header; + candidate.type = m_CIB_TC_map[i]; + candidate.algorithm = triggeralgs::TriggerCandidate::Algorithm::kHSIEventToTriggerCandidate; + candidate.inputs = {}; + + candidates.push_back(candidate); + } else { + ers::error( + dunedaq::trigger::InvalidCIBSignal(ERS_HERE, get_name(), data.signal_map, bits, m_CIB_TC_map.size())); + } + } + } + + return candidates; +} + +void +CIBTriggerCandidateMaker::do_conf(const nlohmann::json& config) +{ + auto params = config.get(); + m_time_before = params.time_before; + m_time_after = params.time_after; + m_prescale = params.prescale; + m_prescale_flag = (m_prescale > 1) ? true : false; + TLOG_DEBUG(TLVL_GENERAL) << "[CIB] " << get_name() + " configured."; + TLOG_DEBUG(TLVL_VERY_IMPORTANT) << "[CIB] Time before: " << m_time_before; + TLOG_DEBUG(TLVL_VERY_IMPORTANT) << "[CIB] Time after: " << m_time_after; + if (m_prescale_flag) { + TLOG_DEBUG(TLVL_VERY_IMPORTANT) << "[CIB] Running with prescale at: " << m_prescale; + } +} + +void +CIBTriggerCandidateMaker::init(const nlohmann::json& iniobj) +{ + try { + auto ci = appfwk::connection_index(iniobj, { "output", "hsi_input" }); + m_output_queue = get_iom_sender(ci["output"]); + m_hsievent_input = get_iom_receiver(ci["hsi_input"]); + } catch (const ers::Issue& excpt) { + throw dunedaq::trigger::InvalidQueueFatalError(ERS_HERE, get_name(), "input/output", excpt); + } +} + +void +CIBTriggerCandidateMaker::do_start(const nlohmann::json& startobj) +{ + // OpMon. + m_tsd_received_count.store(0); + m_tc_sent_count.store(0); + m_tc_sig_type_err_count.store(0); + m_tc_total_count.store(0); + + auto start_params = startobj.get(); + m_run_number.store(start_params.run); + + m_hsievent_input->add_callback(std::bind(&CIBTriggerCandidateMaker::receive_hsievent, this, std::placeholders::_1)); + + TLOG_DEBUG(TLVL_GENERAL) << "[CIB] " << get_name() + " successfully started."; +} + +void +CIBTriggerCandidateMaker::do_stop(const nlohmann::json&) +{ + m_hsievent_input->remove_callback(); + + TLOG() << "[CIB] Received " << m_tsd_received_count << " HSIEvent messages. Successfully sent " << m_tc_sent_count + << " TriggerCandidates"; + TLOG_DEBUG(TLVL_GENERAL) << "[CIB] " << get_name() + " successfully stopped."; +} + +void +CIBTriggerCandidateMaker::receive_hsievent(dfmessages::HSIEvent& data) +{ + TLOG_DEBUG(TLVL_DEBUG_MEDIUM) << "[CIB] Activity received with timestamp " << data.timestamp << ", sequence_counter " + << data.sequence_counter << ", and run_number " << data.run_number; + + if (data.run_number != m_run_number) { + ers::error(dunedaq::trigger::InvalidHSIEventRunNumber( + ERS_HERE, get_name(), data.run_number, m_run_number, data.timestamp, data.sequence_counter)); + return; + } + + ++m_tsd_received_count; + + if (m_prescale_flag) { + if (m_tsd_received_count % m_prescale != 0) { + return; + } + } + + std::vector candidates; + try { + candidates = HSIEventToTriggerCandidate(data); + } catch (SignalTypeError& e) { + m_tc_sig_type_err_count++; + ers::error(e); + return; + } + + for (const auto& candidate : candidates) { + bool successfullyWasSent = false; + while (!successfullyWasSent) { + try { + triggeralgs::TriggerCandidate candidate_copy(candidate); + m_output_queue->send(std::move(candidate_copy), m_queue_timeout); + successfullyWasSent = true; + ++m_tc_sent_count; + } catch (const dunedaq::iomanager::TimeoutExpired& excpt) { + std::ostringstream oss_warn; + oss_warn << "push to output queue \"" << m_output_queue->get_name() << "\""; + ers::warning(dunedaq::iomanager::TimeoutExpired(ERS_HERE, get_name(), oss_warn.str(), m_queue_timeout.count())); + } + } + m_tc_total_count++; + } +} + +void +CIBTriggerCandidateMaker::do_scrap(const nlohmann::json&) +{ +} + +void +CIBTriggerCandidateMaker::get_info(opmonlib::InfoCollector& ci, int /*level*/) +{ + cibtriggercandidatemakerinfo::Info i; + + i.tsd_received_count = m_tsd_received_count.load(); + i.tc_sent_count = m_tc_sent_count.load(); + i.tc_sig_type_err_count = m_tc_sig_type_err_count.load(); + i.tc_total_count = m_tc_total_count.load(); + + ci.add(i); +} + +} // namespace trigger +} // namespace dunedaq + +DEFINE_DUNE_DAQ_MODULE(dunedaq::trigger::CIBTriggerCandidateMaker) diff --git a/plugins/CIBTriggerCandidateMaker.hpp b/plugins/CIBTriggerCandidateMaker.hpp new file mode 100644 index 00000000..8ee9ed71 --- /dev/null +++ b/plugins/CIBTriggerCandidateMaker.hpp @@ -0,0 +1,91 @@ +/** + * @file CIBTriggerCandidateMaker.cpp + * + * 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. + */ + +#ifndef TRIGGER_PLUGINS_CIBTRIGGERCANDIDATEMAKER_HPP_ +#define TRIGGER_PLUGINS_CIBTRIGGERCANDIDATEMAKER_HPP_ + +#include "trigger/Issues.hpp" +#include "trigger/cibtriggercandidatemaker/Nljs.hpp" +#include "trigger/cibtriggercandidatemakerinfo/InfoNljs.hpp" + +#include "appfwk/DAQModule.hpp" +#include "daqdataformats/Types.hpp" +#include "dfmessages/HSIEvent.hpp" +#include "iomanager/Receiver.hpp" +#include "iomanager/Sender.hpp" +#include "triggeralgs/TriggerActivity.hpp" +#include "triggeralgs/TriggerCandidate.hpp" +#include "utilities/WorkerThread.hpp" + +#include +#include +#include +#include +#include + +namespace dunedaq { +namespace trigger { +class CIBTriggerCandidateMaker : public dunedaq::appfwk::DAQModule +{ +public: + explicit CIBTriggerCandidateMaker(const std::string& name); + + CIBTriggerCandidateMaker(const CIBTriggerCandidateMaker&) = delete; + CIBTriggerCandidateMaker& operator=(const CIBTriggerCandidateMaker&) = delete; + CIBTriggerCandidateMaker(CIBTriggerCandidateMaker&&) = delete; + CIBTriggerCandidateMaker& operator=(CIBTriggerCandidateMaker&&) = delete; + + void init(const nlohmann::json& iniobj) override; + void get_info(opmonlib::InfoCollector& ci, int level) override; + +private: + void do_conf(const nlohmann::json& config); + void do_start(const nlohmann::json& obj); + void do_stop(const nlohmann::json& obj); + void do_scrap(const nlohmann::json& obj); + + std::string m_hsievent_receive_connection; + + // Prescale functionality + bool m_prescale_flag; + int m_prescale; + + // Config + int m_time_before; + int m_time_after; + + std::vector HSIEventToTriggerCandidate(const dfmessages::HSIEvent& data); + void receive_hsievent(dfmessages::HSIEvent& data); + + using sink_t = dunedaq::iomanager::SenderConcept; + std::shared_ptr m_output_queue; + std::shared_ptr> m_hsievent_input; + + std::chrono::milliseconds m_queue_timeout; + + // HLT to TC type map + std::map m_CIB_TC_map = { + { 0, trgdataformats::TriggerCandidateData::Type::kCIBFakeTrigger }, + { 1, trgdataformats::TriggerCandidateData::Type::kCIBLaserTriggerP1 }, + { 2, trgdataformats::TriggerCandidateData::Type::kCIBLaserTriggerP2 }, + { 3, trgdataformats::TriggerCandidateData::Type::kCIBLaserTriggerP3 }, + }; + + // Opmon variables + using metric_counter_type = decltype(cibtriggercandidatemakerinfo::Info::tsd_received_count); + std::atomic m_tsd_received_count{ 0 }; + std::atomic m_tc_sent_count{ 0 }; + std::atomic m_tc_sig_type_err_count{ 0 }; + std::atomic m_tc_total_count{ 0 }; + + std::atomic m_run_number; +}; +} // namespace trigger +} // namespace dunedaq + +#endif // TRIGGER_PLUGINS_CIBTRIGGERCANDIDATEMAKER_HPP_ diff --git a/schema/trigger/cibtriggercandidatemaker.jsonnet b/schema/trigger/cibtriggercandidatemaker.jsonnet new file mode 100644 index 00000000..16fe6636 --- /dev/null +++ b/schema/trigger/cibtriggercandidatemaker.jsonnet @@ -0,0 +1,17 @@ +local moo = import "moo.jsonnet"; +local ns = "dunedaq.trigger.cibtriggercandidatemaker"; +local s = moo.oschema.schema(ns); +local nc = moo.oschema.numeric_constraints; + +local types = { + count_t : s.number("count_t", "i8", nc(minimum=1), doc="Counter"), + time_t : s.number("time_t", "i8", doc="Time"), + conf: s.record("Conf", [ + s.field("prescale", self.count_t, default=1, doc="Option to prescale TTCM TCs"), + s.field("time_before", self.time_t, 1000, doc="Readout time before time stamp"), + s.field("time_after", self.time_t, 1000, doc="Readout time after time stamp") + ], doc="CIB configuration block"), + +}; + +moo.oschema.sort_select(types, ns) diff --git a/schema/trigger/cibtriggercandidatemakerinfo.jsonnet b/schema/trigger/cibtriggercandidatemakerinfo.jsonnet new file mode 100644 index 00000000..7c80cf51 --- /dev/null +++ b/schema/trigger/cibtriggercandidatemakerinfo.jsonnet @@ -0,0 +1,20 @@ +// This is the application info schema used by the cib trigger candidate maker module. +// It describes the information object structure passed by the application +// for operational monitoring + +local moo = import "moo.jsonnet"; +local s = moo.oschema.schema("dunedaq.trigger.cibtriggercandidatemakerinfo"); + +local info = { + uint8 : s.number("uint8", "u8", + doc="An unsigned of 8 bytes"), + + info: s.record("Info", [ + s.field("tsd_received_count", self.uint8, 0, doc="Number of HSIEvent messages received."), + s.field("tc_sent_count", self.uint8, 0, doc="Number of trigger candidates added to queue."), + s.field("tc_sig_type_err_count", self.uint8, 0, doc="Number of trigger candidates not added to queue due to a signal type error."), + s.field("tc_total_count", self.uint8, 0, doc="Total number of trigger candidates created."), + ], doc="CIB trigger candidate maker information.") +}; + +moo.oschema.sort_select(info)