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