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