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"
25 , change_after_N_seconds_(ps.get<size_t>(
"change_after_N_seconds", std::numeric_limits<size_t>::max()))
26 , pause_after_N_seconds_(ps.get<size_t>(
"pause_after_N_seconds", 0))
27 , exception_after_N_seconds_(ps.get<bool>(
"exception_after_N_seconds", false))
28 , exit_after_N_seconds_(ps.get<bool>(
"exit_after_N_seconds", false))
29 , abort_after_N_seconds_(ps.get<bool>(
"abort_after_N_seconds", false))
30 , hang_after_N_seconds_(ps.get<bool>(
"hang_after_N_seconds", false))
31 , fragment_type_(demo::toFragmentType(ps.get<std::string>(
"fragment_type")))
32 , maxADCvalue_(static_cast<size_t>(pow(2, NumADCBits()) - 1))
33 , distribution_type_(static_cast<
DistributionType>(ps.get<int>(
"distribution_type")))
35 , engine_(ps.get<int64_t>(
"random_seed", 314159))
36 , uniform_distn_(new std::uniform_int_distribution<demo::ToyFragment::adc_t>(0, maxADCvalue_))
37 , gaussian_distn_(new std::normal_distribution<double>(0.5 * maxADCvalue_, 0.1 * maxADCvalue_))
38 , start_time_(fake_time_)
39 , rate_start_time_(fake_time_)
41 , serial_number_((*uniform_distn_)(engine_))
43 bool planned_disruption = exception_after_N_seconds_ || exit_after_N_seconds_ || abort_after_N_seconds_;
45 if (planned_disruption && change_after_N_seconds_ == std::numeric_limits<size_t>::max())
47 throw cet::exception(
"HardwareInterface") <<
"A FHiCL parameter designed to create a disruption has been "
48 "set, so \"change_after_N_seconds\" should be set as well";
51 if (ps.has_key(
"nADCcounts") || !ps.has_key(
"rate_table"))
54 auto counts1 = ps.get<
size_t>(
"nADCcounts", 40);
55 auto counts2 = ps.get<
size_t>(
"nADCcounts_after_N_seconds", counts1);
57 auto throttle = ps.get<
size_t>(
"throttle_usecs", 100000);
58 auto between = ps.get<
size_t>(
"usecs_between_sends", 0);
59 auto wait = throttle + between;
60 auto rate = wait > 0 ? 1000000 / wait : 0;
63 before.size_bytes = counts1 *
sizeof(demo::ToyFragment::Header::data_t) +
sizeof(demo::ToyFragment::Header);
64 before.rate_hz = rate;
65 before.duration = change_after_N_seconds_ != std::numeric_limits<size_t>::max()
66 ? std::chrono::microseconds(1000000 * change_after_N_seconds_)
67 : std::chrono::microseconds(1000000);
68 configured_rates_.push_back(before);
70 if (change_after_N_seconds_ != std::numeric_limits<size_t>::max())
73 after.size_bytes = counts2 *
sizeof(demo::ToyFragment::Header::data_t) +
sizeof(demo::ToyFragment::Header);
75 after.duration = std::chrono::microseconds(1000000 * change_after_N_seconds_);
76 configured_rates_.push_back(after);
82 auto fhicl_rates = ps.get<std::vector<fhicl::ParameterSet>>(
"rate_table");
84 for (
auto& pps : fhicl_rates)
87 this_rate.size_bytes = pps.get<
size_t>(
"size_bytes");
88 this_rate.rate_hz = pps.get<
size_t>(
"rate_hz");
89 this_rate.duration = std::chrono::microseconds(pps.get<
size_t>(
"duration_us", 1000000));
90 configured_rates_.push_back(this_rate);
95 for (
auto& rate : configured_rates_)
97 TLOG(TLVL_INFO) << (first ?
"W" :
", then w") <<
"ill generate " << rate.size_bytes <<
" B Fragments at " << rate.rate_hz <<
" Hz for " << rate.duration.count() <<
" us";
101 current_rate_ = configured_rates_.begin();
113 rate_send_calls_ = 0;
114 current_rate_ = configured_rates_.begin();
115 start_time_ = std::chrono::steady_clock::now();
116 rate_start_time_ = start_time_;
121 taking_data_ =
false;
122 start_time_ = fake_time_;
123 rate_start_time_ = fake_time_;
128 TLOG(TLVL_TRACE) <<
"FillBuffer BEGIN";
131 auto elapsed_secs_since_datataking_start = artdaq::TimeUtils::GetElapsedTime(start_time_);
132 if (elapsed_secs_since_datataking_start < 0) elapsed_secs_since_datataking_start = 0;
134 if (static_cast<size_t>(elapsed_secs_since_datataking_start) >= change_after_N_seconds_)
136 if (abort_after_N_seconds_)
138 TLOG(TLVL_ERROR) <<
"Engineered Abort!";
141 else if (exit_after_N_seconds_)
143 TLOG(TLVL_ERROR) <<
"Engineered Exit!";
146 else if (exception_after_N_seconds_)
148 TLOG(TLVL_ERROR) <<
"Engineered Exception!";
149 throw cet::exception(
"HardwareInterface")
150 <<
"This is an engineered exception designed for testing purposes";
152 else if (hang_after_N_seconds_)
154 TLOG(TLVL_ERROR) <<
"Pretending that the hardware has hung! Variable name for gdb: hardwareIsHung";
155 volatile bool hardwareIsHung =
true;
157 while (hardwareIsHung)
163 if ((pause_after_N_seconds_ != 0u) && (static_cast<size_t>(elapsed_secs_since_datataking_start) % change_after_N_seconds_ == 0))
165 TLOG(TLVL_DEBUG + 3) <<
"pausing " << pause_after_N_seconds_ <<
" seconds";
166 sleep(pause_after_N_seconds_);
167 TLOG(TLVL_DEBUG + 3) <<
"resuming after pause of " << pause_after_N_seconds_ <<
" seconds";
171 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Setting bytes_read to " <<
sizeof(demo::ToyFragment::Header) + bytes_to_nWords_(current_rate_->size_bytes) *
sizeof(demo::ToyFragment::Header::data_t);
172 *bytes_read =
sizeof(demo::ToyFragment::Header) + bytes_to_nWords_(current_rate_->size_bytes) *
sizeof(demo::ToyFragment::Header::data_t);
173 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Making the fake data, starting with the header";
179 assert(*bytes_read %
sizeof(demo::ToyFragment::Header::data_t) == 0);
181 auto* header =
reinterpret_cast<demo::ToyFragment::Header*
>(buffer);
183 header->event_size = *bytes_read /
sizeof(demo::ToyFragment::Header::data_t);
184 header->trigger_number = 99;
185 header->distribution_type =
static_cast<uint8_t
>(distribution_type_);
187 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Generating nADCcounts ADC values ranging from 0 to max based on the desired distribution";
189 std::function<demo::ToyFragment::adc_t()> generator;
190 demo::ToyFragment::adc_t gen_seed = 0;
192 switch (distribution_type_)
195 generator = [&]() {
return static_cast<demo::ToyFragment::adc_t
>((*uniform_distn_)(engine_)); };
202 gen_seed =
static_cast<demo::ToyFragment::adc_t
>(std::round((*gaussian_distn_)(engine_)));
203 }
while (gen_seed > maxADCvalue_);
210 if (++gen_seed > maxADCvalue_)
220 case DistributionType::uninit2:
224 throw cet::exception(
"HardwareInterface") <<
"Unknown distribution type specified";
229 TLOG(TLVL_DEBUG + 3) <<
"FillBuffer: Calling generate_n";
230 std::generate_n(reinterpret_cast<demo::ToyFragment::adc_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
231 bytes_to_nADCs_(current_rate_->size_bytes), generator);
236 throw cet::exception(
"ToyHardwareInterface") <<
"Attempt to call FillBuffer when not sending data";
239 auto now = std::chrono::steady_clock::now();
240 auto next = next_trigger_time_();
244 std::this_thread::sleep_until(next_trigger_time_());
247 TLOG(TLVL_TRACE) <<
"FillBuffer END";
252 *buffer =
reinterpret_cast<char*
>(
253 new uint8_t[
sizeof(demo::ToyFragment::Header) + maxADCcounts_() *
sizeof(demo::ToyFragment::Header::data_t)]);
263 return static_cast<int>(fragment_type_) + 1000;
266 std::chrono::microseconds ToyHardwareInterface::rate_to_delay_(std::size_t hz) {
return std::chrono::microseconds(static_cast<int>(1000000.0 / hz)); }
268 std::chrono::steady_clock::time_point ToyHardwareInterface::next_trigger_time_()
270 auto next_time = rate_start_time_ + (rate_send_calls_ + 1) * rate_to_delay_(current_rate_->rate_hz);
271 if (next_time > rate_start_time_ + current_rate_->duration)
273 if (++current_rate_ == configured_rates_.end()) current_rate_ = configured_rates_.begin();
274 rate_send_calls_ = 0;
275 rate_start_time_ = next_time;
280 size_t ToyHardwareInterface::bytes_to_nWords_(
size_t bytes)
282 if (bytes <
sizeof(demo::ToyFragment::Header))
return 0;
283 return ceil((bytes -
sizeof(demo::ToyFragment::Header)) / static_cast<double>(
sizeof(demo::ToyFragment::Header::data_t)));
286 size_t ToyHardwareInterface::bytes_to_nADCs_(
size_t bytes)
288 if (bytes <
sizeof(demo::ToyFragment::Header))
return 0;
289 return ceil((bytes -
sizeof(demo::ToyFragment::Header)) / static_cast<double>(
sizeof(demo::ToyFragment::adc_t)));
292 size_t ToyHardwareInterface::maxADCcounts_()
294 size_t max_bytes = 0;
295 for (
auto& rate : configured_rates_)
297 if (rate.size_bytes > max_bytes) max_bytes = rate.size_bytes;
299 return bytes_to_nWords_(max_bytes);
304 switch (fragment_type_)
306 case demo::FragmentType::TOY1:
309 case demo::FragmentType::TOY2:
313 throw cet::exception(
"ToyHardwareInterface") <<
"Unknown board type " << fragment_type_ <<
" ("
314 << demo::fragmentTypeToString(fragment_type_) <<
").\n";
321 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...
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.