$treeview $search $mathjax $extrastylesheet
artdaq_demo
v3_04_01
$projectbrief
|
$projectbrief
|
$searchbox |
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 // JCF, Mar-17-2016 00016 00017 // ToyHardwareInterface is meant to mimic a vendor-provided hardware 00018 // API, usable within the the ToySimulator fragment generator. For 00019 // purposes of realism, it's a C++03-style API, as opposed to, say, one 00020 // based in C++11 capable of taking advantage of smart pointers, etc. 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 , // MUST be after "fragment_type" 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 // JCF, Aug-14-2016 00053 00054 // The logic of checking that nADCcounts_after_N_seconds_ >= 0, 00055 // below, is because signed vs. unsigned comparison won't do what 00056 // you want it to do if nADCcounts_after_N_seconds_ is negative 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 // JCF, Mar-18-2017 00079 00080 // "StartDatataking" is meant to mimic actions one would take when 00081 // telling the hardware to start sending data - the uploading of 00082 // values to registers, etc. 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 // Pretend the hardware hangs 00134 while (true) {} 00135 } 00136 } 00137 00138 // Make the fake data, starting with the header 00139 00140 // Can't handle a fragment whose size isn't evenly divisible by 00141 // the demo::ToyFragment::Header::data_t type size in bytes 00142 //std::cout << "Bytes to read: " << *bytes_read << ", sizeof(data_t): " << sizeof(demo::ToyFragment::Header::data_t) << std::endl; 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 // Generate nADCcounts ADC values ranging from 0 to max based on 00152 // the desired distribution 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 // Pretend that the "BoardType" is some vendor-defined integer which 00252 // differs from the fragment_type_ we want to use as developers (and 00253 // which must be between 1 and 224, inclusive) so add an offset 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 // Serial number is generated from the uniform distribution on initialization of the class 00280 return serial_number_; 00281 }