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