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