diff --git a/plugins/ModuleLevelTrigger.cpp b/plugins/ModuleLevelTrigger.cpp index c10692bc..1b4cafe3 100644 --- a/plugins/ModuleLevelTrigger.cpp +++ b/plugins/ModuleLevelTrigger.cpp @@ -100,11 +100,18 @@ ModuleLevelTrigger::do_configure(const nlohmann::json& confobj) { auto params = confobj.get(); - m_links.clear(); - for (auto const& link : params.links) { - m_links.push_back( + m_mandatory_links.clear(); + for (auto const& link : params.mandatory_links) { + m_mandatory_links.push_back( dfmessages::SourceID{ daqdataformats::SourceID::string_to_subsystem(link.subsystem), link.element }); } + m_group_links.clear(); + m_group_links_data = params.groups_links; + parse_group_links(m_group_links_data); + print_group_links(); + m_total_group_links = m_group_links.size(); + TLOG_DEBUG(3) << "Total group links: " << m_total_group_links; + // m_trigger_decision_connection = params.dfo_connection; // m_inhibit_connection = params.dfo_busy_connection; m_hsi_passthrough = params.hsi_trigger_type_passthrough; @@ -117,11 +124,20 @@ ModuleLevelTrigger::do_configure(const nlohmann::json& confobj) m_td_readout_limit = params.td_readout_limit; m_ignoring_tc_types = (m_ignored_tc_types.size() > 0) ? true : false; m_use_readout_map = params.use_readout_map; + m_use_roi_readout = params.use_roi_readout; m_use_bitwords = params.use_bitwords; TLOG_DEBUG(3) << "Allow merging: " << m_tc_merging; TLOG_DEBUG(3) << "Buffer timeout: " << m_buffer_timeout; TLOG_DEBUG(3) << "Should send timed out TDs: " << m_send_timed_out_tds; TLOG_DEBUG(3) << "TD readout limit: " << m_td_readout_limit; + TLOG_DEBUG(3) << "Use ROI readout?: " << m_use_roi_readout; + + // ROI map + if (m_use_roi_readout) { + m_roi_conf_data = params.roi_conf; + parse_roi_conf(m_roi_conf_data); + print_roi_conf(m_roi_conf); + } // Custom readout map TLOG_DEBUG(3) << "Use readout map: " << m_use_readout_map; @@ -228,7 +244,8 @@ ModuleLevelTrigger::do_resume(const nlohmann::json& /*resumeobj*/) void ModuleLevelTrigger::do_scrap(const nlohmann::json& /*scrapobj*/) { - m_links.clear(); + m_mandatory_links.clear(); + m_group_links.clear(); m_configured_flag.store(false); } @@ -268,13 +285,18 @@ ModuleLevelTrigger::create_decision(const ModuleLevelTrigger::PendingTD& pending << ", request window begin: " << pending_td.readout_start << ", request window end: " << pending_td.readout_end; - for (auto link : m_links) { - dfmessages::ComponentRequest request; - request.component = link; - request.window_begin = pending_td.readout_start; - request.window_end = pending_td.readout_end; + std::vector requests = + create_all_decision_requests(m_mandatory_links, pending_td.readout_start, pending_td.readout_end); + add_requests_to_decision(decision, requests); - decision.components.push_back(request); + if (!m_use_roi_readout) { + for (const auto& [key, value] : m_group_links) { + std::vector group_requests = + create_all_decision_requests(value, pending_td.readout_start, pending_td.readout_end); + add_requests_to_decision(decision, group_requests); + } + } else { // using ROI readout + roi_readout_make_requests(decision); } return decision; @@ -783,6 +805,170 @@ ModuleLevelTrigger::print_readout_map(std::map temp_links; + for (auto link : temp_links_data) { + temp_links.push_back( + dfmessages::SourceID{ daqdataformats::SourceID::string_to_subsystem(link["subsystem"]), link["element"] }); + } + m_group_links.insert({ group["group"], temp_links }); + } + return; +} + +void +ModuleLevelTrigger::print_group_links() +{ + TLOG_DEBUG(3) << "MLT Group Links:"; + for (auto const& [key, val] : m_group_links) { + TLOG_DEBUG(3) << "Group: " << key; + for (auto const& link : val) { + TLOG_DEBUG(3) << link; + } + } + TLOG_DEBUG(3) << " "; + return; +} + +dfmessages::ComponentRequest +ModuleLevelTrigger::create_request_for_link(dfmessages::SourceID link, + triggeralgs::timestamp_t start, + triggeralgs::timestamp_t end) +{ + dfmessages::ComponentRequest request; + request.component = link; + request.window_begin = start; + request.window_end = end; + + return request; +} + +std::vector +ModuleLevelTrigger::create_all_decision_requests(std::vector links, + triggeralgs::timestamp_t start, + triggeralgs::timestamp_t end) +{ + std::vector requests; + for (auto link : links) { + requests.push_back(create_request_for_link(link, start, end)); + } + return requests; +} + +void +ModuleLevelTrigger::add_requests_to_decision(dfmessages::TriggerDecision& decision, + std::vector requests) +{ + for (auto request : requests) { + decision.components.push_back(request); + } +} + +void +ModuleLevelTrigger::parse_roi_conf(const nlohmann::json& data) +{ + int counter = 0; + float run_sum = 0; + for (auto group : data) { + roi_group temp_roi_group; + temp_roi_group.n_links = group["number_of_link_groups"]; + temp_roi_group.prob = group["probability"]; + temp_roi_group.time_window = group["time_window"]; + temp_roi_group.mode = group["groups_selection_mode"]; + m_roi_conf.insert({ counter, temp_roi_group }); + m_roi_conf_ids.push_back(counter); + m_roi_conf_probs.push_back(group["probability"]); + run_sum += static_cast(group["probability"]); + m_roi_conf_probs_c.push_back(run_sum); + counter++; + } + return; +} + +void +ModuleLevelTrigger::print_roi_conf(std::map roi_conf) +{ + TLOG_DEBUG(3) << "ROI CONF"; + for (const auto& [key, value] : roi_conf) { + TLOG_DEBUG(3) << "ID: " << key; + TLOG_DEBUG(3) << "n links: " << value.n_links; + TLOG_DEBUG(3) << "prob: " << value.prob; + TLOG_DEBUG(3) << "time: " << value.time_window; + TLOG_DEBUG(3) << "mode: " << value.mode; + } + TLOG_DEBUG(3) << " "; + return; +} + +float +ModuleLevelTrigger::get_random_num_float(float limit) +{ + float rnd = (double)rand() / RAND_MAX; + return rnd * (limit); +} + +int +ModuleLevelTrigger::pick_roi_group_conf() +{ + float rnd_num = get_random_num_float(m_roi_conf_probs_c.back()); + for (int i = 0; i < static_cast(m_roi_conf_probs_c.size()); i++) { + if (rnd_num < m_roi_conf_probs_c[i]) { + return i; + } + } + return -1; +} + +int +ModuleLevelTrigger::get_random_num_int() +{ + int range = m_total_group_links; + int rnd = rand() % range; + return rnd; +} + +void +ModuleLevelTrigger::roi_readout_make_requests(dfmessages::TriggerDecision& decision) +{ + // Get configuration at random (weighted) + int group_pick = pick_roi_group_conf(); + if (group_pick != -1) { + roi_group this_group = m_roi_conf[m_roi_conf_ids[group_pick]]; + std::vector links; + + // If mode is random, pick groups to request at random + if (this_group.mode == "kRandom") { + TLOG_DEBUG(3) << "RAND"; + std::set groups; + while (static_cast(groups.size()) < this_group.n_links) { + groups.insert(get_random_num_int()); + } + for (auto r_id : groups) { + links.insert(links.end(), m_group_links[r_id].begin(), m_group_links[r_id].end()); + } + // Otherwise, read sequntially by IDs, starting at 0 + } else { + TLOG_DEBUG(3) << "SEQ"; + int r_id = 0; + while (r_id < this_group.n_links) { + links.insert(links.end(), m_group_links[r_id].begin(), m_group_links[r_id].end()); + r_id++; + } + } + + // Once the components are prepared, create requests and append them to decision + std::vector requests = + create_all_decision_requests(links, this_group.time_window, this_group.time_window); + add_requests_to_decision(decision, requests); + links.clear(); + } + return; +} + } // namespace trigger } // namespace dunedaq diff --git a/plugins/ModuleLevelTrigger.hpp b/plugins/ModuleLevelTrigger.hpp index 47defd64..aeccfb8e 100644 --- a/plugins/ModuleLevelTrigger.hpp +++ b/plugins/ModuleLevelTrigger.hpp @@ -81,7 +81,42 @@ class ModuleLevelTrigger : public dunedaq::appfwk::DAQModule std::shared_ptr> m_inhibit_input; std::string m_td_output_connection; - std::vector m_links; + // TD requests + std::vector m_mandatory_links; + std::map> m_group_links; + nlohmann::json m_group_links_data; + int m_total_group_links; + void parse_group_links(const nlohmann::json& data); + void print_group_links(); + dfmessages::ComponentRequest create_request_for_link(dfmessages::SourceID link, + triggeralgs::timestamp_t start, + triggeralgs::timestamp_t end); + std::vector create_all_decision_requests(std::vector links, + triggeralgs::timestamp_t start, + triggeralgs::timestamp_t end); + void add_requests_to_decision(dfmessages::TriggerDecision& decision, + std::vector requests); + + // ROI + bool m_use_roi_readout; + struct roi_group + { + int n_links; + float prob; + triggeralgs::timestamp_t time_window; + std::string mode; + }; + std::map m_roi_conf; + nlohmann::json m_roi_conf_data; + void parse_roi_conf(const nlohmann::json& data); + void print_roi_conf(std::map roi_conf); + std::vector m_roi_conf_ids; + std::vector m_roi_conf_probs; + std::vector m_roi_conf_probs_c; + float get_random_num_float(float limit); + int get_random_num_int(); + int pick_roi_group_conf(); + void roi_readout_make_requests(dfmessages::TriggerDecision& decision); int m_repeat_trigger_count{ 1 }; diff --git a/schema/trigger/moduleleveltrigger.jsonnet b/schema/trigger/moduleleveltrigger.jsonnet index bede7ed1..9b4a70f0 100644 --- a/schema/trigger/moduleleveltrigger.jsonnet +++ b/schema/trigger/moduleleveltrigger.jsonnet @@ -1,8 +1,10 @@ local moo = import "moo.jsonnet"; local ns = "dunedaq.trigger.moduleleveltrigger"; local s = moo.oschema.schema(ns); +local nc = moo.oschema.numeric_constraints; local types = { + group_id: s.number("group_id", "i4"), element_id : s.number("element_id_t", "u4"), subsystem : s.string("subsystem_t"), flag : s.boolean("Boolean", doc="Option for flags, true/false"), @@ -14,6 +16,9 @@ local types = { bitword: s.number( "Bitword", "i4", doc="An integer representing the TC type, to be set in the trigger bitword."), bitword_list: s.sequence( "BitwordList", self.bitword, doc="A sequence of bitword (bits) forming a bitword."), bitwords: s.sequence( "Bitwords", self.bitword_list, doc="List of bitwords to use when forming trigger decisions in MLT" ), + number_of_groups: s.number( "Ngroups", "i4", nc(minimum=0, maximum=150), doc="Number of groups of detector links to readout, for ROI config"), + probability: s.number( "Prob", "f4", nc(minimum=0.0, maximum=1.0), doc="Probability to read out a group of links, for ROI config"), + group_selection: s.enum( "GroupSelection", ["kRandom", "kSequential"]), tc_readout: s.record("tc_readout", [ s.field("candidate_type", self.tc_type, default=0, doc="The TC type"), @@ -91,10 +96,24 @@ local types = { ]), linkvec : s.sequence("link_vec", self.sourceid), - + grouplink: s.record("group_link", [ + s.field("group", self.group_id, doc="ID / group"), + s.field("links", self.linkvec, doc="List of associated elements"), + ]), + grouplinks: s.sequence("group_links", self.grouplink), + + roi_group_conf: s.record("roi_group_conf", [ + s.field("number_of_link_groups", self.number_of_groups, default=1, doc="Number of groups of links to readout"), + s.field("probability", self.probability, default=0.1, doc="Probability to select this configuration [0 to 1]"), + s.field("time_window", self.readout_time, default=1000, doc="Time window to read out pre/post decision, [clock ticks]"), + s.field("groups_selection_mode", self.group_selection, default="kRandom", doc="Whether to read out random groups or in sequence"), + ]), + + roi_conf_map: s.sequence("roi_conf_map", self.roi_group_conf), + conf : s.record("ConfParams", [ - s.field("links", self.linkvec, - doc="List of link identifiers that may be included into trigger decision"), + s.field("mandatory_links", self.linkvec, doc="List of link identifiers that will be included in trigger decision"), + s.field("groups_links", self.grouplinks, doc="List of link identifiers that may be included in trigger decision"), s.field("hsi_trigger_type_passthrough", self.flag, default=false, doc="Option to override the trigger type inside MLT"), s.field("merge_overlapping_tcs", self.flag, default=true, doc="Flag to allow(true)/disable(false) merging of overlapping TCs when forming TD"), s.field("td_out_of_timeout", self.flag, default=true, doc="Option to send TD if TC comes out of timeout window (late, overlapping already sent TD"), @@ -103,6 +122,8 @@ local types = { s.field("ignore_tc", self.tc_types, [], doc="List of TC types to be ignored"), s.field("use_bitwords", self.flag, default=false, doc="Option to use bitwords (ie trigger types, coincidences) when forming trigger decisions"), s.field("trigger_bitwords", self.bitwords, [], doc="Optional dictionary of bitwords to use when forming trigger decisions"), + s.field("use_roi_readout", self.flag, default=false, doc="Option to use ROI readout in MLT: only readout selection of fragment producers"), + s.field("roi_conf", self.roi_conf_map, default=[self.roi_group_conf], doc="The configuration (table) for ROI readout"), s.field("use_readout_map", self.flag, default=false, doc="Option to use defalt readout windows (tc.time_start and tc.time_end) or a custom readout map from daqconf"), s.field("td_readout_map", self.tc_readout_map, self.tc_readout_map, doc="A map holding readout pre/post depending on TC type"), ], doc="ModuleLevelTrigger configuration parameters"),