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_);
176 if (++gen_seed > maxADCvalue_)
186 case DistributionType::uninit2:
190 throw cet::exception(
"HardwareInterface") <<
"Unknown distribution type specified";
195 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Calling generate_n";
196 std::generate_n(reinterpret_cast<data_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
197 nADCcounts_, generator);
202 throw cet::exception(
"ToyHardwareInterface") <<
"Attempt to call FillBuffer when not sending data";
205 if (send_calls_ == 0)
207 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer has set the start_time_";
208 start_time_ = std::chrono::steady_clock::now();
211 if (usecs_between_sends_ != 0)
215 auto usecs_since_start = artdaq::TimeUtils::GetElapsedTimeMicroseconds(start_time_);
216 double delta =
static_cast<double>(usecs_between_sends_ * send_calls_) - usecs_since_start;
217 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer send_calls=" << send_calls_ <<
" usecs_since_start=" << usecs_since_start
218 <<
" delta=" << delta;
221 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Sleeping for " << delta <<
" microseconds";
227 TLOG(TLVL_TRACE) <<
"FillBuffer END";
232 *buffer =
reinterpret_cast<char*
>(
233 new uint8_t[
sizeof(demo::ToyFragment::Header) + maxADCcounts_ *
sizeof(
data_t)]);
243 return static_cast<int>(fragment_type_) + 1000;
248 switch (fragment_type_)
250 case demo::FragmentType::TOY1:
253 case demo::FragmentType::TOY2:
257 throw cet::exception(
"ToyHardwareInterface") <<
"Unknown board type " << fragment_type_ <<
" ("
258 << demo::fragmentTypeToString(fragment_type_) <<
").\n";
265 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.