Line data Source code
1 : /**
2 : * @file RandomTCMakerModule.cpp RandomTCMakerModule class
3 : * implementation
4 : *
5 : * This is part of the DUNE DAQ Software Suite, copyright 2020.
6 : * Licensing/copyright details are in the COPYING file that you should have
7 : * received with this code.
8 : */
9 :
10 : #include "RandomTCMakerModule.hpp"
11 :
12 : #include "trigger/Issues.hpp"
13 :
14 : #include "daqdataformats/ComponentRequest.hpp"
15 : #include "dfmessages/TimeSync.hpp"
16 : #include "dfmessages/TriggerDecision.hpp"
17 : #include "dfmessages/TriggerInhibit.hpp"
18 : #include "dfmessages/Types.hpp"
19 : #include "iomanager/IOManager.hpp"
20 : #include "logging/Logging.hpp"
21 : #include "trgdataformats/Types.hpp"
22 : #include "triggeralgs/TriggerCandidate.hpp"
23 : #include "utilities/TimestampEstimatorSystem.hpp"
24 : #include "utilities/TimestampEstimatorTimeSync.hpp"
25 :
26 : #include <algorithm>
27 : #include <cassert>
28 : #include <pthread.h>
29 : #include <random>
30 : #include <string>
31 : #include <vector>
32 :
33 : namespace dunedaq {
34 :
35 : namespace trigger {
36 :
37 0 : RandomTCMakerModule::RandomTCMakerModule(const std::string& name)
38 : : DAQModule(name)
39 0 : , m_time_sync_source(nullptr)
40 0 : , m_trigger_candidate_sink(nullptr)
41 0 : , m_run_number(0)
42 : {
43 0 : register_command("conf", &RandomTCMakerModule::do_configure);
44 0 : register_command("start", &RandomTCMakerModule::do_start);
45 0 : register_command("stop_trigger_sources", &RandomTCMakerModule::do_stop);
46 0 : register_command("scrap", &RandomTCMakerModule::do_scrap);
47 0 : register_command("change_rate", &RandomTCMakerModule::do_change_trigger_rate);
48 0 : }
49 :
50 : void
51 0 : RandomTCMakerModule::init(std::shared_ptr<appfwk::ConfigurationManager> mcfg)
52 : {
53 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering init() method";
54 :
55 0 : m_mtrg = mcfg->get_dal<appmodel::RandomTCMakerModule>(get_name());
56 :
57 : // Get the clock speed from detector configuration
58 0 : m_clock_speed_hz = mcfg->get_session()->get_detector_configuration()->get_clock_speed_hz();
59 :
60 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Exiting init() method";
61 0 : }
62 :
63 : void
64 0 : RandomTCMakerModule::generate_opmon_data()
65 : {
66 0 : opmon::RandomTCMakerInfo info;
67 :
68 0 : info.set_tc_made_count(m_tc_made_count.load());
69 0 : info.set_tc_sent_count(m_tc_sent_count.load());
70 0 : info.set_tc_failed_sent_count(m_tc_failed_sent_count.load());
71 :
72 0 : this->publish(std::move(info));
73 :
74 0 : if (m_latency_monitoring.load() && m_running_flag.load()) {
75 0 : opmon::TriggerLatencyStandalone lat_info;
76 :
77 0 : lat_info.set_latency_out(m_latency_instance.get_latency_out());
78 :
79 0 : this->publish(std::move(lat_info));
80 0 : }
81 0 : }
82 :
83 : void
84 0 : RandomTCMakerModule::do_configure(const CommandData_t& /*obj*/)
85 : {
86 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering conf() method";
87 :
88 : // Get the output connections
89 0 : for (auto con : m_mtrg->get_outputs()) {
90 0 : TLOG() << "TC sink is " << con->class_name() << "@" << con->UID();
91 0 : m_trigger_candidate_sink = get_iom_sender<triggeralgs::TriggerCandidate>(con->UID());
92 : }
93 :
94 : // Get the input connections
95 0 : for (auto con : m_mtrg->get_inputs()) {
96 : // Get the time sync source
97 0 : TLOG() << "TimeSync receiver connection is " << con->class_name() << "@" << con->UID() << " with tag "
98 0 : << get_name();
99 0 : m_time_sync_source = get_iomanager()->get_receiver<dfmessages::TimeSync>(con->UID(), get_name());
100 : }
101 0 : m_conf = m_mtrg->get_configuration();
102 :
103 : // Get the TC out configuration
104 0 : const appmodel::TCReadoutMap* tc_readout = m_conf->get_tc_readout();
105 0 : m_tcout_time_before = tc_readout->get_time_before();
106 0 : m_tcout_time_after = tc_readout->get_time_after();
107 0 : m_tcout_type =
108 0 : static_cast<TCType>(dunedaq::trgdataformats::string_to_trigger_candidate_type(tc_readout->get_tc_type_name()));
109 :
110 : // Throw error if unknown TC type
111 0 : if (m_tcout_type == TCType::kUnknown) {
112 0 : throw(InvalidConfiguration(ERS_HERE, "Provided an unknown TC type output to RandomTCMakerModule!"));
113 : }
114 :
115 0 : m_latency_monitoring.store(m_conf->get_latency_monitoring());
116 0 : m_trigger_rate_hz.store(m_conf->get_trigger_rate_hz());
117 :
118 0 : TLOG() << "RandomTCMaker will output TC of type: " << tc_readout->get_tc_type_name();
119 0 : TLOG() << "TC window time before: " << m_tcout_time_before << " time after: " << m_tcout_time_after;
120 0 : TLOG() << "Clock speed is: " << m_clock_speed_hz;
121 0 : TLOG() << "Output trigger rate is: " << m_trigger_rate_hz.load();
122 :
123 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << ": Exiting conf() method";
124 0 : }
125 :
126 : void
127 0 : RandomTCMakerModule::do_start(const CommandData_t& obj)
128 : {
129 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering start() method";
130 0 : m_run_number = obj.value<dunedaq::daqdataformats::run_number_t>("run", 0);
131 :
132 0 : m_running_flag.store(true);
133 :
134 : // OpMon.
135 0 : m_tc_made_count.store(0);
136 0 : m_tc_sent_count.store(0);
137 0 : m_tc_failed_sent_count.store(0);
138 :
139 0 : std::string timestamp_method = m_conf->get_timestamp_method();
140 0 : if (timestamp_method == "kTimeSync") {
141 0 : TLOG_DEBUG(0) << "Creating TimestampEstimatorTimeSync";
142 0 : m_timestamp_estimator.reset(new utilities::TimestampEstimatorTimeSync(m_run_number, m_clock_speed_hz));
143 0 : m_time_sync_source->add_callback(
144 0 : std::bind(&utilities::TimestampEstimatorTimeSync::timesync_callback<dfmessages::TimeSync>,
145 0 : reinterpret_cast<utilities::TimestampEstimatorTimeSync*>(m_timestamp_estimator.get()),
146 : std::placeholders::_1));
147 0 : } else if (timestamp_method == "kSystemClock") {
148 0 : TLOG_DEBUG(0) << "Creating TimestampEstimatorSystem";
149 0 : m_timestamp_estimator.reset(new utilities::TimestampEstimatorSystem(m_clock_speed_hz));
150 : } else {
151 : // TODO: write some error message
152 : }
153 :
154 : // Check if the trigger rate was changed in the starting parameters, and set it if so
155 0 : auto start_params = obj.get<rcif::cmd::StartParams>();
156 0 : if (start_params.trigger_rate > 0) {
157 0 : TLOG() << "Changing trigger rate from " << m_trigger_rate_hz.load() << " to " << start_params.trigger_rate;
158 0 : m_trigger_rate_hz.store(start_params.trigger_rate);
159 : }
160 :
161 0 : m_send_trigger_candidates_thread = std::thread(&RandomTCMakerModule::send_trigger_candidates, this);
162 0 : pthread_setname_np(m_send_trigger_candidates_thread.native_handle(), "random-tc-maker");
163 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << ": Exiting start() method";
164 0 : }
165 :
166 : void
167 0 : RandomTCMakerModule::do_stop(const CommandData_t& /*obj*/)
168 : {
169 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering stop() method";
170 0 : m_running_flag.store(false);
171 :
172 0 : m_send_trigger_candidates_thread.join();
173 :
174 0 : if (m_conf->get_timestamp_method() == "kTimeSync") {
175 0 : m_time_sync_source->remove_callback();
176 : }
177 :
178 0 : m_timestamp_estimator.reset(nullptr); // Calls TimestampEstimator dtor
179 :
180 0 : print_opmon_stats();
181 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << ": Exiting stop() method";
182 0 : }
183 :
184 : void
185 0 : RandomTCMakerModule::do_scrap(const CommandData_t& /*obj*/)
186 : {
187 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering scrap() method";
188 0 : m_time_sync_source.reset();
189 0 : m_trigger_candidate_sink.reset();
190 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << ": Exiting scrap() method";
191 0 : }
192 :
193 : void
194 0 : RandomTCMakerModule::do_change_trigger_rate(const CommandData_t& obj)
195 : {
196 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << get_name() << ": Entering change-rate() method";
197 0 : auto change_rate_params = obj.get<rcif::cmd::ChangeRateParams>();
198 :
199 0 : TLOG() << "Changing trigger rate from " << m_trigger_rate_hz.load() << " to " << change_rate_params.trigger_rate;
200 :
201 0 : m_trigger_rate_hz.store(change_rate_params.trigger_rate);
202 0 : TLOG_DEBUG(TLVL_ENTER_EXIT_METHODS) << ": Exiting change-rate() method";
203 0 : }
204 :
205 : triggeralgs::TriggerCandidate
206 0 : RandomTCMakerModule::create_candidate(dfmessages::timestamp_t timestamp)
207 : {
208 0 : triggeralgs::TriggerCandidate candidate;
209 0 : candidate.time_start = (timestamp - m_tcout_time_before);
210 0 : candidate.time_end = (timestamp + m_tcout_time_after);
211 0 : candidate.time_candidate = timestamp;
212 0 : candidate.detid = { 0 };
213 0 : candidate.type = m_tcout_type;
214 :
215 : // TODO: Originally kHSIEventToTriggerCandidate
216 0 : candidate.algorithm = triggeralgs::TriggerCandidate::Algorithm::kCustom;
217 :
218 0 : return candidate;
219 : }
220 :
221 : uint64_t
222 0 : RandomTCMakerModule::get_interval(std::mt19937& gen)
223 : {
224 0 : std::string time_distribution = m_conf->get_time_distribution();
225 :
226 0 : uint64_t interval = m_clock_speed_hz / m_trigger_rate_hz.load();
227 :
228 0 : if (time_distribution == "kUniform") {
229 : return interval;
230 0 : } else if (time_distribution == "kPoisson") {
231 0 : std::exponential_distribution<double> d(1.0 / interval);
232 0 : return static_cast<uint64_t>(0.5 + d(gen));
233 : } else {
234 0 : TLOG_DEBUG(1) << get_name() << " unknown distribution! Using kUniform.";
235 : }
236 0 : return interval;
237 0 : }
238 :
239 : void
240 0 : RandomTCMakerModule::send_trigger_candidates()
241 : {
242 : // OpMon.
243 0 : m_tc_sent_count.store(0);
244 :
245 0 : std::mt19937 gen(m_run_number);
246 0 : dfmessages::timestamp_t initial_timestamp;
247 : // Wait for there to be a valid timestamp estimate before we start
248 0 : if (m_timestamp_estimator->wait_for_valid_timestamp(m_running_flag, initial_timestamp) ==
249 : utilities::TimestampEstimatorBase::kInterrupted) {
250 0 : return;
251 : }
252 :
253 0 : dfmessages::timestamp_t next_trigger_timestamp = initial_timestamp;
254 0 : TLOG_DEBUG(1) << get_name() << " initial timestamp estimate is " << initial_timestamp;
255 :
256 0 : while (m_running_flag.load()) {
257 : // If trigger rate is 0, just sleep. Need to use small number here because
258 : // of floating point precision...
259 0 : constexpr float epsilon = 1e-9;
260 0 : if (m_trigger_rate_hz.load() <= epsilon) {
261 0 : std::this_thread::sleep_for(std::chrono::milliseconds(100));
262 0 : continue;
263 : }
264 :
265 0 : dfmessages::timestamp_t actual_timestamp;
266 0 : if (m_timestamp_estimator->wait_for_requested_timestamp(next_trigger_timestamp, m_running_flag, actual_timestamp) ==
267 : utilities::TimestampEstimatorBase::kInterrupted) {
268 : break;
269 : }
270 0 : triggeralgs::TriggerCandidate candidate = create_candidate(actual_timestamp);
271 :
272 0 : m_tc_made_count++;
273 :
274 0 : TLOG_DEBUG(1) << get_name() << " at timestamp " << actual_timestamp
275 0 : << ", pushing a candidate with timestamp " << candidate.time_candidate;
276 :
277 0 : if (m_latency_monitoring.load())
278 0 : m_latency_instance.update_latency_out(candidate.time_candidate);
279 0 : try {
280 0 : m_trigger_candidate_sink->send(std::move(candidate), std::chrono::milliseconds(10));
281 0 : m_tc_sent_count++;
282 0 : } catch (const ers::Issue& e) {
283 0 : ers::error(e);
284 0 : m_tc_failed_sent_count++;
285 0 : }
286 :
287 0 : next_trigger_timestamp = actual_timestamp + get_interval(gen);
288 0 : }
289 : }
290 :
291 : void
292 0 : RandomTCMakerModule::print_opmon_stats()
293 : {
294 0 : TLOG() << "RandomTCMaker opmon counters summary:";
295 0 : TLOG() << "------------------------------";
296 0 : TLOG() << "Made TCs: \t\t\t" << m_tc_made_count;
297 0 : TLOG() << "Sent TCs: \t\t\t" << m_tc_sent_count;
298 0 : TLOG() << "Failed to send TCs: \t" << m_tc_failed_sent_count;
299 0 : TLOG();
300 0 : }
301 :
302 : } // namespace trigger
303 : } // namespace dunedaq
304 :
305 0 : DEFINE_DUNE_DAQ_MODULE(dunedaq::trigger::RandomTCMakerModule)
306 :
307 : // Local Variables:
308 : // c-basic-offset: 2
309 : // End:
|