artdaq  v3_12_02
GenToBuffer_t.cc
1 #include "TRACE/tracemf.h"
2 #define TRACE_NAME "GenToBuffer"
3 
4 #include "artdaq-core/Utilities/configureMessageFacility.hh"
5 #include "artdaq/Application/LoadParameterSet.hh"
6 #include "artdaq/DAQdata/Globals.hh"
7 #include "artdaq/DAQrate/FragmentBuffer.hh"
8 #include "artdaq/DAQrate/RequestBuffer.hh"
9 #include "artdaq/Generators/CommandableFragmentGenerator.hh"
10 #include "artdaq/Generators/makeCommandableFragmentGenerator.hh"
11 
12 #include "fhiclcpp/ParameterSet.h"
13 #include "fhiclcpp/types/Atom.h"
14 #include "fhiclcpp/types/Comment.h"
15 #include "fhiclcpp/types/Name.h"
16 #include "fhiclcpp/types/Table.h"
17 #include "fhiclcpp/types/TableFragment.h"
18 
19 #include <boost/program_options.hpp>
20 namespace bpo = boost::program_options;
21 #include <boost/thread.hpp>
22 
23 #include <thread>
24 
25 namespace artdaq {
30 {
31 public:
36  explicit GenToBufferTest(fhicl::ParameterSet const& ps)
37  : generator_ptr_(nullptr)
38  , fragment_buffer_ptr_(new FragmentBuffer(ps))
39  , request_buffer_ptr_(new RequestBuffer(1))
40  , fragment_count_(0)
41  , running_(false)
42  {
43  generator_ptr_ = makeCommandableFragmentGenerator(ps.get<std::string>("generator"), ps);
44  generator_ptr_->SetRequestBuffer(request_buffer_ptr_);
45  fragment_buffer_ptr_->SetRequestBuffer(request_buffer_ptr_);
46  }
47 
52  void start(int run_number)
53  {
54  metricMan->do_start();
55  request_buffer_ptr_->setRunning(true);
56  generator_ptr_->StartCmd(run_number, 0, 0);
57 
58  running_ = true;
59  boost::thread::attributes attrs;
60  attrs.set_stack_size(4096 * 2000); // 8 MB
61  receive_thread_.reset(new boost::thread(attrs, boost::bind(&GenToBufferTest::receive_fragments, this)));
62  send_thread_.reset(new boost::thread(attrs, boost::bind(&GenToBufferTest::send_fragments, this)));
63  }
67  void stop()
68  {
69  generator_ptr_->StopCmd(0, 0);
70  fragment_buffer_ptr_->Stop();
71  request_buffer_ptr_->setRunning(false);
72 
73  running_ = false;
74  if (receive_thread_ && receive_thread_->joinable()) receive_thread_->join();
75  if (send_thread_ && send_thread_->joinable()) send_thread_->join();
76  metricMan->do_stop();
77  }
78 
83  std::shared_ptr<RequestBuffer> GetRequestBuffer() { return request_buffer_ptr_; }
84 
85 private:
86  void receive_fragments()
87  {
88  TLOG(TLVL_DEBUG) << "Waiting for first fragment.";
89  artdaq::FragmentPtrs frags;
90 
91  bool active = true;
92 
93  while (active && running_)
94  {
95  auto loop_start = std::chrono::steady_clock::now();
96  TLOG(18) << "receive_fragments getNext start";
97  active = generator_ptr_->getNext(frags);
98  TLOG(18) << "receive_fragments getNext done (active=" << active << ")";
99  auto after_getnext = std::chrono::steady_clock::now();
100  // 08-May-2015, KAB & JCF: if the generator getNext() method returns false
101  // (which indicates that the data flow has stopped) *and* the reason that
102  // it has stopped is because there was an exception that wasn't handled by
103  // the experiment-specific FragmentGenerator class, we move to the
104  // InRunError state so that external observers (e.g. RunControl or
105  // DAQInterface) can see that there was a problem.
106  if (!active && generator_ptr_ && generator_ptr_->exception())
107  {
108  TLOG(TLVL_ERROR) << "Generator has an exception, aborting!";
109  break;
110  }
111 
112  if (!active) { break; }
113 
114  if (frags.size() > 0)
115  {
116  metricMan->sendMetric("Fragments Generated", frags.size(), "fragments", 3, artdaq::MetricMode::Accumulate | artdaq::MetricMode::Rate | artdaq::MetricMode::Average);
117 
118  TLOG(18) << "receive_fragments AddFragmentsToBuffer start";
119  fragment_buffer_ptr_->AddFragmentsToBuffer(std::move(frags));
120  TLOG(18) << "receive_fragments AddFragmentsToBuffer done";
121  auto after_addFragsToBuffer = std::chrono::steady_clock::now();
122  metricMan->sendMetric("FragmentBufferAddTime", artdaq::TimeUtils::GetElapsedTime(after_getnext, after_addFragsToBuffer), "s", 3, artdaq::MetricMode::Accumulate | artdaq::MetricMode::Average | artdaq::MetricMode::Minimum | artdaq::MetricMode::Maximum);
123  }
124  metricMan->sendMetric("GetNextTime", artdaq::TimeUtils::GetElapsedTime(loop_start, after_getnext), "s", 3, artdaq::MetricMode::Accumulate | artdaq::MetricMode::Average | artdaq::MetricMode::Minimum | artdaq::MetricMode::Maximum);
125 
126  frags.clear();
127  }
128  TLOG(TLVL_DEBUG) << "receive_fragments loop end";
129  }
130 
131  void send_fragments()
132  {
133  TLOG(TLVL_DEBUG) << "Waiting for first fragment.";
134  artdaq::FragmentPtrs frags;
135 
136  bool active = true;
137 
138  while (active && running_)
139  {
140  auto loop_start = std::chrono::steady_clock::now();
141 
142  TLOG(18) << "send_fragments applyRequests start";
143  active = fragment_buffer_ptr_->applyRequests(frags);
144  TLOG(18) << "send_fragments applyRequests done (active=" << active << ")";
145 
146  auto after_requests = std::chrono::steady_clock::now();
147  if (!active) { break; }
148 
149  for (auto& fragPtr : frags)
150  {
151  if (!fragPtr.get())
152  {
153  TLOG(TLVL_WARNING) << "Encountered a bad fragment pointer in fragment " << fragment_count_ << ". "
154  << "This is most likely caused by a problem with the Fragment Generator!";
155  continue;
156  }
157  if (fragment_count_ == 0)
158  {
159  TLOG(TLVL_DEBUG) << "Received first Fragment from Fragment Generator, sequence ID " << fragPtr->sequenceID() << ", size = " << fragPtr->sizeBytes() << " bytes.";
160  }
161  artdaq::Fragment::sequence_id_t sequence_id = fragPtr->sequenceID();
162  SetMFIteration("Sequence ID " + std::to_string(sequence_id));
163 
164  TLOG(17) << "send_fragments seq=" << sequence_id << " sendFragment start";
165  ++fragment_count_;
166 
167  // Turn on lvls (mem and/or slow) 3,13,14 to log every send.
168  TLOG(((fragment_count_ == 1) ? TLVL_DEBUG
169  : (((fragment_count_ % 250) == 0) ? 13 : 14)))
170  << ((fragment_count_ == 1)
171  ? "Sent first Fragment"
172  : "Sending fragment " + std::to_string(fragment_count_))
173  << " with SeqID " << sequence_id << ".";
174  }
175 
176  metricMan->sendMetric("Fragments Discarded", frags.size(), "fragments", 3, artdaq::MetricMode::Accumulate | artdaq::MetricMode::Rate | artdaq::MetricMode::Average);
177  frags.clear();
178  auto after_frag_check = std::chrono::steady_clock::now();
179  metricMan->sendMetric("ApplyRequestsTime", artdaq::TimeUtils::GetElapsedTime(loop_start, after_requests), "s", 3, artdaq::MetricMode::Average | artdaq::MetricMode::Accumulate | artdaq::MetricMode::Maximum | artdaq::MetricMode::Minimum);
180  metricMan->sendMetric("FragmentDiscardTime", artdaq::TimeUtils::GetElapsedTime(after_requests, after_frag_check), "s", 3, artdaq::MetricMode::Average | artdaq::MetricMode::Accumulate);
181 
182  std::this_thread::yield();
183  }
184 
185  // 11-May-2015, KAB: call MetricManager::do_stop whenever we exit the
186  // processing fragments loop so that metrics correctly go to zero when
187  // there is no data flowing
188  metricMan->do_stop();
189 
190  TLOG(TLVL_DEBUG) << "send_fragments loop end";
191  }
192 
193 private:
194  std::unique_ptr<CommandableFragmentGenerator> generator_ptr_;
195  std::unique_ptr<FragmentBuffer> fragment_buffer_ptr_;
196  std::shared_ptr<RequestBuffer> request_buffer_ptr_;
197  std::atomic<size_t> fragment_count_;
198  std::atomic<bool> running_;
199  std::unique_ptr<boost::thread> receive_thread_;
200  std::unique_ptr<boost::thread> send_thread_;
201 };
202 } // namespace artdaq
203 
204 int main(int argc, char* argv[])
205 {
206  artdaq::configureMessageFacility("RequestSender");
207 
208  struct FragmentReceiverConfig
209  {
210  fhicl::TableFragment<artdaq::CommandableFragmentGenerator::Config> generatorConfig;
211  fhicl::TableFragment<artdaq::FragmentBuffer::Config> fragmentBufferConfig;
212  };
213 
214  struct DAQConfig
215  {
216  fhicl::Table<FragmentReceiverConfig> frConfig{fhicl::Name{"fragment_receiver"}};
217  };
218 
219  struct Config
220  {
221  fhicl::Table<DAQConfig> daq{fhicl::Name{"daq"}};
222  fhicl::Table<artdaq::MetricManager::Config> metrics{fhicl::Name{"metrics"}};
223  fhicl::Atom<double> test_duration_s{fhicl::Name{"test_duration_s"}, fhicl::Comment{"Duration, in seconds, for the test"}, 60.0};
224  fhicl::Atom<size_t> time_between_requests_us{fhicl::Name{"time_between_requests_us"}, fhicl::Comment{"Amount of time to wait between generated requests, in us"}, 1000};
225  fhicl::Atom<artdaq::Fragment::timestamp_t> timestamp_increment{fhicl::Name{"timestamp_increment"}, fhicl::Comment{"Amount to increment the timestamp for each request"}, 1};
226  fhicl::Atom<int> run_number{fhicl::Name{"run_number"}, fhicl::Comment{"Run Number to use for the test"}, 101};
227  };
228 
229  auto pset = LoadParameterSet<Config>(argc, argv, "GenToBuffer", "This test application evaluates the rate of Fragment Generation and Request Application.");
230  auto fr_pset = pset;
231 
232  metricMan->initialize(pset.get<fhicl::ParameterSet>("metrics", fhicl::ParameterSet()), "GenToBuffer");
233 
234  if (pset.has_key("daq"))
235  {
236  fr_pset = pset.get<fhicl::ParameterSet>("daq");
237  }
238 
239  if (fr_pset.has_key("fragment_receiver"))
240  {
241  fr_pset = fr_pset.get<fhicl::ParameterSet>("fragment_receiver");
242  }
243  artdaq::GenToBufferTest gtbt(fr_pset);
244 
245  auto buf = gtbt.GetRequestBuffer();
246 
247  auto start_time = std::chrono::steady_clock::now();
248  auto duration = pset.get<double>("test_duration_s", 60);
249  artdaq::Fragment::sequence_id_t seq = 0;
250  artdaq::Fragment::timestamp_t timestamp = 1;
251  auto time_between_requests_us = pset.get<size_t>("time_between_requests_us", 1000);
252  auto timestamp_scale = pset.get<artdaq::Fragment::timestamp_t>("timestamp_increment", 1);
253 
254  gtbt.start(pset.get<int>("run_number", 101));
255 
256  while (artdaq::TimeUtils::GetElapsedTime(start_time) < duration)
257  {
258  buf->push(++seq, timestamp);
259  timestamp += timestamp_scale;
260 
261  auto us_since_start = artdaq::TimeUtils::GetElapsedTimeMicroseconds(start_time);
262  int64_t time_diff = seq * time_between_requests_us - us_since_start;
263  TLOG(40) << "Time Diff: " << time_diff << ", Time since start: " << us_since_start << ", current epoch: " << seq * time_between_requests_us;
264  if (time_diff > 10)
265  {
266  usleep(time_diff);
267  }
268  }
269 
270  gtbt.stop();
271 }
Test fixture for GenToBuffer_t.
std::shared_ptr< RequestBuffer > GetRequestBuffer()
Get a handle to the RequestBuffer.
Configuration for simple_metric_sender.
void start(int run_number)
Start the test fixture.
std::unique_ptr< CommandableFragmentGenerator > makeCommandableFragmentGenerator(std::string const &generator_plugin_spec, fhicl::ParameterSet const &ps)
Load a CommandableFragmentGenerator plugin.
FragmentBuffer is a FragmentGenerator-derived abstract class that defines the interface for a Fragmen...
Holds requests from RequestReceiver while they are being processed.
void stop()
Stop the test fixture.
GenToBufferTest(fhicl::ParameterSet const &ps)
GenToBufferTest constructor.