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_ || send_calls_ == 0)
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 header->distribution_type = static_cast<uint8_t>(distribution_type_);
00150
00151
00152
00153
00154 std::function<data_t()> generator;
00155 data_t gen_seed = 0;
00156
00157 switch (distribution_type_)
00158 {
00159 case DistributionType::uniform:
00160 generator = [&]()
00161 {
00162 return static_cast<data_t>
00163 ((*uniform_distn_)(engine_));
00164 };
00165 break;
00166
00167 case DistributionType::gaussian:
00168 generator = [&]()
00169 {
00170 do
00171 {
00172 gen_seed = static_cast<data_t>(std::round((*gaussian_distn_)(engine_)));
00173 } while (gen_seed > maxADCvalue_);
00174 return gen_seed;
00175 };
00176 break;
00177
00178 case DistributionType::monotonic:
00179 {
00180 generator = [&]()
00181 {
00182 if (++gen_seed > maxADCvalue_) gen_seed = 0;
00183 return gen_seed;
00184 };
00185 }
00186 break;
00187
00188 case DistributionType::uninitialized:
00189 case DistributionType::uninit2:
00190 break;
00191
00192 default:
00193 throw cet::exception("HardwareInterface") <<
00194 "Unknown distribution type specified";
00195 }
00196
00197 if (distribution_type_ != DistributionType::uninitialized && distribution_type_ != DistributionType::uninit2)
00198 {
00199 std::generate_n(reinterpret_cast<data_t*>(reinterpret_cast<demo::ToyFragment::Header*>(buffer) + 1),
00200 nADCcounts_,
00201 generator
00202 );
00203 }
00204 }
00205 else
00206 {
00207 throw cet::exception("ToyHardwareInterface") <<
00208 "Attempt to call FillBuffer when not sending data";
00209 }
00210
00211 if (send_calls_ == 0)
00212 { start_time_ = std::chrono::steady_clock::now();
00213 TLOG(50) << "FillBuffer has set the start_time_";
00214 }
00215
00216 if (usecs_between_sends_ != 0)
00217 {
00218
00219 if (send_calls_ != 0)
00220 {
00221
00222 #pragma GCC diagnostic push
00223 #pragma GCC diagnostic ignored "-Wsign-compare"
00224
00225 auto usecs_since_start = artdaq::TimeUtils::GetElapsedTimeMicroseconds(start_time_);
00226 long delta = (long)(usecs_between_sends_ * send_calls_) - usecs_since_start;
00227 if (delta > 0)
00228 usleep(delta);
00229
00230 TLOG(15) << "FillBuffer send_calls=" << send_calls_ << " usecs_since_start=" << usecs_since_start << " delta=" << delta ;
00231
00232 #pragma GCC diagnostic pop
00233 }
00234 }
00235 ++send_calls_;
00236 }
00237
00238 void ToyHardwareInterface::AllocateReadoutBuffer(char** buffer)
00239 {
00240 *buffer = reinterpret_cast<char*>(new uint8_t[sizeof(demo::ToyFragment::Header) + maxADCcounts_ * sizeof(data_t)]);
00241 }
00242
00243 void ToyHardwareInterface::FreeReadoutBuffer(char* buffer)
00244 {
00245 delete[] buffer;
00246 }
00247
00248
00249 int ToyHardwareInterface::BoardType() const
00250 {
00251
00252
00253
00254 return static_cast<int>(fragment_type_) + 1000;
00255 }
00256
00257 int ToyHardwareInterface::NumADCBits() const
00258 {
00259 switch (fragment_type_)
00260 {
00261 case demo::FragmentType::TOY1:
00262 return 12;
00263 break;
00264 case demo::FragmentType::TOY2:
00265 return 14;
00266 break;
00267 default:
00268 throw cet::exception("ToyHardwareInterface")
00269 << "Unknown board type "
00270 << fragment_type_
00271 << " ("
00272 << demo::fragmentTypeToString(fragment_type_)
00273 << ").\n";
00274 };
00275 }
00276
00277 int ToyHardwareInterface::SerialNumber() const
00278 {
00279
00280 return serial_number_;
00281 }