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