1 #include "artdaq/DAQdata/Globals.hh"
2 #define TRACE_NAME (app_name + "_CommandableFragmentGenerator").c_str() // include these 2 first -
4 #include "artdaq/Generators/CommandableFragmentGenerator.hh"
6 #include <boost/exception/all.hpp>
7 #include <boost/throw_exception.hpp>
13 #include "canvas/Utilities/Exception.h"
14 #include "cetlib_except/exception.h"
15 #include "fhiclcpp/ParameterSet.h"
17 #include "artdaq-core/Data/ContainerFragmentLoader.hh"
18 #include "artdaq-core/Data/Fragment.hh"
19 #include "artdaq-core/Utilities/ExceptionHandler.hh"
20 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
21 #include "artdaq-core/Utilities/TimeUtils.hh"
32 #define TLVL_GETNEXT 10
33 #define TLVL_GETNEXT_VERBOSE 20
34 #define TLVL_CHECKSTOP 11
35 #define TLVL_EVCOUNTERINC 12
36 #define TLVL_GETDATALOOP 13
37 #define TLVL_GETDATALOOP_DATABUFFWAIT 21
38 #define TLVL_GETDATALOOP_VERBOSE 20
39 #define TLVL_WAITFORBUFFERREADY 15
40 #define TLVL_GETBUFFERSTATS 16
41 #define TLVL_CHECKDATABUFFER 17
42 #define TLVL_GETMONITORINGDATA 18
43 #define TLVL_APPLYREQUESTS 9
44 #define TLVL_SENDEMPTYFRAGMENTS 19
45 #define TLVL_CHECKWINDOWS 14
46 #define TLVL_EMPTYFRAGMENT 22
50 , useMonitoringThread_(ps.get<bool>(
"separate_monitoring_thread", false))
51 , monitoringInterval_(ps.get<int64_t>(
"hardware_poll_interval_us", 0))
55 , timeout_(std::numeric_limits<uint64_t>::max())
56 , timestamp_(std::numeric_limits<uint64_t>::max())
59 , latest_exception_report_(
"none")
62 , sleep_on_stop_us_(0)
64 board_id_ = ps.get<
int>(
"board_id");
65 instance_name_for_metrics_ =
"BoardReader." + boost::lexical_cast<std::string>(board_id_);
67 auto fragment_ids = ps.get<std::vector<artdaq::Fragment::fragment_id_t>>(
"fragment_ids", std::vector<artdaq::Fragment::fragment_id_t>());
69 TLOG(TLVL_TRACE) <<
"artdaq::CommandableFragmentGenerator::CommandableFragmentGenerator(ps)";
72 if (fragment_id != -99)
74 if (!fragment_ids.empty())
76 latest_exception_report_ = R
"(Error in CommandableFragmentGenerator: can't both define "fragment_id" and "fragment_ids" in FHiCL document)";
77 TLOG(TLVL_ERROR) << latest_exception_report_;
78 throw cet::exception(latest_exception_report_);
81 fragment_ids.emplace_back(fragment_id);
84 auto generated_fragments_per_event = ps.get<
size_t>(
"generated_fragments_per_event", 1);
85 if (generated_fragments_per_event != fragment_ids.size()) {
86 latest_exception_report_ = R
"(Error in CommandableFragmentGenerator: "generated_fragments_per_event" disagrees with size of "fragment_ids" list!)";
87 TLOG(TLVL_ERROR) << latest_exception_report_;
88 throw cet::exception(latest_exception_report_);
91 for (
auto&
id : fragment_ids)
93 expectedTypes_[id] = artdaq::Fragment::EmptyFragmentType;
96 sleep_on_stop_us_ = ps.get<
int>(
"sleep_on_stop_us", 0);
107 TLOG(TLVL_DEBUG) <<
"Joining monitoringThread";
110 if (monitoringThread_.joinable())
112 monitoringThread_.join();
119 TLOG(TLVL_DEBUG) <<
"joinThreads complete";
126 if (check_stop()) usleep(sleep_on_stop_us_);
127 if (exception() || should_stop_)
return false;
129 if (!useMonitoringThread_ && monitoringInterval_ > 0)
131 TLOG(TLVL_GETNEXT) <<
"getNext: Checking whether to collect Monitoring Data";
132 auto now = std::chrono::steady_clock::now();
134 if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
136 TLOG(TLVL_GETNEXT) <<
"getNext: Collecting Monitoring Data";
137 isHardwareOK_ = checkHWStatus_();
138 TLOG(TLVL_GETNEXT) <<
"getNext: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_;
139 lastMonitoringCall_ = now;
145 std::lock_guard<std::mutex> lk(mutex_);
148 TLOG(TLVL_ERROR) <<
"Stopping CFG because the hardware reports bad status!";
151 TLOG(TLVL_TRACE) <<
"getNext: Calling getNext_ w/ ev_counter()=" << ev_counter();
154 result = getNext_(output);
160 TLOG(TLVL_TRACE) <<
"getNext: Done with getNext_ - ev_counter() now " << ev_counter();
161 for (
auto& dataIter : output)
163 TLOG(TLVL_GETNEXT_VERBOSE) <<
"getNext: getNext_() returned fragment with sequenceID = " << dataIter->sequenceID()
164 <<
", type = " << dataIter->typeString() <<
", id = " << std::to_string(dataIter->fragmentID())
165 <<
", timestamp = " << dataIter->timestamp() <<
", and sizeBytes = " << dataIter->sizeBytes();
167 auto fragId = dataIter->fragmentID();
168 auto type = dataIter->type();
171 if (Fragment::isSystemFragmentType(type))
176 if (!expectedTypes_.count(fragId))
178 TLOG(TLVL_ERROR) <<
"Received Fragment with Fragment ID " << fragId <<
", which is not in the declared list of Fragment IDs! Aborting!";
181 if (expectedTypes_[fragId] == Fragment::EmptyFragmentType)
182 expectedTypes_[fragId] = type;
183 else if (expectedTypes_[fragId] != type)
185 TLOG(TLVL_WARNING) <<
"Received Fragment with Fragment ID " << fragId <<
" and type " << dataIter->typeString() <<
"(" << type <<
"), which does not match expected type for this ID (" << expectedTypes_[fragId] <<
")";
189 catch (
const cet::exception& e)
191 latest_exception_report_ =
"cet::exception caught in getNext(): ";
192 latest_exception_report_.append(e.what());
193 TLOG(TLVL_ERROR) <<
"getNext: cet::exception caught: " << e;
197 catch (
const boost::exception& e)
199 latest_exception_report_ =
"boost::exception caught in getNext(): ";
200 latest_exception_report_.append(boost::diagnostic_information(e));
201 TLOG(TLVL_ERROR) <<
"getNext: boost::exception caught: " << boost::diagnostic_information(e);
205 catch (
const std::exception& e)
207 latest_exception_report_ =
"std::exception caught in getNext(): ";
208 latest_exception_report_.append(e.what());
209 TLOG(TLVL_ERROR) <<
"getNext: std::exception caught: " << e.what();
215 latest_exception_report_ =
"Unknown exception caught in getNext().";
216 TLOG(TLVL_ERROR) <<
"getNext: unknown exception caught";
223 TLOG(TLVL_DEBUG) <<
"getNext: Either getNext_ or applyRequests returned false, stopping";
226 if (metricMan && !output.empty())
228 auto timestamp = output.front()->timestamp();
230 if (output.size() > 1)
232 for (
auto& outputfrag : output)
234 if (outputfrag->timestamp() > timestamp)
236 timestamp = outputfrag->timestamp();
241 metricMan->sendMetric(
"Last Timestamp", timestamp,
"Ticks", 1, MetricMode::LastPoint);
249 TLOG(TLVL_CHECKSTOP) <<
"CFG::check_stop: should_stop=" << should_stop() <<
", exception status =" << int(exception());
251 if (!should_stop())
return false;
258 TLOG(TLVL_EVCOUNTERINC) <<
"ev_counter_inc: Incrementing ev_counter from " << ev_counter() <<
" by " << step;
259 return ev_counter_.fetch_add(step);
264 TLOG(TLVL_TRACE) <<
"Start Command received.";
267 TLOG(TLVL_ERROR) <<
"negative run number";
268 throw cet::exception(
"CommandableFragmentGenerator") <<
"negative run number";
272 timestamp_ = timestamp;
273 ev_counter_.store(1);
275 should_stop_.store(
false);
276 exception_.store(
false);
279 latest_exception_report_ =
"none";
283 std::unique_lock<std::mutex> lk(mutex_);
284 if (useMonitoringThread_) startMonitoringThread();
285 TLOG(TLVL_TRACE) <<
"Start Command complete.";
290 TLOG(TLVL_TRACE) <<
"Stop Command received.";
293 timestamp_ = timestamp;
296 should_stop_.store(
true);
297 std::unique_lock<std::mutex> lk(mutex_);
301 TLOG(TLVL_TRACE) <<
"Stop Command complete.";
306 TLOG(TLVL_TRACE) <<
"Pause Command received.";
308 timestamp_ = timestamp;
311 should_stop_.store(
true);
312 std::unique_lock<std::mutex> lk(mutex_);
319 TLOG(TLVL_TRACE) <<
"Resume Command received.";
321 timestamp_ = timestamp;
324 should_stop_ =
false;
329 std::unique_lock<std::mutex> lk(mutex_);
332 TLOG(TLVL_TRACE) <<
"Resume Command complete.";
337 TLOG(TLVL_TRACE) <<
"Report Command received.";
338 std::lock_guard<std::mutex> lk(mutex_);
345 std::string childReport = reportSpecific(which);
346 if (childReport.length() > 0) {
return childReport; }
349 if (which ==
"latest_exception")
351 return latest_exception_report_;
355 childReport = report();
356 if (childReport.length() > 0) {
return childReport; }
366 TLOG(TLVL_TRACE) <<
"Report Command complete.";
373 #pragma message "Using default implementation of CommandableFragmentGenerator::pauseNoMutex()"
378 #pragma message "Using default implementation of CommandableFragmentGenerator::pause()"
382 #pragma message "Using default implementation of CommandableFragmentGenerator::resume()"
387 #pragma message "Using default implementation of CommandableFragmentGenerator::report()"
393 #pragma message "Using default implementation of CommandableFragmentGenerator::reportSpecific(std::string)"
399 #pragma message "Using default implementation of CommandableFragmentGenerator::checkHWStatus_()"
405 #pragma message "Using default implementation of CommandableFragmentGenerator::metaCommand(std::string, std::string)"
411 if (monitoringThread_.joinable())
413 monitoringThread_.join();
415 TLOG(TLVL_INFO) <<
"Starting Hardware Monitoring Thread";
420 snprintf(tname,
sizeof(tname) - 1,
"%d-CFGMon", my_rank);
421 tname[
sizeof(tname) - 1] =
'\0';
422 auto handle = monitoringThread_.native_handle();
423 pthread_setname_np(handle, tname);
425 catch (
const boost::exception& e)
427 TLOG(TLVL_ERROR) <<
"Caught boost::exception starting Hardware Monitoring thread: " << boost::diagnostic_information(e) <<
", errno=" << errno;
428 throw cet::exception(
"ThreadError") <<
"Caught boost::exception starting Hardware Monitoring thread: " << boost::diagnostic_information(e) <<
", errno=" << errno << std::endl;
434 while (!should_stop())
436 if (should_stop() || monitoringInterval_ <= 0)
438 TLOG(TLVL_DEBUG) <<
"getMonitoringDataLoop: should_stop() is " << std::boolalpha << should_stop()
439 <<
" and monitoringInterval is " << monitoringInterval_ <<
", returning";
442 TLOG(TLVL_GETMONITORINGDATA) <<
"getMonitoringDataLoop: Determining whether to call checkHWStatus_";
444 auto now = std::chrono::steady_clock::now();
445 if (TimeUtils::GetElapsedTimeMicroseconds(lastMonitoringCall_, now) >= static_cast<size_t>(monitoringInterval_))
447 isHardwareOK_ = checkHWStatus_();
448 TLOG(TLVL_GETMONITORINGDATA) <<
"getMonitoringDataLoop: isHardwareOK_ is now " << std::boolalpha << isHardwareOK_;
449 lastMonitoringCall_ = now;
451 usleep(monitoringInterval_ / 10);
CommandableFragmentGenerator(const fhicl::ParameterSet &ps)
CommandableFragmentGenerator Constructor.
virtual bool checkHWStatus_()
Check any relavent hardware status registers. Return false if an error condition exists that should h...
virtual ~CommandableFragmentGenerator()
CommandableFragmentGenerator Destructor.
void getMonitoringDataLoop()
This function regularly calls checkHWStatus_(), and sets the isHardwareOK flag accordingly.
artdaq::Fragment::fragment_id_t fragment_id() const
Get the Fragment ID of this Fragment generator.
std::string ReportCmd(std::string const &which="")
Get a report about a user-specified run-time quantity.
virtual bool metaCommand(std::string const &command, std::string const &arg)
The meta-command is used for implementing user-specific commands in a CommandableFragmentGenerator.
void StopCmd(uint64_t timeout, uint64_t timestamp)
Stop the CommandableFragmentGenerator.
void StartCmd(int run, uint64_t timeout, uint64_t timestamp)
Start the CommandableFragmentGenerator.
virtual void pauseNoMutex()
On call to PauseCmd, pauseNoMutex() is called prior to PauseCmd acquiring the mutex ...
bool check_stop()
Routine used by applyRequests to make sure that all outstanding requests have been fulfilled before r...
void ResumeCmd(uint64_t timeout, uint64_t timestamp)
Resume the CommandableFragmentGenerator.
bool getNext(FragmentPtrs &output) overridefinal
getNext calls either applyRequests or getNext_ to get any data that is ready to be sent to the EventB...
void PauseCmd(uint64_t timeout, uint64_t timestamp)
Pause the CommandableFragmentGenerator.
void startMonitoringThread()
Function that launches the monitoring thread (getMonitoringDataLoop())
virtual void pause()
If a CommandableFragmentGenerator subclass is reading from hardware, the implementation of pause() sh...
virtual void resume()
The subrun number will be incremented before a call to resume.
virtual std::string report()
Let's say that the contract with the report() functions is that they return a non-empty string if the...
size_t ev_counter_inc(size_t step=1)
Increment the event counter.
virtual std::string reportSpecific(std::string const &what)
Report the status of a specific quantity
void joinThreads()
Join any data-taking threads. Should be called when destructing CommandableFragmentGenerator.