1 #include "artdaq-demo/Generators/ToyHardwareInterface/ToyHardwareInterface.hh"
2 #define TRACE_NAME "ToyHardwareInterface"
3 #include "artdaq-core-demo/Overlays/FragmentType.hh"
4 #include "artdaq-core-demo/Overlays/ToyFragment.hh"
5 #include "artdaq/DAQdata/Globals.hh"
7 #include "cetlib_except/exception.h"
8 #include "fhiclcpp/ParameterSet.h"
24 , nADCcounts_(ps.get<size_t>(
"nADCcounts", 40))
25 , maxADCcounts_(ps.get<size_t>(
"maxADCcounts", 50000000))
26 , change_after_N_seconds_(ps.get<size_t>(
"change_after_N_seconds", std::numeric_limits<size_t>::max()))
27 , pause_after_N_seconds_(ps.get<size_t>(
"pause_after_N_seconds", 0))
28 , nADCcounts_after_N_seconds_(ps.get<size_t>(
"nADCcounts_after_N_seconds", nADCcounts_))
29 , exception_after_N_seconds_(ps.get<bool>(
"exception_after_N_seconds", false))
30 , exit_after_N_seconds_(ps.get<bool>(
"exit_after_N_seconds", false))
31 , abort_after_N_seconds_(ps.get<bool>(
"abort_after_N_seconds", false))
32 , hang_after_N_seconds_(ps.get<bool>(
"hang_after_N_seconds", false))
33 , fragment_type_(demo::toFragmentType(ps.get<std::string>(
"fragment_type")))
34 , maxADCvalue_(static_cast<size_t>(pow(2, NumADCBits()) - 1))
36 throttle_usecs_(ps.get<size_t>(
"throttle_usecs", 100000))
37 , usecs_between_sends_(ps.get<size_t>(
"usecs_between_sends", 0))
38 , distribution_type_(static_cast<
DistributionType>(ps.get<int>(
"distribution_type")))
39 , engine_(ps.get<int64_t>(
"random_seed", 314159))
40 , uniform_distn_(new std::uniform_int_distribution<
data_t>(0, maxADCvalue_))
41 , gaussian_distn_(new std::normal_distribution<double>(0.5 * maxADCvalue_, 0.1 * maxADCvalue_))
42 , start_time_(fake_time_)
44 , serial_number_((*uniform_distn_)(engine_))
46 if (nADCcounts_ > maxADCcounts_ ||
47 nADCcounts_after_N_seconds_ > maxADCcounts_)
49 throw cet::exception(
"HardwareInterface")
50 << R
"(Either (or both) of "nADCcounts" and "nADCcounts_after_N_seconds")"
51 << " is larger than the \"maxADCcounts\" setting (currently at " << maxADCcounts_ <<
")";
54 bool planned_disruption = nADCcounts_after_N_seconds_ != nADCcounts_ || exception_after_N_seconds_ ||
55 exit_after_N_seconds_ || abort_after_N_seconds_;
57 if (planned_disruption && change_after_N_seconds_ == std::numeric_limits<size_t>::max())
59 throw cet::exception(
"HardwareInterface") <<
"A FHiCL parameter designed to create a disruption has been "
60 "set, so \"change_after_N_seconds\" should be set as well";
79 start_time_ = fake_time_;
84 TLOG(TLVL_TRACE) <<
"FillBuffer BEGIN";
87 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Sleeping for " << throttle_usecs_ <<
" microseconds";
88 usleep(throttle_usecs_);
90 auto elapsed_secs_since_datataking_start = artdaq::TimeUtils::GetElapsedTime(start_time_);
91 if (elapsed_secs_since_datataking_start < 0) elapsed_secs_since_datataking_start = 0;
93 if (static_cast<size_t>(elapsed_secs_since_datataking_start) < change_after_N_seconds_ || send_calls_ == 0)
95 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Setting bytes_read to " <<
sizeof(demo::ToyFragment::Header) + nADCcounts_ *
sizeof(
data_t);
96 *bytes_read =
sizeof(demo::ToyFragment::Header) + nADCcounts_ *
sizeof(
data_t);
100 if (abort_after_N_seconds_)
102 TLOG(TLVL_ERROR) <<
"Engineered Abort!";
105 else if (exit_after_N_seconds_)
107 TLOG(TLVL_ERROR) <<
"Engineered Exit!";
110 else if (exception_after_N_seconds_)
112 TLOG(TLVL_ERROR) <<
"Engineered Exception!";
113 throw cet::exception(
"HardwareInterface")
114 <<
"This is an engineered exception designed for testing purposes";
116 else if (hang_after_N_seconds_)
118 TLOG(TLVL_ERROR) <<
"Pretending that the hardware has hung! Variable name for gdb: hardwareIsHung";
119 volatile bool hardwareIsHung =
true;
121 while (hardwareIsHung)
128 if ((pause_after_N_seconds_ != 0u) && (static_cast<size_t>(elapsed_secs_since_datataking_start) % change_after_N_seconds_ == 0))
130 TLOG(TLVL_DEBUG + 3) <<
"pausing " << pause_after_N_seconds_ <<
" seconds";
131 sleep(pause_after_N_seconds_);
132 TLOG(TLVL_DEBUG + 3) <<
"resuming after pause of " << pause_after_N_seconds_ <<
" seconds";
134 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Setting bytes_read to " <<
sizeof(demo::ToyFragment::Header) + nADCcounts_after_N_seconds_ *
sizeof(
data_t);
135 *bytes_read =
sizeof(demo::ToyFragment::Header) + nADCcounts_after_N_seconds_ *
sizeof(
data_t);
139 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Making the fake data, starting with the header";
145 assert(*bytes_read %
sizeof(demo::ToyFragment::Header::data_t) == 0);
147 auto* header =
reinterpret_cast<demo::ToyFragment::Header*
>(buffer);
149 header->event_size = *bytes_read /
sizeof(demo::ToyFragment::Header::data_t);
150 header->trigger_number = 99;
151 header->distribution_type =
static_cast<uint8_t
>(distribution_type_);
153 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Generating nADCcounts ADC values ranging from 0 to max based on the desired distribution";
155 std::function<data_t()> generator;
158 switch (distribution_type_)
161 generator = [&]() {
return static_cast<data_t>((*uniform_distn_)(engine_)); };
168 gen_seed =
static_cast<data_t>(std::round((*gaussian_distn_)(engine_)));
169 }
while (gen_seed > maxADCvalue_);
177 if (++gen_seed > maxADCvalue_)
187 case DistributionType::uninit2:
191 throw cet::exception(
"HardwareInterface") <<
"Unknown distribution type specified";
196 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Calling generate_n";
197 std::generate_n(reinterpret_cast<data_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
198 nADCcounts_, generator);
203 throw cet::exception(
"ToyHardwareInterface") <<
"Attempt to call FillBuffer when not sending data";
206 if (send_calls_ == 0)
208 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer has set the start_time_";
209 start_time_ = std::chrono::steady_clock::now();
212 if (usecs_between_sends_ != 0)
216 auto usecs_since_start = artdaq::TimeUtils::GetElapsedTimeMicroseconds(start_time_);
217 double delta =
static_cast<double>(usecs_between_sends_ * send_calls_) - usecs_since_start;
218 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer send_calls=" << send_calls_ <<
" usecs_since_start=" << usecs_since_start
219 <<
" delta=" << delta;
222 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Sleeping for " << delta <<
" microseconds";
228 TLOG(TLVL_TRACE) <<
"FillBuffer END";
233 *buffer =
reinterpret_cast<char*
>(
234 new uint8_t[
sizeof(demo::ToyFragment::Header) + maxADCcounts_ *
sizeof(
data_t)]);
244 return static_cast<int>(fragment_type_) + 1000;
249 switch (fragment_type_)
251 case demo::FragmentType::TOY1:
254 case demo::FragmentType::TOY2:
258 throw cet::exception(
"ToyHardwareInterface") <<
"Unknown board type " << fragment_type_ <<
" ("
259 << demo::fragmentTypeToString(fragment_type_) <<
").\n";
266 return serial_number_;
int NumADCBits() const
Get the number of ADC bits used in generating data.
void StartDatataking()
"StartDatataking" 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(const 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 "board type" of the simulated hardware.