artdaq_demo  v2_10_00
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
ToyHardwareInterface.cc
1 #include "artdaq-demo/Generators/ToyHardwareInterface/ToyHardwareInterface.hh"
2 #include "artdaq/DAQdata/Globals.hh"
3 #include "artdaq-core-demo/Overlays/ToyFragment.hh"
4 #include "artdaq-core-demo/Overlays/FragmentType.hh"
5 
6 #include "fhiclcpp/ParameterSet.h"
7 #include "cetlib/exception.h"
8 
9 #include <random>
10 #include <unistd.h>
11 #include <iostream>
12 #include <cstdlib>
13 
14 // JCF, Mar-17-2016
15 
16 // ToyHardwareInterface is meant to mimic a vendor-provided hardware
17 // API, usable within the the ToySimulator fragment generator. For
18 // purposes of realism, it's a C++03-style API, as opposed to, say, one
19 // based in C++11 capable of taking advantage of smart pointers, etc.
20 
21 ToyHardwareInterface::ToyHardwareInterface(fhicl::ParameterSet const& ps) :
22  taking_data_(false)
23  , nADCcounts_(ps.get<size_t>("nADCcounts", 40))
24  , maxADCcounts_(ps.get<size_t>("maxADCcounts", 50000000))
25  , change_after_N_seconds_(ps.get<size_t>("change_after_N_seconds",
26  std::numeric_limits<size_t>::max()))
27  , nADCcounts_after_N_seconds_(ps.get<int>("nADCcounts_after_N_seconds",
28  nADCcounts_))
29  , exception_after_N_seconds_(ps.get<bool>("exception_after_N_seconds",
30  false))
31  , exit_after_N_seconds_(ps.get<bool>("exit_after_N_seconds",
32  false))
33  , abort_after_N_seconds_(ps.get<bool>("abort_after_N_seconds",
34  false))
35  , fragment_type_(demo::toFragmentType(ps.get<std::string>("fragment_type")))
36  , maxADCvalue_(pow(2, NumADCBits()) - 1)
37  , // MUST be after "fragment_type"
38  throttle_usecs_(ps.get<size_t>("throttle_usecs", 100000))
39  , usecs_between_sends_(ps.get<size_t>("usecs_between_sends", 0))
40  , distribution_type_(static_cast<DistributionType>(ps.get<int>("distribution_type")))
41  , engine_(ps.get<int64_t>("random_seed", 314159))
42  , uniform_distn_(new std::uniform_int_distribution<data_t>(0, maxADCvalue_))
43  , gaussian_distn_(new std::normal_distribution<double>(0.5 * maxADCvalue_, 0.1 * maxADCvalue_))
44  , start_time_(fake_time_)
45  , send_calls_(0)
46  , serial_number_((*uniform_distn_)(engine_))
47 {
48 #pragma GCC diagnostic push
49 #pragma GCC diagnostic ignored "-Wsign-compare"
50 
51  // JCF, Aug-14-2016
52 
53  // The logic of checking that nADCcounts_after_N_seconds_ >= 0,
54  // below, is because signed vs. unsigned comparison won't do what
55  // you want it to do if nADCcounts_after_N_seconds_ is negative
56 
57  if (nADCcounts_ > maxADCcounts_ ||
58  (nADCcounts_after_N_seconds_ >= 0 && nADCcounts_after_N_seconds_ > maxADCcounts_))
59  {
60  throw cet::exception("HardwareInterface") << "Either (or both) of \"nADCcounts\" and \"nADCcounts_after_N_seconds\"" <<
61  " is larger than the \"maxADCcounts\" setting (currently at " << maxADCcounts_ << ")";
62  }
63 
64  bool planned_disruption = nADCcounts_after_N_seconds_ != nADCcounts_ ||
65  exception_after_N_seconds_ ||
66  exit_after_N_seconds_ ||
67  abort_after_N_seconds_;
68 
69  if (planned_disruption &&
70  change_after_N_seconds_ == std::numeric_limits<size_t>::max())
71  {
72  throw cet::exception("HardwareInterface") << "A FHiCL parameter designed to create a disruption has been set, so \"change_after_N_seconds\" should be set as well";
73 #pragma GCC diagnostic pop
74  }
75 }
76 
77 // JCF, Mar-18-2017
78 
79 // "StartDatataking" is meant to mimic actions one would take when
80 // telling the hardware to start sending data - the uploading of
81 // values to registers, etc.
82 
84 {
85  taking_data_ = true;
86  send_calls_ = 0;
87 }
88 
90 {
91  taking_data_ = false;
92  start_time_ = fake_time_;
93 }
94 
95 
96 void ToyHardwareInterface::FillBuffer(char* buffer, size_t* bytes_read)
97 {
98  if (taking_data_)
99  {
100  usleep(throttle_usecs_);
101 
102  auto elapsed_secs_since_datataking_start =
103  std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now()
104  - start_time_).count();
105 
106 #pragma GCC diagnostic push
107 #pragma GCC diagnostic ignored "-Wsign-compare"
108  if (elapsed_secs_since_datataking_start < change_after_N_seconds_)
109  {
110 #pragma GCC diagnostic pop
111 
112  *bytes_read = sizeof(demo::ToyFragment::Header) + nADCcounts_ * sizeof(data_t);
113  }
114  else
115  {
116  if (abort_after_N_seconds_)
117  {
118  std::abort();
119  }
120  else if (exit_after_N_seconds_)
121  {
122  std::exit(1);
123  }
124  else if (exception_after_N_seconds_)
125  {
126  throw cet::exception("HardwareInterface") << "This is an engineered exception designed for testing purposes";
127  }
128  else if (nADCcounts_after_N_seconds_ >= 0)
129  {
130  *bytes_read = sizeof(demo::ToyFragment::Header) + nADCcounts_after_N_seconds_ * sizeof(data_t);
131  }
132  else
133  {
134  // Pretend the hardware hangs
135  while (true) {}
136  }
137  }
138 
139  // Make the fake data, starting with the header
140 
141  // Can't handle a fragment whose size isn't evenly divisible by
142  // the demo::ToyFragment::Header::data_t type size in bytes
143  //std::cout << "Bytes to read: " << *bytes_read << ", sizeof(data_t): " << sizeof(demo::ToyFragment::Header::data_t) << std::endl;
144  assert(*bytes_read % sizeof(demo::ToyFragment::Header::data_t) == 0);
145 
146  demo::ToyFragment::Header* header = reinterpret_cast<demo::ToyFragment::Header*>(buffer);
147 
148  header->event_size = *bytes_read / sizeof(demo::ToyFragment::Header::data_t);
149  header->trigger_number = 99;
150 
151  // Generate nADCcounts ADC values ranging from 0 to max based on
152  // the desired distribution
153 
154  std::function<data_t()> generator;
155  data_t gen_seed = 0;
156 
157  switch (distribution_type_)
158  {
160  generator = [&]()
161  {
162  return static_cast<data_t>
163  ((*uniform_distn_)(engine_));
164  };
165  break;
166 
168  generator = [&]()
169  {
170  do
171  {
172  gen_seed = static_cast<data_t>(std::round((*gaussian_distn_)(engine_)));
173  } while (gen_seed > maxADCvalue_);
174  return gen_seed;
175  };
176  break;
177 
179  {
180  generator = [&]()
181  {
182  if (++gen_seed > maxADCvalue_) gen_seed = 0;
183  return gen_seed;
184  };
185  }
186  break;
187 
189  case DistributionType::uninit2:
190  break;
191 
192  default:
193  throw cet::exception("HardwareInterface") <<
194  "Unknown distribution type specified";
195  }
196 
197  if (distribution_type_ != DistributionType::uninitialized && distribution_type_ != DistributionType::uninit2)
198  {
199  std::generate_n(reinterpret_cast<data_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
200  nADCcounts_,
201  generator
202  );
203  }
204  }
205  else
206  {
207  throw cet::exception("ToyHardwareInterface") <<
208  "Attempt to call FillBuffer when not sending data";
209  }
210 
211  if (usecs_between_sends_ != 0)
212  {
213  if (send_calls_ == 0)
214  { start_time_ = std::chrono::high_resolution_clock::now();
215  TRACE( 50, "ToyHardwareInterface::FillBuffer has set the start_time_" );
216  }
217  else
218  {
219 
220 #pragma GCC diagnostic push
221 #pragma GCC diagnostic ignored "-Wsign-compare"
222 
223  auto usecs_since_start =
224  std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()
225  - start_time_).count();
226  long delta = (long)(usecs_between_sends_ * send_calls_) - usecs_since_start;
227  if (delta > 0)
228  usleep(delta);
229 
230  TRACE(15, "ToyHardwareInterface::FillBuffer send_calls=%d usecs_since_start=%ld delta=%ld"
231  , send_calls_, usecs_since_start, delta);
232 
233 #pragma GCC diagnostic pop
234  }
235  }
236  ++send_calls_;
237 }
238 
240 {
241  *buffer = reinterpret_cast<char*>(new uint8_t[sizeof(demo::ToyFragment::Header) + maxADCcounts_ * sizeof(data_t)]);
242 }
243 
245 {
246  delete[] buffer;
247 }
248 
249 
251 {
252  // Pretend that the "BoardType" is some vendor-defined integer which
253  // differs from the fragment_type_ we want to use as developers (and
254  // which must be between 1 and 224, inclusive) so add an offset
255  return static_cast<int>(fragment_type_) + 1000;
256 }
257 
259 {
260  switch (fragment_type_)
261  {
262  case demo::FragmentType::TOY1:
263  return 12;
264  break;
265  case demo::FragmentType::TOY2:
266  return 14;
267  break;
268  default:
269  throw cet::exception("ToyHardwareInterface")
270  << "Unknown board type "
271  << fragment_type_
272  << " ("
273  << demo::fragmentTypeToString(fragment_type_)
274  << ").\n";
275  };
276 }
277 
279 {
280  // Serial number is generated from the uniform distribution on initialization of the class
281  return serial_number_;
282 }
int NumADCBits() const
Get the number of ADC bits used in generating data.
void StartDatataking()
&quot;StartDatataking&quot; is meant to mimic actions one would take when telling the hardware to start sending...
uint16_t data_t
The type used to represent ADC counts (which are 12 or 14 bits, for TOY1 or TOY2) ...
void StopDatataking()
Performs shutdown actions.
void FillBuffer(char *buffer, size_t *bytes_read)
Use configured generator to fill a buffer with data.
DistributionType
Allow for the selection of output distribution.
void FreeReadoutBuffer(char *buffer)
Release the given buffer to the hardware.
A monotonically-increasing distribution.
ToyHardwareInterface(fhicl::ParameterSet const &ps)
Construct and configure ToyHardwareInterface.
A use-after-free expliot distribution.
void AllocateReadoutBuffer(char **buffer)
Request a buffer from the hardware.
int SerialNumber() const
Gets the serial number of the simulated hardware.
int BoardType() const
Return the &quot;board type&quot; of the simulated hardware.