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