artdaq_demo  3.13.00
ToySimulator_generator.cc
1 // For an explanation of this class, look at its header,
2 // ToySimulator.hh, as well as
3 // https://cdcvs.fnal.gov/redmine/projects/artdaq-demo/wiki/Fragments_and_FragmentGenerators_w_Toy_Fragments_as_Examples
4 
5 #include "artdaq-demo/Generators/ToySimulator.hh"
6 #include "artdaq/DAQdata/Globals.hh"
7 
8 #include "cetlib_except/exception.h"
9 #include "fhiclcpp/ParameterSet.h"
10 
11 #include "artdaq-core-demo/Overlays/FragmentType.hh"
12 #include "artdaq-core-demo/Overlays/ToyFragment.hh"
13 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
14 #include "artdaq/Generators/GeneratorMacros.hh"
15 
16 #define TRACE_NAME "ToySimulator"
17 #include "TRACE/tracemf.h" // TRACE, TLOG*
18 
19 #include <unistd.h>
20 #include <fstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <iterator>
24 
25 demo::ToySimulator::ToySimulator(fhicl::ParameterSet const& ps)
26  : CommandableFragmentGenerator(ps)
27  , hardware_interface_(new ToyHardwareInterface(ps))
28  , timestamp_(0)
29  , starting_timestamp_(0)
30  , timestampScale_(ps.get<int>("timestamp_scale_factor", 1))
31  , sequence_id_scale_(ps.get<size_t>("sequence_scale_factor", 1))
32  , initial_sequence_id_(ps.get<size_t>("initial_sequence_id", 1))
33  , rollover_subrun_interval_(ps.get<int>("rollover_subrun_interval", 0))
34  , metadata_({0, 0, 0})
35  , readout_buffer_(nullptr)
36  , fragment_type_(static_cast<decltype(fragment_type_)>(artdaq::Fragment::InvalidFragmentType))
37  , distribution_type_(static_cast<ToyHardwareInterface::DistributionType>(ps.get<int>("distribution_type")))
38  , fragment_group_size_(ps.get<int>("fragment_group_size", 1))
39  , fragment_group_timeout_(ps.get<int>("fragment_group_timeout_us", 1000000))
40  , exception_on_config_(ps.get<bool>("exception_on_config", false))
41  , dies_on_config_(ps.get<bool>("dies_on_config", false))
42  , lazy_mode_(ps.get<bool>("lazy_mode", false))
43 
44 {
45  hardware_interface_->AllocateReadoutBuffer(&readout_buffer_);
46 
47  auto ts = ps.get<int>("starting_timestamp", 0);
48  if (ts < 0) { starting_timestamp_ = artdaq::Fragment::InvalidTimestamp; }
49  else
50  {
51  starting_timestamp_ = static_cast<artdaq::Fragment::timestamp_t>(ts);
52  }
53  timestamp_ = starting_timestamp_;
54 
55  if (exception_on_config_)
56  {
57  throw cet::exception("ToySimulator") << "This is an engineered exception designed for testing purposes, set " // NOLINT(cert-err60-cpp)
58  "by the exception_on_config FHiCL variable";
59  }
60  if (dies_on_config_)
61  {
62  TLOG(TLVL_ERROR) << "This is an engineered process death, set by the dies_on_config FHiCL variable";
63  std::exit(1);
64  }
65 
66  metadata_.board_serial_number = hardware_interface_->SerialNumber() & 0xFFFF;
67  metadata_.num_adc_bits = hardware_interface_->NumADCBits();
68  TLOG(TLVL_INFO) << "Constructor: metadata_.unused = 0x" << std::hex << metadata_.unused
69  << " sizeof(metadata_) = " << std::dec << sizeof(metadata_);
70 
71  switch (hardware_interface_->BoardType())
72  {
73  case 1002:
74  fragment_type_ = toFragmentType("TOY1");
75  break;
76  case 1003:
77  fragment_type_ = toFragmentType("TOY2");
78  break;
79  default:
80  throw cet::exception("ToySimulator") << "Unable to determine board type supplied by hardware"; // NOLINT(cert-err60-cpp)
81  }
82 }
83 
84 demo::ToySimulator::~ToySimulator() { hardware_interface_->FreeReadoutBuffer(readout_buffer_); }
85 
86 bool demo::ToySimulator::getNext_(artdaq::FragmentPtrs& frags)
87 {
88  if (should_stop())
89  {
90  return false;
91  }
92 
93  auto start = std::chrono::steady_clock::now();
94 
95  // ToyHardwareInterface (an instance to which "hardware_interface_"
96  // is a unique_ptr object) is just one example of the sort of
97  // interface a hardware library might offer. For example, other
98  // interfaces might require you to allocate and free the memory used
99  // to store hardware data in your generator using standard C++ tools
100  // (rather than via the "AllocateReadoutBuffer" and
101  // "FreeReadoutBuffer" functions provided here), or could have a
102  // function which directly returns a pointer to the data buffer
103  // rather than sticking the data in the location pointed to by your
104  // pointer (which is what happens here with readout_buffer_)
105 
106  while (frags.size() < fragment_group_size_ * fragmentIDs().size() && std::chrono::steady_clock::now() - start < fragment_group_timeout_)
107  {
108  // 15-Nov-2019, KAB, JCF: added handling of the 'lazy' mode.
109  // In this context, "lazy" is intended to mean "only generate data when
110  // it is requested". With this code, we return before doing the work
111  // of filling the buffer (lazy!), and we overwrite whatever local
112  // calculation of the timestamp has been done with the very specific
113  // timestamp that is contained in the request. We could also capture
114  // the sequence_id from the request and use it when creating the
115  // artdaq::Fragment, but that isn't strictly necessary since the sequence_ids
116  // of pull-mode fragments get overwritten when they are matched to requests
117  // in CommandableFragmentGenerator.
118  // For completeness, we include tests of both the GetNextRequest and
119  // GetRequest methods (controlled by the LAZY_MODEL pre-processor variable).
120  if (lazy_mode_)
121  {
122 #define LAZY_MODEL 0
123 #if LAZY_MODEL == 0
124 
125  auto requests = GetRequestBuffer();
126  if (requests == nullptr)
127  {
128  throw cet::exception("ToySimulator") << "Lazy mode is enabled, but the RequestBuffer is nullptr";
129  }
130 
131  auto request = requests->GetNextRequest();
132  if (request.first == 0)
133  {
134  usleep(10);
135  return true;
136  }
137 
138  timestamp_ = request.second;
139  TLOG(51) << "Received a request for the fragment with timestamp " << timestamp_
140  << " and sequenceId " << request.first << ". Proceeding to fill the fragment buffer, etc.";
141 #else
142  auto requests = GetRequests();
143  auto request_iterator = requests.begin();
144  std::pair<artdaq::Fragment::sequence_id_t, artdaq::Fragment::timestamp_t> new_request(0, 0);
145  TLOG(52) << "Looping through " << requests.size() << " requests to see if there is a new one.";
146  while (request_iterator != requests.end())
147  {
148  if (lazily_handled_requests_.find(request_iterator->first) == lazily_handled_requests_.end())
149  {
150  lazily_handled_requests_.insert(request_iterator->first);
151  new_request = *request_iterator;
152  break;
153  }
154  ++request_iterator;
155  }
156  if (new_request.first == 0)
157  {
158  usleep(10);
159  return true;
160  }
161  timestamp_ = new_request.second;
162  TLOG(51) << "Found a new request for the fragment with timestamp " << timestamp_
163  << " and sequenceId " << new_request.first << ". Proceeding to fill the fragment buffer, etc.";
164 #endif
165  }
166 
167  TLOG(TLVL_DEBUG + 3) << "getNext_: Calling ToyHardwareInterface::FillBuffer";
168  std::size_t bytes_read = 0;
169  hardware_interface_->FillBuffer(readout_buffer_, &bytes_read);
170  TLOG(TLVL_DEBUG + 3) << "getNext_: Done with FillBuffer";
171 
172  // We'll use the static factory function
173 
174  // artdaq::Fragment::FragmentBytes(std::size_t payload_size_in_bytes, sequence_id_t sequence_id,
175  // fragment_id_t fragment_id, type_t type, const T & metadata)
176 
177  // which will then return a unique_ptr to an artdaq::Fragment
178  // object.
179 
180  TLOG(TLVL_DEBUG + 3) << "getNext_: Creating Fragments for configured Fragment IDs";
181  for (auto& id : fragmentIDs())
182  {
183  // The offset logic below is designed to both ensure
184  // backwards compatibility and to (help) avoid collisions
185  // with fragment_ids from other boardreaders if more than
186  // one fragment is generated per event
187 
188  std::unique_ptr<artdaq::Fragment> fragptr(
189  artdaq::Fragment::FragmentBytes(bytes_read, ev_counter(), id, fragment_type_, metadata_, timestamp_));
190  frags.emplace_back(std::move(fragptr));
191 
192  TLOG(TLVL_DEBUG + 4) << "getNext_: Before memcpy";
194  {
195  memcpy(frags.back()->dataBeginBytes(), readout_buffer_, bytes_read);
196  }
197  else
198  {
199  // Must preserve the Header!
200  memcpy(frags.back()->dataBeginBytes(), readout_buffer_, sizeof(ToyFragment::Header));
201  }
202 
203  TLOG(TLVL_DEBUG + 4) << "getNext_ after memcpy " << bytes_read
204  << " bytes and std::move dataSizeBytes()=" << frags.back()->sizeBytes()
205  << " metabytes=" << sizeof(metadata_);
206  }
207 
208  if (metricMan != nullptr)
209  {
210  metricMan->sendMetric("Fragments Sent", ev_counter(), "Events", 3, artdaq::MetricMode::LastPoint);
211  }
212 
213  TLOG(TLVL_DEBUG + 3) << "getNext_: Checking for subrun rollover";
214  if (rollover_subrun_interval_ > 0 && ev_counter() % rollover_subrun_interval_ == 0 && fragment_id() == 0)
215  {
216  bool fragmentIdZero = false;
217  for (auto& id : fragmentIDs())
218  {
219  if (id == 0)
220  {
221  fragmentIdZero = true;
222  }
223  }
224  if (fragmentIdZero)
225  {
226  artdaq::FragmentPtr endOfSubrunFrag(new artdaq::Fragment(static_cast<size_t>(
227  ceil(sizeof(my_rank) / static_cast<double>(sizeof(artdaq::Fragment::value_type))))));
228  endOfSubrunFrag->setSystemType(artdaq::Fragment::EndOfSubrunFragmentType);
229 
230  endOfSubrunFrag->setSequenceID(ev_counter() + 1);
231  endOfSubrunFrag->setTimestamp(1 + (ev_counter() / rollover_subrun_interval_));
232 
233  *endOfSubrunFrag->dataBegin() = my_rank;
234  frags.emplace_back(std::move(endOfSubrunFrag));
235  }
236  }
237 
238  ev_counter_inc(sequence_id_scale_);
239  timestamp_ += timestampScale_;
240  }
241  TLOG(TLVL_DEBUG + 3) << "getNext_: DONE";
242  return true;
243 }
244 
245 void demo::ToySimulator::start()
246 {
247  hardware_interface_->StartDatataking();
248  while (ev_counter() < initial_sequence_id_)
249  {
250  ev_counter_inc();
251  }
252  timestamp_ = starting_timestamp_;
253  lazily_handled_requests_.clear();
254 }
255 
256 void demo::ToySimulator::stop() { hardware_interface_->StopDatataking(); }
257 
258 // The following macro is defined in artdaq's GeneratorMacros.hh header
259 DEFINE_ARTDAQ_COMMANDABLE_GENERATOR(demo::ToySimulator)
A use-after-free expliot distribution.
ToySimulator(fhicl::ParameterSet const &ps)
ToySimulator Constructor.
JCF, Mar-17-2016: ToyHardwareInterface is meant to mimic a vendor-provided hardware API...
virtual ~ToySimulator()
Shutdown the ToySimulator.
ToySimulator is a simple type of fragment generator intended to be studied by new users of artdaq as ...
Definition: ToySimulator.hh:35