otsdaq  v2_03_00
ARTDAQBoardReaderTable_table.cc
1 #include "otsdaq-core/ConfigurationInterface/ConfigurationManager.h"
2 #include "otsdaq-core/Macros/TablePluginMacros.h"
3 #include "otsdaq-core/TablePluginDataFormats/ARTDAQBoardReaderTable.h"
4 #include "otsdaq-core/TablePluginDataFormats/XDAQContextTable.h"
5 
6 #include <stdio.h>
7 #include <sys/stat.h> //for mkdir
8 #include <fstream> // std::fstream
9 #include <iostream>
10 
11 using namespace ots;
12 
13 #define ARTDAQ_FCL_PATH std::string(getenv("USER_DATA")) + "/" + "ARTDAQConfigurations/"
14 #define ARTDAQ_FILE_PREAMBLE "boardReader"
15 
16 // helpers
17 #define OUT out << tabStr << commentStr
18 #define PUSHTAB tabStr += "\t"
19 #define POPTAB tabStr.resize(tabStr.size() - 1)
20 #define PUSHCOMMENT commentStr += "# "
21 #define POPCOMMENT commentStr.resize(commentStr.size() - 2)
22 
23 //========================================================================================================================
24 ARTDAQBoardReaderTable::ARTDAQBoardReaderTable(void) : TableBase("ARTDAQBoardReaderTable")
25 {
27  // WARNING: the names used in C++ MUST match the Table INFO //
29 }
30 
31 //========================================================================================================================
32 ARTDAQBoardReaderTable::~ARTDAQBoardReaderTable(void) {}
33 
34 //========================================================================================================================
35 void ARTDAQBoardReaderTable::init(ConfigurationManager* configManager)
36 {
37  // make directory just in case
38  mkdir((ARTDAQ_FCL_PATH).c_str(), 0755);
39 
40  __COUT__ << "*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*" << __E__;
41  __COUT__ << configManager->__SELF_NODE__ << __E__;
42 
43  const XDAQContextTable* contextConfig =
44  configManager->__GET_CONFIG__(XDAQContextTable);
45 
46  std::vector<const XDAQContextTable::XDAQContext*> readerContexts =
47  contextConfig->getBoardReaderContexts();
48 
49  // for each reader context
50  // do NOT output associated fcl config file
51  // but do check the link is to a DataManager-esque thing
52  for(auto& readerContext : readerContexts)
53  {
54  try
55  {
56  ConfigurationTree readerConfigNode = contextConfig->getSupervisorConfigNode(
57  configManager,
58  readerContext->contextUID_,
59  readerContext->applications_[0].applicationUID_);
60 
61  __COUT__ << "Path for this reader config is " << readerContext->contextUID_
62  << "/" << readerContext->applications_[0].applicationUID_ << "/"
63  << readerConfigNode.getValueAsString() << __E__;
64 
65  __COUT__ << "Checking that this reader supervisor node is DataManager-like."
66  << __E__;
67 
68  readerConfigNode.getNode("LinkToDataBufferTable").getChildren();
69  }
70  catch(const std::runtime_error& e)
71  {
72  __SS__ << "artdaq Board Readers must be instantiated as a Consumer within a "
73  "DataManager table. "
74  << "Error found while checking for LinkToDataBufferTable: " << e.what()
75  << __E__;
76  __COUT_ERR__ << ss.str();
77  __COUT__ << "Path for this reader config is " << readerContext->contextUID_
78  << "/" << readerContext->applications_[0].applicationUID_ << "/X"
79  << __E__;
80  __COUT_ERR__ << "This board reader will likely not get instantiated "
81  "properly! Proceeding anyway with fcl generation."
82  << __E__;
83 
84  // proceed anyway, because it was really annoying to not be able to activate
85  // the table group when the context is being developed also.
86  //__SS_THROW__;
87  }
88 
89  // artdaq Reader is not at Supervisor level like other apps
90  // it is at Consumer level!
91  // outputFHICL(readerConfigNode,
92  // contextConfig);
93  }
94 
95  // handle fcl file generation, wherever the level of this table
96 
97  auto childrenMap = configManager->__SELF_NODE__.getChildren();
98  std::string appUID, buffUID, consumerUID;
99  for(auto& child : childrenMap)
100  {
101  const XDAQContextTable::XDAQContext* thisContext = nullptr;
102  for(auto& readerContext : readerContexts)
103  {
104  ConfigurationTree readerConfigNode = contextConfig->getSupervisorConfigNode(
105  configManager,
106  readerContext->contextUID_,
107  readerContext->applications_[0].applicationUID_);
108  auto dataManagerConfMap =
109  readerConfigNode.getNode("LinkToDataBufferTable").getChildren();
110  for(auto dmc : dataManagerConfMap)
111  {
112  auto dataBufferConfMap =
113  dmc.second.getNode("LinkToDataProcessorTable").getChildren();
114  for(auto dbc : dataBufferConfMap)
115  {
116  auto processorConfUID =
117  dbc.second.getNode("LinkToProcessorUID").getUIDAsString();
118  if(processorConfUID == child.second.getValue())
119  {
120  __COUT__ << "Found match for context UID: "
121  << readerContext->contextUID_ << __E__;
122  thisContext = readerContext;
123  }
124  }
125  }
126  }
127 
128  if(thisContext == nullptr)
129  {
130  __COUT_ERR__ << "Could not find matching context for this table!" << __E__;
131  }
132  else
133  {
134  if(child.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
135  .getValue<bool>())
136  {
137  outputFHICL(configManager,
138  child.second,
139  contextConfig->getARTDAQAppRank(thisContext->contextUID_),
140  contextConfig->getContextAddress(thisContext->contextUID_),
141  contextConfig->getARTDAQDataPort(configManager,
142  thisContext->contextUID_),
143  contextConfig);
144  }
145  }
146  }
147 }
148 
150 // void ARTDAQBoardReaderTable::getBoardReaderParents(const ConfigurationTree
151 // &readerNode, const ConfigurationTree &contextNode, std::string &applicationUID,
152 // std::string &bufferUID, std::string &consumerUID)
153 //{
154 // //search through all board reader contexts
155 // contextNode.getNode()
156 //}
157 
158 //========================================================================================================================
159 std::string ARTDAQBoardReaderTable::getFHICLFilename(
160  const ConfigurationTree& boardReaderNode)
161 {
162  __COUT__ << "ARTDAQ BoardReader UID: " << boardReaderNode.getValue() << __E__;
163  std::string filename = ARTDAQ_FCL_PATH + ARTDAQ_FILE_PREAMBLE + "-";
164  std::string uid = boardReaderNode.getValue();
165  for(unsigned int i = 0; i < uid.size(); ++i)
166  if((uid[i] >= 'a' && uid[i] <= 'z') || (uid[i] >= 'A' && uid[i] <= 'Z') ||
167  (uid[i] >= '0' && uid[i] <= '9')) // only allow alpha numeric in file name
168  filename += uid[i];
169 
170  filename += ".fcl";
171 
172  __COUT__ << "fcl: " << filename << __E__;
173 
174  return filename;
175 }
176 
177 //========================================================================================================================
178 void ARTDAQBoardReaderTable::outputFHICL(ConfigurationManager* configManager,
179  const ConfigurationTree& boardReaderNode,
180  unsigned int selfRank,
181  std::string selfHost,
182  unsigned int selfPort,
183  const XDAQContextTable* contextConfig)
184 {
185  /*
186  the file will look something like this:
187 
188  daq: {
189  fragment_receiver: {
190  mpi_sync_interval: 50
191 
192  # CommandableFragmentGenerator Table:
193  fragment_ids: []
194  fragment_id: -99 # Please define only one of these
195 
196  sleep_on_stop_us: 0
197 
198  requests_enabled: false # Whether to set up the socket for listening for
199  trigger messages request_mode: "Ignored" # Possible values are: Ignored, Single,
200  Buffer, Window
201 
202  data_buffer_depth_fragments: 1000
203  data_buffer_depth_mb: 1000
204 
205  request_port: 3001
206  request_address: "227.128.12.26" # Multicast request address
207 
208  request_window_offset: 0 # Request message contains tzero. Window will be from
209  tzero - offset to tzero + width request_window_width: 0 stale_request_timeout:
210  "0xFFFFFFFF" # How long to wait before discarding request messages that are outside
211  the available data request_windows_are_unique: true # If request windows are
212  unique, avoids a copy operation, but the same data point cannot be used for two
213  requests. If this is not anticipated, leave set to "true"
214 
215  separate_data_thread: false # MUST be true for triggers to be applied! If
216  triggering is not desired, but a separate readout thread is, set this to true,
217  triggers_enabled to false and trigger_mode to ignored. separate_monitoring_thread:
218  false # Whether a thread should be started which periodically calls checkHWStatus_,
219  a user-defined function which should be used to check hardware status registers and
220  report to MetricMan. poll_hardware_status: false # Whether checkHWStatus_ will be
221  called, either through the thread or at the start of getNext
222  hardware_poll_interval_us: 1000000 # If hardware monitoring thread is enabled,
223  how often should it call checkHWStatus_
224 
225 
226  # Generated Parameters:
227  generator: ToySimulator
228  fragment_type: TOY1
229  fragment_id: 0
230  board_id: 0
231  starting_fragment_id: 0
232  random_seed: 5780
233  sleep_on_stop_us: 500000
234 
235  # Generator-Specific Table:
236 
237  nADCcounts: 40
238 
239  throttle_usecs: 100000
240 
241  distribution_type: 1
242 
243  timestamp_scale_factor: 1
244 
245 
246  destinations: {
247  d2: { transferPluginType: MPI
248  destination_rank: 2
249  max_fragment_size_bytes: 2097152
250  host_map: [
251  {
252  host: "mu2edaq01.fnal.gov"
253  rank: 0
254  },
255  {
256  host: "mu2edaq01.fnal.gov"
257  rank: 1
258  }]
259  }
260  d3: { transferPluginType: MPI
261  destination_rank: 3
262  max_fragment_size_bytes: 2097152
263  host_map: [
264  {
265  host: "mu2edaq01.fnal.gov"
266  rank: 0
267  },
268  {
269  host: "mu2edaq01.fnal.gov"
270  rank: 1
271  }]
272  }
273 
274  }
275  }
276 
277  metrics: {
278  brFile: {
279  metricPluginType: "file"
280  level: 3
281  fileName: "/tmp/boardreader/br_%UID%_metrics.log"
282  uniquify: true
283  }
284  # ganglia: {
285  # metricPluginType: "ganglia"
286  # level: %{ganglia_level}
287  # reporting_interval: 15.0
288  #
289  # configFile: "/etc/ganglia/gmond.conf"
290  # group: "ARTDAQ"
291  # }
292  # msgfac: {
293  # level: %{mf_level}
294  # metricPluginType: "msgFacility"
295  # output_message_application_name: "ARTDAQ Metric"
296  # output_message_severity: 0
297  # }
298  # graphite: {
299  # level: %{graphite_level}
300  # metricPluginType: "graphite"
301  # host: "localhost"
302  # port: 20030
303  # namespace: "artdaq."
304  # }
305  }
306  }
307 
308  */
309 
310  std::string filename = getFHICLFilename(boardReaderNode);
311 
313  // generate xdaq run parameter file
314  std::fstream out;
315 
316  std::string tabStr = "";
317  std::string commentStr = "";
318 
319  out.open(filename, std::fstream::out | std::fstream::trunc);
320  if(out.fail())
321  {
322  __SS__ << "Failed to open ARTDAQ Builder fcl file: " << filename << __E__;
323  __SS_THROW__;
324  }
325 
326  //--------------------------------------
327  // header
328  OUT << "###########################################################" << __E__;
329  OUT << "#" << __E__;
330  OUT << "# artdaq reader fcl configuration file produced by otsdaq." << __E__;
331  OUT << "# Creation timestamp: " << StringMacros::getTimestampString() << __E__;
332  OUT << "# Original filename: " << filename << __E__;
333  OUT << "# otsdaq-ARTDAQ Reader UID: " << boardReaderNode.getValue() << __E__;
334  OUT << "#" << __E__;
335  OUT << "###########################################################" << __E__;
336  OUT << "\n\n";
337 
338  // no primary link to table tree for reader node!
339  try
340  {
341  if(boardReaderNode.isDisconnected())
342  {
343  // create empty fcl
344  OUT << "{}\n\n";
345  out.close();
346  return;
347  }
348  }
349  catch(const std::runtime_error)
350  {
351  __COUT__ << "Ignoring error, assume this a valid UID node." << __E__;
352  // error is expected here for UIDs.. so just ignore
353  // this check is valuable if source node is a unique-Link node, rather than UID
354  }
355 
356  //--------------------------------------
357  // handle daq
358  OUT << "daq: {\n";
359 
360  // fragment_receiver
361  PUSHTAB;
362  OUT << "fragment_receiver: {\n";
363 
364  PUSHTAB;
365  {
366  // plugin type and fragment data-type
367  OUT << "generator"
368  << ": " << boardReaderNode.getNode("daqGeneratorPluginType").getValue()
369  << ("\t #daq generator plug-in type") << "\n";
370  OUT << "fragment_type"
371  << ": " << boardReaderNode.getNode("daqGeneratorFragmentType").getValue()
372  << ("\t #generator data fragment type") << "\n\n";
373 
374  // shared and unique parameters
375  auto parametersLink = boardReaderNode.getNode("daqParametersLink");
376  if(!parametersLink.isDisconnected())
377  {
378  auto parameters = parametersLink.getChildren();
379  for(auto& parameter : parameters)
380  {
381  if(!parameter.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
382  .getValue<bool>())
383  PUSHCOMMENT;
384 
385  // __COUT__ <<
386  // parameter.second.getNode("daqParameterKey").getValue() <<
387  // ": " <<
388  // parameter.second.getNode("daqParameterValue").getValue()
389  // <<
390  // "\n";
391 
392  auto comment = parameter.second.getNode("CommentDescription");
393  OUT << parameter.second.getNode("daqParameterKey").getValue() << ": "
394  << parameter.second.getNode("daqParameterValue").getValue()
395  << (comment.isDefaultValue() ? "" : ("\t # " + comment.getValue()))
396  << "\n";
397 
398  if(!parameter.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
399  .getValue<bool>())
400  POPCOMMENT;
401  }
402  }
403  OUT << "\n"; // end daq board reader parameters
404  }
405  // { //unique parameters
406  // auto parametersLink = boardReaderNode.getNode("daqUniqueParametersLink");
407  // if(!parametersLink.isDisconnected())
408  // {
409  //
410  // auto parameters = parametersLink.getChildren();
411  // for(auto &parameter:parameters)
412  // {
413  // if(!parameter.second.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue<bool>())
414  // PUSHCOMMENT;
415  //
416  // auto comment = parameter.second.getNode("CommentDescription");
417  // OUT << parameter.second.getNode("daqParameterKey").getValue() <<
418  // ": " <<
419  // parameter.second.getNode("daqParameterValue").getValue()
420  // <<
421  // (comment.isDefaultValue()?"":("\t # " + comment.getValue()))
422  //<<
423  // "\n";
424  //
425  // if(!parameter.second.getNode(TableViewColumnInfo::COL_NAME_STATUS).getValue<bool>())
426  // POPCOMMENT;
427  // }
428  // }
429  // OUT << "\n"; //end shared daq board reader parameters
430  // }
431 
432  OUT << "destinations: {\n";
433 
434  PUSHTAB;
435  auto destinationsGroup = boardReaderNode.getNode("daqDestinationsLink");
436  if(!destinationsGroup.isDisconnected())
437  {
438  try
439  {
440  auto destinations = destinationsGroup.getChildren();
441  for(auto& destination : destinations)
442  {
443  auto destinationContextUID =
444  destination.second.getNode("destinationARTDAQContextLink")
445  .getValueAsString();
446 
447  unsigned int destinationRank =
448  contextConfig->getARTDAQAppRank(destinationContextUID);
449  std::string host =
450  contextConfig->getContextAddress(destinationContextUID);
451  unsigned int port = contextConfig->getARTDAQDataPort(
452  configManager, destinationContextUID);
453 
454  // open destination object
455  OUT << destination.second.getNode("destinationKey").getValue() << ": {\n";
456  PUSHTAB;
457 
458  OUT << "transferPluginType: "
459  << destination.second.getNode("transferPluginType").getValue()
460  << __E__;
461 
462  OUT << "destination_rank: " << destinationRank << __E__;
463 
464  try
465  {
466  OUT << "max_fragment_size_bytes: "
467  << destination.second
468  .getNode("ARTDAQGlobalTableLink/maxFragmentSizeBytes")
469  .getValue<unsigned long long>()
470  << __E__;
471  }
472  catch(...)
473  {
474  __SS__ << "The field ARTDAQGlobalTableLink/maxFragmentSizeBytes "
475  "could not be accessed. Make sure the link is valid."
476  << __E__;
477  __SS_THROW__;
478  }
479 
480  OUT << "host_map: [\n";
481  PUSHTAB;
482  OUT << "{\n";
483  PUSHTAB;
484  OUT << "rank: " << destinationRank << __E__;
485  OUT << "host: \"" << host << "\"" << __E__;
486  OUT << "portOffset: " << std::to_string(port) << __E__;
487  POPTAB;
488  OUT << "},\n";
489  OUT << "{\n";
490  PUSHTAB;
491  OUT << "rank: " << selfRank << __E__;
492  OUT << "host: \"" << selfHost << "\"" << __E__;
493  OUT << "portOffset: " << std::to_string(selfPort) << __E__;
494  POPTAB;
495  OUT << "}" << __E__;
496  POPTAB;
497  OUT << "]" << __E__; // close host_map
498 
499  POPTAB;
500  OUT << "}" << __E__; // close destination object
501  }
502  }
503  catch(const std::runtime_error& e)
504  {
505  __SS__ << "Are the DAQ destinations valid? Error occurred looking for Board "
506  "Reader DAQ sources for UID '"
507  << boardReaderNode.getValue() << "': " << e.what() << __E__;
508  __SS_THROW__;
509  }
510  }
511  POPTAB;
512  OUT << "}\n\n"; // end destinations
513 
514  POPTAB;
515  OUT << "}\n\n"; // end fragment_receiver
516 
517  OUT << "metrics: {\n";
518 
519  PUSHTAB;
520  auto metricsGroup = boardReaderNode.getNode("daqMetricsLink");
521  if(!metricsGroup.isDisconnected())
522  {
523  auto metrics = metricsGroup.getChildren();
524 
525  for(auto& metric : metrics)
526  {
527  if(!metric.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
528  .getValue<bool>())
529  PUSHCOMMENT;
530 
531  OUT << metric.second.getNode("metricKey").getValue() << ": {\n";
532  PUSHTAB;
533 
534  OUT << "metricPluginType: "
535  << metric.second.getNode("metricPluginType").getValue() << "\n";
536  OUT << "level: " << metric.second.getNode("metricLevel").getValue() << "\n";
537 
538  auto metricParametersGroup = metric.second.getNode("metricParametersLink");
539  if(!metricParametersGroup.isDisconnected())
540  {
541  auto metricParameters = metricParametersGroup.getChildren();
542  for(auto& metricParameter : metricParameters)
543  {
544  if(!metricParameter.second
545  .getNode(TableViewColumnInfo::COL_NAME_STATUS)
546  .getValue<bool>())
547  PUSHCOMMENT;
548 
549  OUT << metricParameter.second.getNode("metricParameterKey").getValue()
550  << ": "
551  << metricParameter.second.getNode("metricParameterValue")
552  .getValue()
553  << "\n";
554 
555  if(!metricParameter.second
556  .getNode(TableViewColumnInfo::COL_NAME_STATUS)
557  .getValue<bool>())
558  POPCOMMENT;
559  }
560  }
561  POPTAB;
562  OUT << "}\n\n"; // end metric
563 
564  if(!metric.second.getNode(TableViewColumnInfo::COL_NAME_STATUS)
565  .getValue<bool>())
566  POPCOMMENT;
567  }
568  }
569  POPTAB;
570  OUT << "}\n\n"; // end metrics
571 
572  POPTAB;
573  OUT << "}\n\n"; // end daq
574 
575  out.close();
576 }
577 
578 DEFINE_OTS_TABLE(ARTDAQBoardReaderTable)