00001 #include "artdaq-demo/Generators/ToyHardwareInterface/ToyHardwareInterface.hh"
00002 #define TRACE_NAME "ToyHardwareInterface"
00003 #include "artdaq/DAQdata/Globals.hh"
00004 #include "artdaq-core-demo/Overlays/ToyFragment.hh"
00005 #include "artdaq-core-demo/Overlays/FragmentType.hh"
00006
00007 #include "fhiclcpp/ParameterSet.h"
00008 #include "cetlib_except/exception.h"
00009
00010 #include <random>
00011 #include <unistd.h>
00012 #include <iostream>
00013 #include <cstdlib>
00014
00015
00016
00017
00018
00019
00020
00021
00022 ToyHardwareInterface::ToyHardwareInterface(fhicl::ParameterSet const& ps) :
00023 taking_data_(false)
00024 , nADCcounts_(ps.get<size_t>("nADCcounts", 40))
00025 , maxADCcounts_(ps.get<size_t>("maxADCcounts", 50000000))
00026 , change_after_N_seconds_(ps.get<size_t>("change_after_N_seconds",
00027 std::numeric_limits<size_t>::max()))
00028 , nADCcounts_after_N_seconds_(ps.get<int>("nADCcounts_after_N_seconds",
00029 nADCcounts_))
00030 , exception_after_N_seconds_(ps.get<bool>("exception_after_N_seconds",
00031 false))
00032 , exit_after_N_seconds_(ps.get<bool>("exit_after_N_seconds",
00033 false))
00034 , abort_after_N_seconds_(ps.get<bool>("abort_after_N_seconds",
00035 false))
00036 , fragment_type_(demo::toFragmentType(ps.get<std::string>("fragment_type")))
00037 , maxADCvalue_(pow(2, NumADCBits()) - 1)
00038 ,
00039 throttle_usecs_(ps.get<size_t>("throttle_usecs", 100000))
00040 , usecs_between_sends_(ps.get<size_t>("usecs_between_sends", 0))
00041 , distribution_type_(static_cast<DistributionType>(ps.get<int>("distribution_type")))
00042 , engine_(ps.get<int64_t>("random_seed", 314159))
00043 , uniform_distn_(new std::uniform_int_distribution<data_t>(0, maxADCvalue_))
00044 , gaussian_distn_(new std::normal_distribution<double>(0.5 * maxADCvalue_, 0.1 * maxADCvalue_))
00045 , start_time_(fake_time_)
00046 , send_calls_(0)
00047 , serial_number_((*uniform_distn_)(engine_))
00048 {
00049 #pragma GCC diagnostic push
00050 #pragma GCC diagnostic ignored "-Wsign-compare"
00051
00052
00053
00054
00055
00056
00057
00058 if (nADCcounts_ > maxADCcounts_ ||
00059 (nADCcounts_after_N_seconds_ >= 0 && nADCcounts_after_N_seconds_ > maxADCcounts_))
00060 {
00061 throw cet::exception("HardwareInterface") << "Either (or both) of \"nADCcounts\" and \"nADCcounts_after_N_seconds\"" <<
00062 " is larger than the \"maxADCcounts\" setting (currently at " << maxADCcounts_ << ")";
00063 }
00064
00065 bool planned_disruption = nADCcounts_after_N_seconds_ != nADCcounts_ ||
00066 exception_after_N_seconds_ ||
00067 exit_after_N_seconds_ ||
00068 abort_after_N_seconds_;
00069
00070 if (planned_disruption &&
00071 change_after_N_seconds_ == std::numeric_limits<size_t>::max())
00072 {
00073 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";
00074 #pragma GCC diagnostic pop
00075 }
00076 }
00077
00078
00079
00080
00081
00082
00083
00084 void ToyHardwareInterface::StartDatataking()
00085 {
00086 taking_data_ = true;
00087 send_calls_ = 0;
00088 }
00089
00090 void ToyHardwareInterface::StopDatataking()
00091 {
00092 taking_data_ = false;
00093 start_time_ = fake_time_;
00094 }
00095
00096
00097 void ToyHardwareInterface::FillBuffer(char* buffer, size_t* bytes_read)
00098 {
00099 if (taking_data_)
00100 {
00101 usleep(throttle_usecs_);
00102
00103 auto elapsed_secs_since_datataking_start = artdaq::TimeUtils::GetElapsedTime(start_time_);
00104
00105 #pragma GCC diagnostic push
00106 #pragma GCC diagnostic ignored "-Wsign-compare"
00107 if (elapsed_secs_since_datataking_start < change_after_N_seconds_)
00108 {
00109 #pragma GCC diagnostic pop
00110
00111 *bytes_read = sizeof(demo::ToyFragment::Header) + nADCcounts_ * sizeof(data_t);
00112 }
00113 else
00114 {
00115 if (abort_after_N_seconds_)
00116 {
00117 std::abort();
00118 }
00119 else if (exit_after_N_seconds_)
00120 {
00121 std::exit(1);
00122 }
00123 else if (exception_after_N_seconds_)
00124 {
00125 throw cet::exception("HardwareInterface") << "This is an engineered exception designed for testing purposes";
00126 }
00127 else if (nADCcounts_after_N_seconds_ >= 0)
00128 {
00129 *bytes_read = sizeof(demo::ToyFragment::Header) + nADCcounts_after_N_seconds_ * sizeof(data_t);
00130 }
00131 else
00132 {
00133
00134 while (true) {}
00135 }
00136 }
00137
00138
00139
00140
00141
00142
00143 assert(*bytes_read % sizeof(demo::ToyFragment::Header::data_t) == 0);
00144
00145 demo::ToyFragment::Header* header = reinterpret_cast<demo::ToyFragment::Header*>(buffer);
00146
00147 header->event_size = *bytes_read / sizeof(demo::ToyFragment::Header::data_t);
00148 header->trigger_number = 99;
00149
00150
00151
00152
00153 std::function<data_t()> generator;
00154 data_t gen_seed = 0;
00155
00156 switch (distribution_type_)
00157 {
00158 case DistributionType::uniform:
00159 generator = [&]()
00160 {
00161 return static_cast<data_t>
00162 ((*uniform_distn_)(engine_));
00163 };
00164 break;
00165
00166 case DistributionType::gaussian:
00167 generator = [&]()
00168 {
00169 do
00170 {
00171 gen_seed = static_cast<data_t>(std::round((*gaussian_distn_)(engine_)));
00172 } while (gen_seed > maxADCvalue_);
00173 return gen_seed;
00174 };
00175 break;
00176
00177 case DistributionType::monotonic:
00178 {
00179 generator = [&]()
00180 {
00181 if (++gen_seed > maxADCvalue_) gen_seed = 0;
00182 return gen_seed;
00183 };
00184 }
00185 break;
00186
00187 case DistributionType::uninitialized:
00188 case DistributionType::uninit2:
00189 break;
00190
00191 default:
00192 throw cet::exception("HardwareInterface") <<
00193 "Unknown distribution type specified";
00194 }
00195
00196 if (distribution_type_ != DistributionType::uninitialized && distribution_type_ != DistributionType::uninit2)
00197 {
00198 std::generate_n(reinterpret_cast<data_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
00199 nADCcounts_,
00200 generator
00201 );
00202 }
00203 }
00204 else
00205 {
00206 throw cet::exception("ToyHardwareInterface") <<
00207 "Attempt to call FillBuffer when not sending data";
00208 }
00209
00210 if (send_calls_ == 0)
00211 { start_time_ = std::chrono::steady_clock::now();
00212 TRACE( 50, "ToyHardwareInterface::FillBuffer has set the start_time_" );
00213 }
00214
00215 if (usecs_between_sends_ != 0)
00216 {
00217
00218 if (send_calls_ != 0)
00219 {
00220
00221 #pragma GCC diagnostic push
00222 #pragma GCC diagnostic ignored "-Wsign-compare"
00223
00224 auto usecs_since_start = artdaq::TimeUtils::GetElapsedTimeMicroseconds(start_time_);
00225 long delta = (long)(usecs_between_sends_ * send_calls_) - usecs_since_start;
00226 if (delta > 0)
00227 usleep(delta);
00228
00229 TLOG_ARB(15, "ToyHardwareInterface") << "FillBuffer send_calls=" << std::to_string(send_calls_) << " usecs_since_start=" << std::to_string(usecs_since_start) << " delta=" << std::to_string(delta) << TLOG_ENDL;
00230
00231 #pragma GCC diagnostic pop
00232 }
00233 }
00234 ++send_calls_;
00235 }
00236
00237 void ToyHardwareInterface::AllocateReadoutBuffer(char** buffer)
00238 {
00239 *buffer = reinterpret_cast<char*>(new uint8_t[sizeof(demo::ToyFragment::Header) + maxADCcounts_ * sizeof(data_t)]);
00240 }
00241
00242 void ToyHardwareInterface::FreeReadoutBuffer(char* buffer)
00243 {
00244 delete[] buffer;
00245 }
00246
00247
00248 int ToyHardwareInterface::BoardType() const
00249 {
00250
00251
00252
00253 return static_cast<int>(fragment_type_) + 1000;
00254 }
00255
00256 int ToyHardwareInterface::NumADCBits() const
00257 {
00258 switch (fragment_type_)
00259 {
00260 case demo::FragmentType::TOY1:
00261 return 12;
00262 break;
00263 case demo::FragmentType::TOY2:
00264 return 14;
00265 break;
00266 default:
00267 throw cet::exception("ToyHardwareInterface")
00268 << "Unknown board type "
00269 << fragment_type_
00270 << " ("
00271 << demo::fragmentTypeToString(fragment_type_)
00272 << ").\n";
00273 };
00274 }
00275
00276 int ToyHardwareInterface::SerialNumber() const
00277 {
00278
00279 return serial_number_;
00280 }