artdaq  v3_12_02
driver.cc
1 //
2 // artdaqDriver is a program for testing the behavior of the generic
3 // RawInput source. Run 'artdaqDriver --help' to get a description of the
4 // expected command-line parameters.
5 //
6 //
7 // The current version generates simple data fragments, for testing
8 // that data are transmitted without corruption from the
9 // artdaq::Eventevent_manager through to the artdaq::RawInput source.
10 //
11 #include "TRACE/tracemf.h"
12 #define TRACE_NAME "artdaqDriver"
13 #include "artdaq-core/Data/Fragment.hh"
14 
15 #include "artdaq-core/Plugins/FragmentGenerator.hh"
16 #include "artdaq-core/Plugins/makeFragmentGenerator.hh"
17 #include "artdaq-core/Utilities/ExceptionHandler.hh"
18 #include "artdaq-utilities/Plugins/MetricManager.hh"
19 #include "artdaq/Application/LoadParameterSet.hh"
20 #include "artdaq/ArtModules/detail/ArtConfig.hh"
21 #include "artdaq/DAQdata/GenericFragmentSimulator.hh"
22 #include "artdaq/DAQdata/Globals.hh"
23 #include "artdaq/DAQrate/SharedMemoryEventManager.hh"
24 #include "artdaq/Generators/makeCommandableFragmentGenerator.hh"
25 
26 #include "fhiclcpp/ParameterSet.h"
27 
28 #include <boost/program_options.hpp>
29 
30 #include <csignal>
31 #include <iostream>
32 #include <memory>
33 #include <utility>
34 
35 volatile int events_to_generate;
36 void sig_handler(int /*unused*/) { events_to_generate = -1; }
37 
38 template<typename B, typename D>
39 std::unique_ptr<D>
40 dynamic_unique_ptr_cast(std::unique_ptr<B>& p);
41 
42 int main(int argc, char* argv[])
43 try
44 {
45  struct Config
46  {
47  fhicl::Atom<int> run_number{fhicl::Name{"run_number"}, fhicl::Comment{"Run number to use for output file"}, 1};
48  fhicl::Atom<bool> debug_cout{fhicl::Name{"debug_cout"}, fhicl::Comment{"Whether to print debug messages to console"}, false};
49  fhicl::Atom<uint64_t> transition_timeout{fhicl::Name{"transition_timeout"}, fhicl::Comment{"Timeout to use (in seconds) for automatic transitions"}, 30};
50  fhicl::Table<artdaq::CommandableFragmentGenerator::Config> generator{fhicl::Name{"fragment_receiver"}};
51  fhicl::Table<artdaq::MetricManager::Config> metrics{fhicl::Name{"metrics"}};
52  fhicl::Table<artdaq::SharedMemoryEventManager::Config> event_builder{fhicl::Name{"event_builder"}};
53  fhicl::Atom<int> events_to_generate{fhicl::Name{"events_to_generate"}, fhicl::Comment{"Number of events to generate and process"}, 0};
54  fhicl::TableFragment<art::Config> art_config;
55  };
56  auto pset = LoadParameterSet<Config>(argc, argv, "driver", "The artdaqDriver executable runs a Fragment Generator and an art process, acting as a \"unit integration\" test for a data source");
57 
58  int run = pset.get<int>("run_number", 1);
59  bool debug = pset.get<bool>("debug_cout", false);
60  auto timeout = pset.get<uint64_t>("transition_timeout", 30);
61  uint64_t timestamp = 0;
62 
63  app_name = "artdaqDriver";
64 
65  artdaq::configureMessageFacility(app_name.c_str(), true, debug);
66 
67  auto fragment_receiver_pset = pset.get<fhicl::ParameterSet>("fragment_receiver");
68 
69  std::unique_ptr<artdaq::FragmentGenerator>
70  gen(artdaq::makeFragmentGenerator(fragment_receiver_pset.get<std::string>("generator"),
71  fragment_receiver_pset));
72 
73  std::unique_ptr<artdaq::CommandableFragmentGenerator> commandable_gen =
74  dynamic_unique_ptr_cast<artdaq::FragmentGenerator, artdaq::CommandableFragmentGenerator>(gen);
75 
76  my_rank = 0;
77  // pull out the Metric part of the ParameterSet
78  fhicl::ParameterSet metric_pset;
79  try
80  {
81  metric_pset = pset.get<fhicl::ParameterSet>("metrics");
82  }
83  catch (...)
84  {} // OK if there's no metrics table defined in the FHiCL
85 
86  if (metric_pset.is_empty())
87  {
88  TLOG(TLVL_INFO) << "No metric plugins appear to be defined";
89  }
90  try
91  {
92  metricMan->initialize(metric_pset, app_name);
93  metricMan->do_start();
94  }
95  catch (...)
96  {
97  }
98  artdaq::FragmentPtrs frags;
100  // Note: we are constrained to doing all this here rather than
101  // encapsulated neatly in a function due to the lifetime issues
102  // associated with async threads and std::string::c_str().
103  auto event_builder_pset = pset.get<fhicl::ParameterSet>("event_builder");
104  auto art_pset = pset.get<fhicl::ParameterSet>("art", pset);
105 
106  artdaq::SharedMemoryEventManager event_manager(event_builder_pset, art_pset);
108 
109  int events_to_generate = pset.get<int>("events_to_generate", 0);
110  int event_count = 0;
111  artdaq::Fragment::sequence_id_t previous_sequence_id = -1;
112 
113  if (commandable_gen)
114  {
115  commandable_gen->StartCmd(run, timeout, timestamp);
116  }
117 
118  TLOG(50) << "driver main before event_manager.startRun";
119  event_manager.startRun(run);
120 
121  // Read or generate fragments as rapidly as possible, and feed them
122  // into the Eventevent_manager. The throughput resulting from this design
123  // choice is likely to have the fragment reading (or generation)
124  // speed as the limiting factor
125  while ((commandable_gen && commandable_gen->getNext(frags)) ||
126  (gen && gen->getNext(frags)))
127  {
128  TLOG(50) << "driver main: getNext returned frags.size()=" << frags.size() << " current event_count=" << event_count;
129  for (auto& val : frags)
130  {
131  if (val->sequenceID() != previous_sequence_id)
132  {
133  ++event_count;
134  previous_sequence_id = val->sequenceID();
135  }
136  if (events_to_generate != 0 && event_count > events_to_generate)
137  {
138  if (commandable_gen)
139  {
140  commandable_gen->StopCmd(timeout, timestamp);
141  }
142  break;
143  }
144 
145  auto start_time = std::chrono::steady_clock::now();
146  bool sts = false;
147  auto loop_count = 0;
148  while (!sts)
149  {
150  artdaq::FragmentPtr tempFrag;
151  sts = event_manager.AddFragment(std::move(val), 1000000, tempFrag);
152  if (!sts && event_count <= 10 && loop_count > 100)
153  {
154  TLOG(TLVL_ERROR) << "Fragment was not added after " << artdaq::TimeUtils::GetElapsedTime(start_time) << " s. Check art thread status!";
155  event_manager.endOfData();
156  exit(1);
157  }
158  val = std::move(tempFrag);
159  if (!sts)
160  {
161  loop_count++;
162  // usleep(10000);
163  }
164  }
165  }
166  frags.clear();
167 
168  if (events_to_generate != 0 && event_count >= events_to_generate)
169  {
170  if (commandable_gen)
171  {
172  commandable_gen->StopCmd(timeout, timestamp);
173  }
174  break;
175  }
176  }
177 
178  if (commandable_gen)
179  {
180  commandable_gen->joinThreads();
181  }
182 
183 #if 0
184  volatile bool keep_looping = true;
185  while (keep_looping)
186  {
187  usleep(10000);
188  }
189 #endif
190 
191  TLOG(TLVL_INFO) << "Fragments generated, waiting for art to process them.";
192  auto art_wait_start_time = std::chrono::steady_clock::now();
193  auto last_delta_time = std::chrono::steady_clock::now();
194  auto last_count = event_manager.size() - event_manager.WriteReadyCount(false);
195 
196  while (last_count > 0 && artdaq::TimeUtils::GetElapsedTime(last_delta_time) < 1.0)
197  {
198  auto this_count = event_manager.size() - event_manager.WriteReadyCount(false);
199  if (this_count != last_count)
200  {
201  last_delta_time = std::chrono::steady_clock::now();
202  last_count = this_count;
203  }
204  usleep(1000);
205  }
206 
207  TLOG(TLVL_INFO) << "Ending Run, waited " << std::setprecision(2) << artdaq::TimeUtils::GetElapsedTime(art_wait_start_time) << " seconds for art to process events. (" << last_count << " buffers remain).";
208  event_manager.endRun();
209  usleep(artdaq::TimeUtils::GetElapsedTimeMicroseconds(art_wait_start_time)); // Wait as long again for EndRun message to go through
210 
211  TLOG(TLVL_INFO) << "Shutting down art";
212  bool endSucceeded = false;
213  int attemptsToEnd = 1;
214  endSucceeded = event_manager.endOfData();
215  while (!endSucceeded && attemptsToEnd < 3)
216  {
217  ++attemptsToEnd;
218  endSucceeded = event_manager.endOfData();
219  }
220  if (!endSucceeded)
221  {
222  TLOG(TLVL_ERROR) << "Failed to shut down the reader and the SharedMemoryEventManager "
223  << "because the endOfData marker could not be pushed "
224  << "onto the queue.";
225  }
226 
227  metricMan->do_stop();
228 
230  return 0;
231 }
232 catch (std::string& x)
233 {
234  std::cerr << "Exception (type string) caught in artdaqDriver: " << x << '\n';
235  return 1;
236 }
237 catch (char const* m)
238 {
239  std::cerr << "Exception (type char const*) caught in artdaqDriver: ";
240  if (m != nullptr)
241  {
242  std::cerr << m;
243  }
244  else
245  {
246  std::cerr << "[the value was a null pointer, so no message is available]";
247  }
248  std::cerr << '\n';
249 }
250 catch (...)
251 {
252  artdaq::ExceptionHandler(artdaq::ExceptionHandlerRethrow::no,
253  "Exception caught in artdaqDriver");
254  exit(-2);
255 }
256 
257 template<typename B, typename D>
258 std::unique_ptr<D>
259 dynamic_unique_ptr_cast(std::unique_ptr<B>& p)
260 {
261  D* result = dynamic_cast<D*>(p.release());
262 
263  if (result)
264  {
265  return std::unique_ptr<D>(result);
266  }
267  return nullptr;
268 }
The SharedMemoryEventManager is a SharedMemoryManger which tracks events as they are built...
static void CleanUpGlobals()
Clean up statically-allocated Manager class instances.
Definition: Globals.hh:156
void StopCmd(uint64_t timeout, uint64_t timestamp)
Stop the CommandableFragmentGenerator.
void StartCmd(int run, uint64_t timeout, uint64_t timestamp)
Start the CommandableFragmentGenerator.
bool getNext(FragmentPtrs &output) overridefinal
getNext calls either applyRequests or getNext_ to get any data that is ready to be sent to the EventB...
Configuration for simple_metric_sender.
CommandableFragmentGenerator is a FragmentGenerator-derived abstract class that defines the interface...
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.