otsdaq  3.06.00
ARTDAQTableBase.cc
1 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h"
2 
3 #include <dirent.h> //DIR and dirent
4 #include <fstream> // for std::ofstream
5 #include <iostream> // std::cout
6 #include <typeinfo>
7 
8 #include "otsdaq/Macros/CoutMacros.h"
9 #define TRACE_NAME "ARTDAQTableBase"
10 
11 #include <fhiclcpp/ParameterSet.h>
12 #include <fhiclcpp/detail/print_mode.h>
13 #include <fhiclcpp/intermediate_table.h>
14 #include <fhiclcpp/parse.h>
15 
16 #include "otsdaq/ProgressBar/ProgressBar.h"
17 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h"
18 
19 using namespace ots;
20 
21 #undef __MF_SUBJECT__
22 #define __MF_SUBJECT__ "ARTDAQTableBase"
23 
24 // clang-format off
25 
26 #define FCL_COMMENT_POSITION 65
27 #define TABSZ 4
28 
30 #define OUTCF(X,C,F) { std::stringstream outSs; outSs << X; addCommentWhitespace(outSs, tabStr.size()*TABSZ + commentStr.size() + outSs.str().size()); outSs << (C) << (std::string(C).size()?" - ":"") << "from config-tree: " << parentPath << (std::string(F).size()?(std::string("/") + std::string(F)):std::string("")) << "\n"; OUT << outSs.str();}
32 #define OUTC(X,C) OUTCF(X,C,"")
34 #define OUTCLF(X,C,F) { std::stringstream outSs; outSs << X; addCommentWhitespace(outSs, tabStr.size()*TABSZ + commentStr.size() + outSs.str().size()); outSs << (C) << (std::string(C).size()?" - ":"") << "from config-tree: " << localParentPath << std::string(std::string(F).size()?("/" + std::string(F)):std::string("")) << "\n"; OUT << outSs.str();}
36 #define OUTCL(X,C) OUTCLF(X,C,"")
38 #define OUTCL2F(X,C,F) { std::stringstream outSs; outSs << X; addCommentWhitespace(outSs, tabStr.size()*TABSZ + commentStr.size() + outSs.str().size()); outSs << (C) << (std::string(C).size()?" - ":"") << "from config-tree: " << localParentPath2 << (std::string(F).size()?(std::string("/") + std::string(F)):std::string("")) << "\n"; OUT << outSs.str();}
40 #define OUTCL2(X,C) OUTCL2F(X,C,"")
42 
43 
44 const std::string ARTDAQTableBase::ARTDAQ_FCL_PATH = std::string(__ENV__("USER_DATA")) + "/" + "ARTDAQConfigurations/";
45 const std::string ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH =
46  (((getenv("SERVICE_DATA_PATH") == NULL)
47  ? (std::string(getenv("USER_DATA")) + "/ServiceData")
48  : std::string(getenv("SERVICE_DATA_PATH")))) +
49  "/ConfigurationGUI_artdaqLayouts/";
50 const bool ARTDAQTableBase::ARTDAQ_DONOTWRITE_FCL = ((getenv("OTS_FCL_DONOTWRITE") == NULL) ? false : true);
51 
52 const std::string ARTDAQTableBase::ARTDAQ_SUPERVISOR_CLASS = "ots::ARTDAQSupervisor";
53 const std::string ARTDAQTableBase::ARTDAQ_SUPERVISOR_TABLE = "ARTDAQSupervisorTable";
54 
55 const std::string ARTDAQTableBase::ARTDAQ_READER_TABLE = "ARTDAQBoardReaderTable";
56 const std::string ARTDAQTableBase::ARTDAQ_BUILDER_TABLE = "ARTDAQEventBuilderTable";
57 const std::string ARTDAQTableBase::ARTDAQ_LOGGER_TABLE = "ARTDAQDataLoggerTable";
58 const std::string ARTDAQTableBase::ARTDAQ_DISPATCHER_TABLE = "ARTDAQDispatcherTable";
59 const std::string ARTDAQTableBase::ARTDAQ_MONITOR_TABLE = "ARTDAQMonitorTable";
60 const std::string ARTDAQTableBase::ARTDAQ_ROUTER_TABLE = "ARTDAQRoutingManagerTable";
61 
62 const std::string ARTDAQTableBase::ARTDAQ_SUBSYSTEM_TABLE = "ARTDAQSubsystemTable";
63 const std::string ARTDAQTableBase::ARTDAQ_DAQ_TABLE = "ARTDAQDaqTable";
64 const std::string ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE = "ARTDAQDaqParameterTable";
65 const std::string ARTDAQTableBase::ARTDAQ_ART_TABLE = "ARTDAQArtTable";
66 
67 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME = "ExecutionHostname";
68 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS = "AllowedProcessors";
69 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK = "SubsystemLink";
70 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID = "SubsystemLinkUID";
71 
72 
73 const int ARTDAQTableBase::NULL_SUBSYSTEM_DESTINATION = 0;
74 const std::string ARTDAQTableBase::NULL_SUBSYSTEM_DESTINATION_LABEL = "nullDestinationSubsystem";
75 
76 ARTDAQTableBase::ARTDAQInfo ARTDAQTableBase::info_;
77 
78 ARTDAQTableBase::ColARTDAQSupervisor ARTDAQTableBase::colARTDAQSupervisor_;
79 ARTDAQTableBase::ColARTDAQSubsystem ARTDAQTableBase::colARTDAQSubsystem_;
80 ARTDAQTableBase::ColARTDAQReader ARTDAQTableBase::colARTDAQReader_;
81 ARTDAQTableBase::ColARTDAQNotReader ARTDAQTableBase::colARTDAQNotReader_;
82 ARTDAQTableBase::ColARTDAQDaq ARTDAQTableBase::colARTDAQDaq_;
83 ARTDAQTableBase::ColARTDAQDaqParameter ARTDAQTableBase::colARTDAQDaqParameter_;
84 ARTDAQTableBase::ColARTDAQArt ARTDAQTableBase::colARTDAQArt_;
85 
88 
89 // clang-format on
90 
91 //==============================================================================
97 ARTDAQTableBase::ARTDAQTableBase(std::string tableName,
98  std::string* accumulatedExceptions /* =0 */)
99  : TableBase(tableName, accumulatedExceptions)
100 {
101  // make directory just in case
102  mkdir((ARTDAQ_FCL_PATH).c_str(), 0755);
103 
104  // December 2021 started seeing an issue where traceTID is found to be cleared to 0
105  // which crashes TRACE if __COUT__ is used in a Table plugin constructor
106  // This check and re-initialization seems to cover up the issue for now.
107  // Why it is cleared to 0 after the constructor sets it to -1 is still unknown.
108  // Note: it seems to only happen on the first alphabetially ARTDAQ Configure Table plugin.
109  if(traceTID == 0)
110  {
111  std::cout << "ARTDAQTableBase Before traceTID=" << traceTID << __E__;
112  char buf[40];
113  traceInit(trace_name(TRACE_NAME, __TRACE_FILE__, buf, sizeof(buf)), 0);
114  std::cout << "ARTDAQTableBase After traceTID=" << traceTID << __E__;
115  __COUT__ << "ARTDAQTableBase TRACE reinit and Constructed." << __E__;
116  }
117 
118 } // end constuctor()
119 
120 //==============================================================================
123 ARTDAQTableBase::ARTDAQTableBase(void) : TableBase("ARTDAQTableBase")
124 {
125  __SS__ << "Should not call void constructor, table type is lost!" << __E__;
126  __SS_THROW__;
127 } // end illegal default constructor()
128 
129 //==============================================================================
130 ARTDAQTableBase::~ARTDAQTableBase(void) {} // end destructor()
131 
132 //==============================================================================
133 bool ARTDAQTableBase::doGenFiles(ConfigurationManager* configManager)
134 {
135  // use isFirstAppInContext to only run once per context, for example to avoid
136  // generating files on local disk multiple times.
138 
139  __COUTVS__(4, isFirstAppInContext_);
141  return false;
142 
143  //if artdaq supervisor is disabled, skip fcl handling
144  if(!ARTDAQTableBase::isARTDAQEnabled(configManager))
145  {
146  __COUT_INFO__ << "ARTDAQ Supervisor is disabled, so skipping fcl handling."
147  << __E__;
148  return false;
149  }
150 
151  //allow any table with artdaq prerequisites to init!
152  configManager->initPrereqsForARTDAQ();
153 
154  // make directory just in case
155  mkdir((ARTDAQTableBase::ARTDAQ_FCL_PATH).c_str(), 0755);
156 
157  return true;
158 } // end doGenFiles()
159 
160 //==============================================================================
161 const std::string& ARTDAQTableBase::getTypeString(ARTDAQAppType type)
162 {
163  switch(type)
164  {
165  case ARTDAQAppType::EventBuilder:
166  return processTypes_.BUILDER;
167  case ARTDAQAppType::DataLogger:
168  return processTypes_.LOGGER;
169  case ARTDAQAppType::Dispatcher:
170  return processTypes_.DISPATCHER;
171  case ARTDAQAppType::BoardReader:
172  return processTypes_.READER;
173  case ARTDAQAppType::Monitor:
174  return processTypes_.MONITOR;
175  case ARTDAQAppType::RoutingManager:
176  return processTypes_.ROUTER;
177  }
178  // return "UNKNOWN";
179  __SS__ << "Illegal translation attempt for type '" << (unsigned int)type << "'"
180  << __E__;
181  __SS_THROW__;
182 } // end getTypeString()
183 
184 //==============================================================================
185 std::string ARTDAQTableBase::getFHICLFilename(ARTDAQAppType type, const std::string& name)
186 {
187  //__COUT__ << "Type: " << getTypeString(type) << " Name: " << name
188  //<< __E__;
189  std::string filename = ARTDAQ_FCL_PATH + getTypeString(type) + "-";
190  std::string uid = name;
191  for(unsigned int i = 0; i < uid.size(); ++i)
192  if((uid[i] >= 'a' && uid[i] <= 'z') || (uid[i] >= 'A' && uid[i] <= 'Z') ||
193  (uid[i] >= '0' && uid[i] <= '9')) // only allow alpha numeric in file name
194  filename += uid[i];
195 
196  filename += ".fcl";
197 
198  //__COUT__ << "fcl: " << filename << __E__;
199 
200  return filename;
201 } // end getFHICLFilename()
202 
203 //==============================================================================
204 std::string ARTDAQTableBase::getFlatFHICLFilename(ARTDAQAppType type,
205  const std::string& name)
206 {
207  //__COUT__ << "Type: " << getTypeString(type) << " Name: " << name
208  // << __E__;
209  std::string filename = ARTDAQ_FCL_PATH + getTypeString(type) + "-";
210  std::string uid = name;
211  for(unsigned int i = 0; i < uid.size(); ++i)
212  if((uid[i] >= 'a' && uid[i] <= 'z') || (uid[i] >= 'A' && uid[i] <= 'Z') ||
213  (uid[i] >= '0' && uid[i] <= '9')) // only allow alpha numeric in file name
214  filename += uid[i];
215 
216  filename += "_flattened.fcl";
217 
218  //__COUT__ << "fcl: " << filename << __E__;
219 
220  return filename;
221 } // end getFlatFHICLFilename()
222 
223 //==============================================================================
224 void ARTDAQTableBase::flattenFHICL(ARTDAQAppType type,
225  const std::string& name,
226  std::string* returnFcl /* = nullptr */)
227 {
228  std::chrono::steady_clock::time_point startClock = std::chrono::steady_clock::now();
229  __COUTS__(3) << "flattenFHICL()" << __ENV__("FHICL_FILE_PATH") << __E__;
230  __COUTVS__(4, StringMacros::stackTrace());
231  //return;
232 
233  std::string inFile = getFHICLFilename(type, name);
234  std::string outFile = getFlatFHICLFilename(type, name);
235 
236  __COUTVS__(3, inFile);
237  __COUTVS__(3, outFile);
238 
239  cet::filepath_lookup_nonabsolute policy("FHICL_FILE_PATH");
240  fhicl::ParameterSet pset;
241 
242  try
243  {
244  __COUT_INFO__ << "parsing document: " << inFile;
245  // tbl = fhicl::parse_document(inFile, policy);
246  // pset = fhicl::ParameterSet::make(tbl);
247  pset = fhicl::ParameterSet::make(inFile, policy);
248  __COUTT__ << "document: " << inFile << " parsed";
249  __COUTT__ << "got pset from table:";
250 
251  std::ofstream ofs{outFile};
252  if(!ofs)
253  {
254  __SS__ << "Failed to open fhicl output file '" << outFile << "!'" << __E__;
255  __SS_THROW__;
256  }
257  std::ostringstream out;
258  out << pset.to_indented_string(
259  0); // , fhicl::detail::print_mode::annotated); // Only really useful for debugging
260  if(returnFcl)
261  {
262  *returnFcl = out.str();
263  __COUTVS__(21, returnFcl);
264  }
265  ofs << out.str();
266  }
267  catch(cet::exception const& e)
268  {
269  __SS__ << "Failed to parse fhicl into output file '" << outFile
270  << "' - here is the error: " << e.what() << __E__;
271 
272  //add additional user helper information, based on error keywords
273  if(std::string(e.what()).find("TriggerEpilogs") != std::string::npos)
274  ss << "\n\n"
275  << "The Trigger Epilogs are located at "
276  "$USER_DATA/TriggerConfigurations/TriggerEpilogs. "
277  << "Please check that the Trigger Epilogs were properly generated, or "
278  "copy them from a previously working area."
279  << __E__;
280  __SS_THROW__;
281  }
282 
283  __COUTT__ << name
284  << " Flatten Clock time = " << artdaq::TimeUtils::GetElapsedTime(startClock)
285  << __E__;
286 } // end flattenFHICL()
287 
288 //==============================================================================
295 void ARTDAQTableBase::insertParameters(std::ostream& out,
296  std::string& tabStr,
297  std::string& commentStr,
298  const std::string& parentPath,
299  ConfigurationTree parameterGroupLink,
300  const std::string& parameterPreamble,
301  bool onlyInsertAtTableParameters /*=false*/,
302  bool includeAtTableParameters /*=false*/)
303 {
304  // skip if link is disconnected
305  if(!parameterGroupLink.isDisconnected())
306  {
308  auto otherParameters = parameterGroupLink.getChildren();
309 
310  std::string key;
311  if(TTEST(3))
312  {
313  __COUTVS__(3, otherParameters.size());
314  __COUTVS__(3, onlyInsertAtTableParameters);
315  __COUTVS__(3, includeAtTableParameters);
316  }
317  size_t paramCount = 0;
318  for(auto& parameter : otherParameters)
319  {
320  key = parameter.second.getNode(parameterPreamble + "Key").getValue();
321 
322  std::string localParentPath =
323  parentPath + "/" + parameterGroupLink.getParentLinkColumnName() + ":" +
324  parameter.second.getTableName() + ":" +
325  parameterGroupLink.getParentLinkIndex() + ":" +
326  parameterGroupLink.getParentLinkID() + "/" + parameter.second.getValue();
327 
328  // handle special keyword @table:: (which imports full tables, usually as
329  // defaults)
330  if(key.find("@table::") != std::string::npos)
331  {
332  // include @table::
333  if(onlyInsertAtTableParameters || includeAtTableParameters)
334  {
335  ++paramCount;
336  if(!parameter.second.status())
337  PUSHCOMMENT;
338 
339  __COUTT__ << "Inserting parameter... " << localParentPath << __E__;
340 
341  // skip connecting : if special keywords found
342  OUTCL(key << parameter.second.getNode(parameterPreamble + "Value")
343  .getValue(),
344  parameter.second.hasComment() ? parameter.second.getComment()
345  : "");
346 
347  if(!parameter.second.status())
348  POPCOMMENT;
349  }
350  // else skip it
351 
352  continue;
353  }
354  // else NOT @table:: keyword parameter
355 
356  if(onlyInsertAtTableParameters)
357  continue; // skip all other types
358 
359  ++paramCount;
360  if(!parameter.second.status())
361  PUSHCOMMENT;
362 
363  __COUTT__ << "Inserting parameter... " << localParentPath << __E__;
364 
365  // skip connecting : if special keywords found
366  if(key.size() && key.find("#include") == std::string::npos)
367  {
368  //normal key / value pair ? or is it a value like @@<table> -> getFclValueForARTDAQ()
369 
370  std::string value =
371  parameter.second.getNode(parameterPreamble + "Value").getValue();
372  StringMacros::trim(value); //trim whitespace
373 
374  if(value.size() > 2 && value[0] == '@' && value[1] == '@')
375  {
376  __COUTT__
377  << "Checking for getFclValueForARTDAQ @@ indicator from value = "
378  << value << __E__;
379  std::string potentialTable = value.substr(2);
380  __COUTTV__(potentialTable);
381  try
382  {
383  auto cfgMgr = parameterGroupLink.getConfigurationManager();
384  value = cfgMgr->getTableByName(potentialTable)
385  ->getFclValueForARTDAQ(cfgMgr, key);
386  }
387  catch(const std::runtime_error& e)
388  {
389  __SS__ << "getFclValueForARTDAQ @@ indicator from value = "
390  << value << " corresponds to table '" << potentialTable
391  << "'... however fcl value failed to load: " << e.what();
392  __SS_THROW__;
393  }
394 
395  __COUTT__ << "Value from getFclValueForARTDAQ: value = " << value
396  << __E__;
397 
398  std::string localParentPath2 = "/" + potentialTable;
399  OUTCL2(key << ": " << value,
400  parameter.second.hasComment() ? parameter.second.getComment()
401  : "");
402  }
403  else //normal key / value pair
404  {
405  OUTCL(key << ": "
406  << parameter.second.getNode(parameterPreamble + "Value")
407  .getValue(),
408  parameter.second.hasComment() ? parameter.second.getComment()
409  : "");
410  }
411  }
412  else if(key == "")
413  {
414  OUTCL(parameter.second.getNode(parameterPreamble + "Value").getValue(),
415  parameter.second.hasComment() ? parameter.second.getComment() : "");
416  }
417  else //#include can not have a comment at end of line, so do before!
418  {
419  OUTCL("# comment for #include below:",
420  parameter.second.hasComment() ? parameter.second.getComment() : "");
421  OUT << key
422  << parameter.second.getNode(parameterPreamble + "Value").getValue()
423  << "\n";
424  }
425 
426  if(!parameter.second.status())
427  POPCOMMENT;
428  }
429 
430  if(!paramCount)
431  {
432  __COUTS__(3) << "Empty parameter set found onlyInsertAtTableParameters="
433  << onlyInsertAtTableParameters << __E__;
434  std::string localParentPath =
435  parentPath + "/" + parameterGroupLink.getParentLinkColumnName();
436 
437  if(onlyInsertAtTableParameters)
438  {
439  OUTCL("# no @table parameters found", "" /* comment*/);
440  }
441  else
442  {
443  OUTCL("# empty parameter set found", "" /* comment*/);
444  }
445  }
446  }
447  else
448  {
449  __COUTS__(3) << "No parameters found" << __E__;
450  std::string localParentPath =
451  parentPath + "/" + parameterGroupLink.getParentLinkColumnName();
452  OUTCL("# no parameters inserted", "" /* comment*/);
453  }
454 
455 } // end insertParameters()
456 
457 //==============================================================================
460 std::string ARTDAQTableBase::insertModuleType(std::ostream& out,
461  std::string& tabStr,
462  std::string& commentStr,
463  const std::string& parentPath,
464  ConfigurationTree moduleTypeNode)
465 {
466  std::string value = moduleTypeNode.getValue();
467  __COUTTV__(parentPath);
468  OUTCF((value.find("@table::") == std::string::npos ? "module_type: " : "") << value,
469  "" /* comment */,
470  moduleTypeNode.getFieldName());
471  return value;
472 } // end insertModuleType()
473 
474 //==============================================================================
476 void ARTDAQTableBase::insertMetricsBlock(std::ostream& out,
477  std::string& tabStr,
478  std::string& commentStr,
479  const std::string& parentPath,
480  ConfigurationTree daqNode)
481 {
482  auto metricsGroup = daqNode.getNode("daqMetricsLink");
483 
484  out << "\n";
485  OUTCF("metrics: {", "", metricsGroup.getParentLinkColumnName());
486  PUSHTAB;
487  if(!metricsGroup.isDisconnected())
488  {
489  auto metrics = metricsGroup.getChildren();
490  bool sendSystemMetrics(false), sendProcessMetrics(false);
491  for(auto& metric : metrics)
492  {
493  if(!metric.second.status())
494  PUSHCOMMENT;
495 
496  __COUTT__ << "Inserting metric... " << parentPath << __E__;
497  std::string localParentPath =
498  parentPath + "/" + metricsGroup.getParentLinkColumnName() + ":" +
499  metric.second.getTableName() + ":" + metricsGroup.getParentLinkIndex() +
500  ":" + metricsGroup.getParentLinkID() + "/" + metric.second.getValue();
501  __COUTT__ << "Inserting metric... " << localParentPath << __E__;
502 
503  OUTCL(metric.second.getNode("metricKey").getValue() << ": {",
504  metric.second.hasComment() ? metric.second.getComment() : "");
505  PUSHTAB;
506 
507  if(metric.second.getNode("sendSystemMetrics").getValue<bool>())
508  {
509  sendSystemMetrics = true;
510  }
511  if(metric.second.getNode("sendProcessMetrics").getValue<bool>())
512  {
513  sendProcessMetrics = true;
514  }
515 
516  OUTCLF("metricPluginType: "
517  << metric.second.getNode("metricPluginType").getValue(),
518  "" /* comment */,
519  "metricPluginType");
520  OUTCLF(
521  "level_string: " << metric.second.getNode("metricLevelString").getValue(),
522  "" /* comment */,
523  "metricLevelString");
524 
525  auto metricParametersGroup = metric.second.getNode("metricParametersLink");
526  if(!metricParametersGroup.isDisconnected())
527  {
528  auto metricParameters = metricParametersGroup.getChildren();
529  for(auto& metricParameter : metricParameters)
530  {
531  if(!metricParameter.second.status())
532  PUSHCOMMENT;
533 
534  __COUTT__ << "Inserting metric... " << localParentPath << __E__;
535  std::string localParentPath2 =
536  localParentPath + "/" +
537  metricParametersGroup.getParentLinkColumnName() + ":" +
538  metricParameter.second.getTableName() + ":" +
539  metricParametersGroup.getParentLinkIndex() + ":" +
540  metricParametersGroup.getParentLinkID() + "/" +
541  metricParameter.second.getValue();
542  __COUTT__ << "Inserting metric... " << localParentPath2 << __E__;
543  OUTCL2(metricParameter.second.getNode("metricParameterKey").getValue()
544  << ": "
545  << metricParameter.second.getNode("metricParameterValue")
546  .getValue(),
547  metricParameter.second.hasComment()
548  ? metricParameter.second.getComment()
549  : "");
550 
551  if(!metricParameter.second.status())
552  POPCOMMENT;
553  }
554  }
555  POPTAB;
556  OUT << "} # end " << metric.second.getNode("metricKey").getValue()
557  << "\n\n"; // end metric
558 
559  if(!metric.second.status())
560  POPCOMMENT;
561  } //end metricsGroup children loop
562 
563  __COUTT__ << "Inserting metric send... " << parentPath << __E__;
564  std::string localParentPath =
565  parentPath + "/" + metricsGroup.getParentLinkColumnName() + ":" +
566  metricsGroup.getTableName() + ":" + metricsGroup.getParentLinkIndex() + ":" +
567  metricsGroup.getParentLinkID();
568  if(sendSystemMetrics)
569  {
570  __COUTT__ << "Inserting send_system_metrics... " << localParentPath << __E__;
571  OUTCLF("send_system_metrics: true ",
572  "true, if any children are true",
573  "*/sendSystemMetrics");
574  }
575  else
576  OUTCLF("# send_system_metrics: false ",
577  "true, if any children are true",
578  "*/sendSystemMetrics");
579 
580  if(sendProcessMetrics)
581  {
582  __COUTT__ << "Inserting send_process_metrics... " << localParentPath << __E__;
583  OUTCLF("send_process_metrics: true ",
584  "true, if any children are true",
585  "*/sendProcessMetrics");
586  }
587  else
588  OUTCLF("# send_process_metrics: false ",
589  "true, if any children are true",
590  "*/sendProcessMetrics");
591  } //end connected daq metrics link handling
592  else
593  {
594  __COUTS__(3) << "No metrics found" << __E__;
595  std::string localParentPath =
596  parentPath + "/" + metricsGroup.getParentLinkColumnName();
597  OUTCL("# no metrics found", "" /* comment*/);
598  }
599 
600  POPTAB;
601  OUT << "} # end metrics\n\n"; // end metrics
602 } // end insertMetricsBlock()
603 
604 //==============================================================================
605 void ARTDAQTableBase::outputBoardReaderFHICL(
606  const ConfigurationTree& boardReaderNode,
607  size_t /*maxFragmentSizeBytes */ /* = DEFAULT_MAX_FRAGMENT_SIZE */,
608  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
609  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */)
610 {
611  if(ARTDAQ_DONOTWRITE_FCL)
612  {
613  __COUT__ << "Skipping fcl generation." << __E__;
614  return;
615  }
616 
617  /*
618  the file will look something like this:
619 
620  daq: {
621  fragment_receiver: {
622  mpi_sync_interval: 50
623 
624  # CommandableFragmentGenerator Table:
625  fragment_ids: []
626  fragment_id: -99 # Please define only one of these
627 
628  sleep_on_stop_us: 0
629 
630  requests_enabled: false # Whether to set up the socket for listening for
631  trigger messages request_mode: "Ignored" # Possible values are: Ignored, Single,
632  Buffer, Window
633 
634  data_buffer_depth_fragments: 1000
635  data_buffer_depth_mb: 1000
636 
637  request_port: 3001
638  request_address: "227.128.12.26" # Multicast request address
639 
640  request_window_offset: 0 # Request message contains tzero. Window will be from
641  tzero - offset to tzero + width request_window_width: 0 stale_request_timeout:
642  "0xFFFFFFFF" # How long to wait before discarding request messages that are outside
643  the available data request_windows_are_unique: true # If request windows are
644  unique, avoids a copy operation, but the same data point cannot be used for two
645  requests. If this is not anticipated, leave set to "true"
646 
647  separate_data_thread: false # MUST be true for triggers to be applied! If
648  triggering is not desired, but a separate readout thread is, set this to true,
649  triggers_enabled to false and trigger_mode to ignored. separate_monitoring_thread:
650  false # Whether a thread should be started which periodically calls checkHWStatus_,
651  a user-defined function which should be used to check hardware status registers and
652  report to MetricMan. poll_hardware_status: false # Whether checkHWStatus_ will be
653  called, either through the thread or at the start of getNext
654  hardware_poll_interval_us: 1000000 # If hardware monitoring thread is enabled,
655  how often should it call checkHWStatus_
656 
657 
658  # Generated Parameters:
659  generator: ToySimulator
660  fragment_type: TOY1
661  fragment_id: 0
662  board_id: 0
663  starting_fragment_id: 0
664  random_seed: 5780
665  sleep_on_stop_us: 500000
666 
667  # Generator-Specific Table:
668 
669  nADCcounts: 40
670 
671  throttle_usecs: 100000
672 
673  distribution_type: 1
674 
675  timestamp_scale_factor: 1
676 
677 
678  destinations: {
679  d2: { transferPluginType: MPI
680  destination_rank: 2
681  max_fragment_size_bytes: 2097152
682  host_map: [
683  {
684  host: "mu2edaq01.fnal.gov"
685  rank: 0
686  },
687  {
688  host: "mu2edaq01.fnal.gov"
689  rank: 1
690  }]
691  }
692  d3: { transferPluginType: MPI
693  destination_rank: 3
694  max_fragment_size_bytes: 2097152
695  host_map: [
696  {
697  host: "mu2edaq01.fnal.gov"
698  rank: 0
699  },
700  {
701  host: "mu2edaq01.fnal.gov"
702  rank: 1
703  }]
704  }
705 
706  }
707  }
708 
709  metrics: {
710  brFile: {
711  metricPluginType: "file"
712  level: 3
713  fileName: "/tmp/boardreader/br_%UID%_metrics.log"
714  uniquify: true
715  }
716  # ganglia: {
717  # metricPluginType: "ganglia"
718  # level: %{ganglia_level}
719  # reporting_interval: 15.0
720  #
721  # configFile: "/etc/ganglia/gmond.conf"
722  # group: "ARTDAQ"
723  # }
724  # msgfac: {
725  # level: %{mf_level}
726  # metricPluginType: "msgFacility"
727  # output_message_application_name: "ARTDAQ Metric"
728  # output_message_severity: 0
729  # }
730  # graphite: {
731  # level: %{graphite_level}
732  # metricPluginType: "graphite"
733  # host: "localhost"
734  # port: 20030
735  # namespace: "artdaq."
736  # }
737  }
738  }
739 
740  */
741 
742  std::string filename =
743  getFHICLFilename(ARTDAQAppType::BoardReader, boardReaderNode.getValue());
744 
746  // generate xdaq run parameter file
747  std::fstream out;
748 
749  std::string tabStr = "";
750  std::string commentStr = "";
751 
752  __COUTV__(filename);
753  out.open(filename, std::fstream::out | std::fstream::trunc);
754  if(out.fail())
755  {
756  __SS__ << "Failed to open ARTDAQ BoardReader fcl file: " << filename << __E__;
757  __SS_THROW__;
758  }
759 
760  try //catch and give error in fcl file if issue!
761  {
762  //--------------------------------------
763  // header
764  OUT << "###########################################################" << __E__;
765  OUT << "#" << __E__;
766  OUT << "# artdaq " << getTypeString(ARTDAQAppType::BoardReader)
767  << " fcl configuration file produced by otsdaq." << __E__;
768  OUT << "# Creation time: \t" << StringMacros::getTimestampString()
769  << __E__;
770  OUT << "# Original filename: \t" << filename << __E__;
771  OUT << "# otsdaq-ARTDAQ " << getTypeString(ARTDAQAppType::BoardReader)
772  << " UID:\t" << boardReaderNode.getValue() << __E__;
773  OUT << "#" << __E__;
774  OUT << "###########################################################" << __E__;
775  OUT << "\n\n";
776 
777  // no primary link to table tree for reader node!
778  try
779  {
780  if(boardReaderNode.isDisconnected())
781  {
782  // create empty fcl
783  OUT << "{}\n\n";
784  out.close();
785  return;
786  }
787  }
788  catch(const std::runtime_error&)
789  {
790  __COUTT__ << "Ignoring error, assume this a valid UID node." << __E__;
791  // error is expected here for UIDs.. so just ignore
792  // this check is valuable if source node is a unique-Link node, rather than UID
793  }
794 
795  std::string parentPath =
796  boardReaderNode.getTableName() + "/" + boardReaderNode.getValue();
797 
798  OUTC("# start of " << getTypeString(ARTDAQAppType::BoardReader) << " '"
799  << boardReaderNode.getValue() << "' fcl",
800  "" /* comment */);
801 
802  //--------------------------------------
803  // handle preamble parameters
804  __COUTT__ << "Inserting " << getTypeString(ARTDAQAppType::BoardReader)
805  << " preamble parameters... " << parentPath << __E__;
806  out << "\n";
807  insertParameters(out,
808  tabStr,
809  commentStr,
810  parentPath,
811  boardReaderNode.getNode("preambleParametersLink"),
812  "daqParameter" /*parameterType*/,
813  false /*onlyInsertAtTableParameters*/,
814  true /*includeAtTableParameters*/);
815 
816  //--------------------------------------
817  // handle daq
818  __COUTT__ << "Generating daq block..." << __E__;
819  out << "\n";
820  OUTC("daq: {", "" /* comment */);
821  PUSHTAB;
822 
823  // fragment_receiver
824  out << "\n";
825  OUT << "fragment_receiver: {\n";
826  PUSHTAB;
827  {
828  // plugin type and fragment data-type
829  OUTCF("generator"
830  << ": "
831  << boardReaderNode.getNode("daqGeneratorPluginType").getValue(),
832  "daq generator plug-in type" /* comment */,
833  "daqGeneratorPluginType" /* field*/);
834  OUTCF("fragment_type"
835  << ": "
836  << boardReaderNode.getNode("daqGeneratorFragmentType").getValue(),
837  "generator data fragment type" /* comment */,
838  "daqGeneratorFragmentType" /* field*/);
839 
840  __COUTT__ << "Inserting " << getTypeString(ARTDAQAppType::BoardReader)
841  << " DAQ Parameters... " << parentPath << __E__;
842  // shared and unique parameters
843  insertParameters(out,
844  tabStr,
845  commentStr,
846  parentPath,
847  boardReaderNode.getNode("daqParametersLink"),
848  "daqParameter");
849 
850  try //try to get daqFragmentId
851  {
852  auto fragmentId = boardReaderNode.getNode("daqFragmentIDs");
853  std::string value = fragmentId.getValue();
854  if(value.size() < 2 || value[0] != '[' || value[value.size() - 1] != ']')
855  {
856  __SS__ << "Invalid 'daqFragmentIDs' - the value must be a valid fcl "
857  "array with starting and ending square brackets: [ ]"
858  << __E__;
859  __SS_THROW__;
860  }
861  __COUTS__(20) << "fragment_ids: " << fragmentId.getValue() << __E__;
862  OUTCF("fragment_ids: " << fragmentId.getValue(),
863  "" /* comment */,
864  "daqFragmentIDs");
865  }
866  catch(...)
867  {
868  __COUT__
869  << "Ignoring missing fragment_id column associated with Board Reader."
870  << __E__;
871 
872  OUTCF("# fragment_ids not specified, but could be", "", "daqFragmentIDs");
873  }
874 
875  OUT << "\n"; // end daq board reader parameters
876  }
877 
878  OUT << "destinations: { # empty placeholder, '"
879  << getTypeString(ARTDAQAppType::BoardReader)
880  << "' destinations handled by artdaq interface\n";
881  OUT << "}\n\n"; // end destinations
882 
883  OUT << "routing_table_config: {\n";
884  PUSHTAB;
885 
886  auto readerSubsystemID = 1;
887  auto readerSubsystemLink = boardReaderNode.getNode("SubsystemLink");
888  if(!readerSubsystemLink.isDisconnected())
889  {
890  readerSubsystemID = getSubsytemId(readerSubsystemLink);
891  }
892  if(info_.subsystems[readerSubsystemID].hasRoutingManager)
893  {
894  std::string localParentPath =
895  parentPath + "/" + readerSubsystemLink.getParentLinkColumnName() + ":" +
896  readerSubsystemLink.getTableName() + "/" + readerSubsystemLink.getValue();
897  __COUTT__ << "Inserting routing manager... " << localParentPath << __E__;
898  OUTCL("use_routing_manager: true",
899  "auto-generated because subsystem '" +
900  std::to_string(readerSubsystemID) + "' has Routing Manager added");
901 
902  OUTCLF("routing_manager_hostname: \""
903  << info_.subsystems[readerSubsystemID].routingManagerHost << "\"",
904  "" /* comment */,
905  ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME);
906  OUT << "table_update_port: 0\n";
907  OUT << "table_update_address: \"0.0.0.0\"\n";
908  OUT << "table_update_multicast_interface: \"0.0.0.0\"\n";
909  OUT << "table_acknowledge_port : 0\n";
910  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
911  OUT << "routing_retry_count: " << routingRetryCount << "\n";
912  }
913  else
914  {
915  OUTCF("use_routing_manager: false",
916  "auto-generated if subsystem '" + std::to_string(readerSubsystemID) +
917  "' has Routing Manager added",
918  readerSubsystemLink.getParentLinkColumnName());
919  }
920 
921  POPTAB;
922  OUT << "}\n"; // end routing_table_config
923 
924  POPTAB;
925  OUT << "} # end fragment_receiver\n"; // end fragment_receiver
926 
927  insertMetricsBlock(OUT, tabStr, commentStr, parentPath, boardReaderNode);
928 
929  POPTAB;
930  OUT << "} # end daq\n\n"; // end daq
931 
932  //--------------------------------------
933  // handle ALL add-on parameters
934  parentPath = boardReaderNode.getTableName() + "/" + boardReaderNode.getValue();
935  __COUTT__ << "Inserting " << getTypeString(ARTDAQAppType::BoardReader)
936  << " add-on parameters... " << parentPath << __E__;
937  insertParameters(out,
938  tabStr,
939  commentStr,
940  parentPath,
941  boardReaderNode.getNode("addOnParametersLink"),
942  "daqParameter" /*parameterType*/,
943  false /*onlyInsertAtTableParameters*/,
944  true /*includeAtTableParameters*/);
945  out << "\n";
946  OUTC("# end of " << getTypeString(ARTDAQAppType::BoardReader) << " '"
947  << boardReaderNode.getValue() << "' fcl",
948  "" /* comment */);
949  __COUTT__ << "outputBoardReaderFHICL DONE" << __E__;
950  }
951  catch(...)
952  {
953  __SS__ << "\n\nError while generating FHiCL for "
954  << getTypeString(ARTDAQAppType::BoardReader) << " node at filename '"
955  << filename << "'" << __E__;
956  try
957  {
958  throw;
959  }
960  catch(const std::runtime_error& e)
961  {
962  ss << " Here is the error: " << e.what() << __E__;
963  }
964  catch(const std::exception& e)
965  {
966  ss << " Here is the error: " << e.what() << __E__;
967  }
968  out << ss.str();
969  out.close();
970  __SS_THROW__;
971  }
972 
973  out.close();
974 } // end outputBoardReaderFHICL()
975 
976 //==============================================================================
981  const ConfigurationTree& receiverNode,
982  ARTDAQAppType appType,
983  size_t /*maxFragmentSizeBytes */ /* = DEFAULT_MAX_FRAGMENT_SIZE */,
984  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
985  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */,
986  std::string* returnFcl /* = nullptr */)
987 {
988  if(ARTDAQ_DONOTWRITE_FCL)
989  {
990  __COUT__ << "Skipping fcl generation." << __E__;
991  return;
992  }
993 
994  std::string filename = getFHICLFilename(appType, receiverNode.getValue());
995 
997  // generate xdaq run parameter file
998  std::fstream outf;
999  std::ostringstream out;
1000 
1001  std::string tabStr = "";
1002  std::string commentStr = "";
1003 
1004  __COUTV__(filename);
1005  outf.open(filename, std::fstream::out | std::fstream::trunc);
1006  if(outf.fail())
1007  {
1008  __SS__ << "Failed to open ARTDAQ fcl file: " << filename << __E__;
1009  __SS_THROW__;
1010  }
1011 
1012  try //catch and give error in fcl file if issue!
1013  {
1014  //--------------------------------------
1015  // header
1016  OUT << "###########################################################" << __E__;
1017  OUT << "#" << __E__;
1018  OUT << "# artdaq " << getTypeString(appType)
1019  << " fcl configuration file produced by otsdaq." << __E__;
1020  OUT << "# Creation time: \t"
1021  << StringMacros::getTimestampString() << __E__;
1022  OUT << "# Original filename: \t" << filename << __E__;
1023  OUT << "# otsdaq-ARTDAQ " << getTypeString(appType) << " UID:\t"
1024  << receiverNode.getValue() << __E__;
1025  OUT << "#" << __E__;
1026  OUT << "###########################################################" << __E__;
1027  OUT << "\n\n";
1028 
1029  // no primary link to table tree for data receiver node!
1030  try
1031  {
1032  if(receiverNode.isDisconnected())
1033  {
1034  // create empty fcl
1035  OUT << "{}\n\n";
1036  if(returnFcl)
1037  {
1038  *returnFcl = out.str();
1039  __COUTVS__(21, *returnFcl);
1040  }
1041  outf << out.str();
1042  outf.close();
1043  return;
1044  }
1045  }
1046  catch(const std::runtime_error&)
1047  {
1048  __COUTT__ << "Ignoring error, assume this a valid UID node." << __E__;
1049  // error is expected here for UIDs.. so just ignore
1050  // this check is valuable if source node is a unique-Link node, rather than UID
1051  }
1052 
1053  std::string parentPath =
1054  receiverNode.getTableName() + "/" + receiverNode.getValue();
1055 
1056  OUTC("# start of " << getTypeString(appType) << " '" << receiverNode.getValue()
1057  << "' fcl",
1058  "" /* comment */);
1059 
1060  //--------------------------------------
1061  // handle preamble parameters
1062  __COUTT__ << "Inserting " << getTypeString(appType) << " preamble parameters... "
1063  << parentPath << __E__;
1064  out << "\n";
1065  insertParameters(out,
1066  tabStr,
1067  commentStr,
1068  parentPath,
1069  receiverNode.getNode("preambleParametersLink"),
1070  "daqParameter" /*parameterType*/,
1071  false /*onlyInsertAtTableParameters*/,
1072  true /*includeAtTableParameters*/);
1073 
1074  //--------------------------------------
1075  // handle daq
1076  __COUTT__ << "Generating daq block..." << __E__;
1077  out << "\n";
1078  auto daq = receiverNode.getNode("daqLink");
1079  if(!daq.isDisconnected())
1080  {
1082  OUTCF("daq: {", "" /* comment */, daq.getParentLinkColumnName());
1083 
1084  PUSHTAB;
1085  if(appType == ARTDAQAppType::EventBuilder)
1086  OUT << "event_builder: {\n";
1087  else // both datalogger and dispatcher use aggregator for now
1088  OUT << "aggregator: {\n";
1089 
1090  PUSHTAB;
1091 
1092  { //define datalogger vs dispatcher
1093  std::stringstream outSs;
1094  if(appType == ARTDAQAppType::DataLogger)
1095  outSs << "is_datalogger: true";
1096  else if(appType == ARTDAQAppType::Dispatcher)
1097  outSs << "is_dispatcher: true";
1098  if(outSs.str().size())
1099  {
1100  addCommentWhitespace(
1101  outSs,
1102  tabStr.size() * TABSZ + commentStr.size() + outSs.str().size());
1103  outSs << "auto-generated based on app type '"
1104  << getTypeString(appType) << "'\n";
1105  OUT << outSs.str();
1106  }
1107  }
1108 
1109  //--------------------------------------
1110  // handle ALL daq parameters
1111  std::string parentPath = daq.getParentTableName() + "/" +
1112  daq.getParentRecordName() + "/" +
1113  daq.getParentLinkColumnName() + ":" +
1114  daq.getTableName() + "/" + daq.getValue();
1115  __COUTT__ << "Inserting " << getTypeString(appType) << " DAQ Parameters... "
1116  << parentPath << __E__;
1117  insertParameters(out,
1118  tabStr,
1119  commentStr,
1120  parentPath,
1121  daq.getNode("daqParametersLink"),
1122  "daqParameter" /*parameterType*/,
1123  false /*onlyInsertAtTableParameters*/,
1124  true /*includeAtTableParameters*/);
1125 
1126  if(appType == ARTDAQAppType::EventBuilder)
1127  {
1128  out << "\n";
1129  OUT << "routing_token_config: {\n";
1130  PUSHTAB;
1131 
1132  auto builderSubsystemID = 1;
1133  auto builderSubsystemLink = receiverNode.getNode("SubsystemLink");
1134  if(!builderSubsystemLink.isDisconnected())
1135  {
1136  builderSubsystemID = getSubsytemId(builderSubsystemLink);
1137  }
1138  if(info_.subsystems[builderSubsystemID].hasRoutingManager)
1139  {
1140  std::string localParentPath =
1141  parentPath + "/" +
1142  builderSubsystemLink.getParentLinkColumnName() + ":" +
1143  builderSubsystemLink.getTableName() + "/" +
1144  builderSubsystemLink.getValue();
1145  __COUTT__ << "Inserting routing manager... " << localParentPath
1146  << __E__;
1147  OUTCL("use_routing_manager: true",
1148  "auto-generated because subsystem '" +
1149  std::to_string(builderSubsystemID) +
1150  "' has Routing Manager added");
1151 
1152  OUTCLF("routing_manager_hostname: \""
1153  << info_.subsystems[builderSubsystemID].routingManagerHost
1154  << "\"",
1155  "" /* comment */,
1156  ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME);
1157  OUT << "routing_token_port: 0\n";
1158  }
1159  else
1160  {
1161  OUTCF("use_routing_manager: false",
1162  "auto-generated if subsystem '" +
1163  std::to_string(builderSubsystemID) +
1164  "' has Routing Manager added",
1165  builderSubsystemLink.getParentLinkColumnName());
1166  }
1167  POPTAB;
1168  OUT << "}\n"; // end routing_token_config
1169  }
1170 
1171  __COUTT__ << "Adding sources placeholder" << __E__;
1172  out << "\n";
1173  OUT << "sources: { # empty placeholder, '" << getTypeString(appType)
1174  << "' sources handled by artdaq interface\n";
1175  OUT << "}\n\n"; // end sources
1176 
1177  POPTAB;
1178 
1179  if(appType == ARTDAQAppType::EventBuilder)
1180  OUT << "} # end event_builder\n"; // end event builder
1181  else // both datalogger and dispatcher use aggregator for now
1182  OUT << "} # end aggregator\n"; // end aggregator
1183 
1184  insertMetricsBlock(OUT, tabStr, commentStr, parentPath, daq);
1185 
1186  POPTAB;
1187  OUT << "} # end daq\n\n"; // end daq
1188  }
1189  else
1190  {
1191  __COUTS__(3) << "No daq found" << __E__;
1192  std::string localParentPath =
1193  parentPath + "/" + daq.getParentLinkColumnName();
1194  OUTCL("# no daq found", "" /* comment*/);
1195  }
1196 
1197  //--------------------------------------
1198  // handle art
1199  __COUTT__ << "Filling art block..." << __E__;
1200  out << "\n";
1201  auto art =
1202  receiverNode.getNode(ARTDAQTableBase::colARTDAQNotReader_.colLinkToArt_);
1203  if(!art.isDisconnected())
1204  {
1205  std::string localParentPath = parentPath + "/" +
1206  art.getParentLinkColumnName() + ":" +
1207  art.getTableName() + "/" + art.getValue();
1208  OUTCF("art: {", "" /* comment */, art.getParentLinkColumnName());
1209 
1210  PUSHTAB;
1211 
1213  tabStr,
1214  commentStr,
1215  localParentPath,
1216  art,
1217  receiverNode.getNode("SubsystemLink"),
1218  routingTimeoutMs,
1219  routingRetryCount);
1220 
1221  POPTAB;
1222  OUT << "} # end art\n\n"; // end art
1223  }
1224  else
1225  {
1226  __COUTS__(3) << "No art found" << __E__;
1227  std::string localParentPath =
1228  parentPath + "/" + art.getParentLinkColumnName();
1229  OUTCL("# no art found", "" /* comment*/);
1230  }
1231 
1232  //--------------------------------------
1233  // handle ALL add-on parameters
1234  __COUTT__ << "Inserting " << getTypeString(appType) << " add-on parameters... "
1235  << parentPath << __E__;
1236  insertParameters(out,
1237  tabStr,
1238  commentStr,
1239  parentPath,
1240  receiverNode.getNode("addOnParametersLink"),
1241  "daqParameter" /*parameterType*/,
1242  false /*onlyInsertAtTableParameters*/,
1243  true /*includeAtTableParameters*/);
1244 
1245  out << "\n";
1246  OUTC("# end of " << getTypeString(appType) << " '" << receiverNode.getValue()
1247  << "' fcl",
1248  "" /* comment */);
1249  __COUTT__ << "outputDataReceiverFHICL DONE" << __E__;
1250  }
1251  catch(...)
1252  {
1253  __SS__ << "\n\nError while generating FHiCL for " << getTypeString(appType)
1254  << " node at filename '" << filename << "'" << __E__;
1255  try
1256  {
1257  throw;
1258  }
1259  catch(const std::runtime_error& e)
1260  {
1261  ss << " Here is the error: " << e.what() << __E__;
1262  }
1263  catch(const std::exception& e)
1264  {
1265  ss << " Here is the error: " << e.what() << __E__;
1266  }
1267  out << ss.str();
1268  if(returnFcl)
1269  {
1270  *returnFcl = out.str();
1271  __COUTVS__(21, *returnFcl);
1272  }
1273  outf << out.str();
1274  outf.close();
1275  __SS_THROW__;
1276  }
1277 
1278  if(returnFcl)
1279  {
1280  *returnFcl = out.str();
1281  __COUTVS__(21, *returnFcl);
1282  }
1283  outf << out.str();
1284  outf.close();
1285 } // end outputDataReceiverFHICL()
1286 
1287 //==============================================================================
1291 {
1292  if(ARTDAQ_DONOTWRITE_FCL)
1293  {
1294  __COUT__ << "Skipping fcl generation." << __E__;
1295  return;
1296  }
1297 
1298  std::string filename =
1299  getFHICLFilename(ARTDAQAppType::Monitor, monitorNode.getValue());
1300 
1302  // generate xdaq run parameter file
1303  std::fstream out;
1304 
1305  std::string tabStr = "";
1306  std::string commentStr = "";
1307 
1308  __COUTV__(filename);
1309  out.open(filename, std::fstream::out | std::fstream::trunc);
1310  if(out.fail())
1311  {
1312  __SS__ << "Failed to open ARTDAQ fcl file: " << filename << __E__;
1313  __SS_THROW__;
1314  }
1315 
1316  try //catch and give error in fcl file if issue!
1317  {
1318  //--------------------------------------
1319  // header
1320  OUT << "###########################################################" << __E__;
1321  OUT << "#" << __E__;
1322  OUT << "# artdaq " << getTypeString(ARTDAQAppType::Monitor)
1323  << " fcl configuration file produced by otsdaq." << __E__;
1324  OUT << "# Creation time: \t"
1325  << StringMacros::getTimestampString() << __E__;
1326  OUT << "# Original filename: \t" << filename << __E__;
1327  OUT << "# otsdaq-ARTDAQ " << getTypeString(ARTDAQAppType::Monitor) << " UID:\t"
1328  << monitorNode.getValue() << __E__;
1329  OUT << "#" << __E__;
1330  OUT << "###########################################################" << __E__;
1331  OUT << "\n\n";
1332 
1333  // no primary link to table tree for data receiver node!
1334  try
1335  {
1336  if(monitorNode.isDisconnected())
1337  {
1338  // create empty fcl
1339  OUT << "{}\n\n";
1340  out.close();
1341  return;
1342  }
1343  }
1344  catch(const std::runtime_error&)
1345  {
1346  __COUTT__ << "Ignoring error, assume this a valid UID node." << __E__;
1347  // error is expected here for UIDs.. so just ignore
1348  // this check is valuable if source node is a unique-Link node, rather than UID
1349  }
1350 
1351  //--------------------------------------
1352  // handle preamble parameters
1353  std::string parentPath =
1354  monitorNode.getParentTableName() + "/" + monitorNode.getParentRecordName() +
1355  "/" + monitorNode.getParentLinkColumnName() + ":" +
1356  monitorNode.getTableName() + "/" + monitorNode.getValue();
1357  __COUTT__ << "Inserting " << getTypeString(ARTDAQAppType::Monitor)
1358  << " preamble parameters... " << parentPath << __E__;
1359  insertParameters(out,
1360  tabStr,
1361  commentStr,
1362  parentPath,
1363  monitorNode.getNode("preambleParametersLink"),
1364  "daqParameter" /*parameterType*/,
1365  false /*onlyInsertAtTableParameters*/,
1366  true /*includeAtTableParameters*/);
1367 
1368  //--------------------------------------
1369  // handle art
1370  //__COUT__ << "Filling art block..." << __E__;
1371  auto art =
1372  monitorNode.getNode(ARTDAQTableBase::colARTDAQNotReader_.colLinkToArt_);
1373  if(!art.isDisconnected())
1374  {
1375  insertArtProcessBlock(out, tabStr, commentStr, parentPath, art);
1376  OUT << "services.message: { "
1377  << artdaq::generateMessageFacilityConfiguration(
1378  mf::GetApplicationName().c_str(), true, false)
1379  << "}\n";
1380  OUT << "services.message.destinations.file: {type: \"GenFile\" threshold: "
1381  "\"INFO\" seperator: \"-\""
1382  << " pattern: \"" << monitorNode.getValue() << "-%?H%t-%p.log"
1383  << "\""
1384  << " timestamp_pattern: \"%Y%m%d%H%M%S\""
1385  << " directory: \"" << __ENV__("OTSDAQ_LOG_ROOT") << "/"
1386  << monitorNode.getValue() << "\""
1387  << " append : false }\n";
1388  }
1389 
1390  auto dispatcherLink = monitorNode.getNode("dispatcherLink");
1391  if(!dispatcherLink.isDisconnected())
1392  {
1393  std::string monitorHost =
1394  monitorNode.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
1395  .getValueWithDefault("localhost");
1396  std::string dispatcherHost =
1397  dispatcherLink.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
1398  .getValueWithDefault("localhost");
1399  OUT << "source.dispatcherHost: \"" << dispatcherHost << "\"\n";
1400  int dispatcherPort = dispatcherLink.getNode("DispatcherPort").getValue<int>();
1401  OUT << "source.dispatcherPort: " << dispatcherPort << "\n";
1402  OUT << "source.commanderPluginType: xmlrpc\n";
1403 
1404  int om_rank = monitorNode.getNode("MonitorID").getValue<int>();
1405  int om_tcp_listen_port =
1406  monitorNode.getNode("MonitorTCPListenPort").getValue<int>();
1407  int disp_fake_rank =
1408  dispatcherLink.getNode("DispatcherID").getValueWithDefault<int>(200);
1409 
1410  size_t max_fragment_size = monitorNode.getNode("max_fragment_size_words")
1411  .getValueWithDefault(0x100000);
1412  std::string transfer_plugin_type = monitorNode.getNode("transfer_plugin_type")
1413  .getValueWithDefault("Autodetect");
1414 
1415  OUT << "TransferPluginConfig: {\n";
1416  PUSHTAB;
1417  OUT << "transferPluginType: " << transfer_plugin_type << "\n";
1418  OUT << "host_map: [{ rank: " << disp_fake_rank << " host: \""
1419  << dispatcherHost << "\"}, { rank: " << om_rank << " host: \""
1420  << monitorHost << "\"}]\n";
1421  OUT << "max_fragment_size_words: " << max_fragment_size << "\n";
1422  OUT << "source_rank: " << disp_fake_rank << "\n";
1423  OUT << "destination_rank: " << om_rank << "\n";
1424  OUT << "port: " << om_tcp_listen_port << "\n";
1425  OUT << "unique_label: " << monitorNode.getValue() << "_to_"
1426  << dispatcherLink.getValue() << "\n";
1427  POPTAB;
1428  OUT << "}\n";
1429  OUT << "source.transfer_plugin: @local::TransferPluginConfig \n";
1430  auto dispatcherArt = monitorNode.getNode("dispatcherArtLink");
1431  if(!dispatcherArt.isDisconnected())
1432  {
1433  OUT << "source.dispatcher_config: {\n";
1434 
1435  PUSHTAB;
1436 
1437  OUT << "path: " << monitorNode.getNode("dispatcher_path").getValue()
1438  << "\n";
1439  OUT << "filter_paths: [\n";
1440 
1441  PUSHTAB;
1442 
1443  auto filterPathsLink = monitorNode.getNode("filterPathsLink");
1444  if(!filterPathsLink.isDisconnected())
1445  {
1447  auto filterPaths = filterPathsLink.getChildren();
1448  bool first = true;
1449 
1450  //__COUTV__(otherParameters.size());
1451  for(auto& filterPath : filterPaths)
1452  {
1453  if(!first)
1454  OUT << ",";
1455  OUT << "{ ";
1456 
1457  if(!filterPath.second.status())
1458  PUSHCOMMENT;
1459 
1460  OUT << "name: " << filterPath.second.getNode("Name").getValue()
1461  << " ";
1462  OUT << "path: " << filterPath.second.getNode("Path").getValue()
1463  << " ";
1464 
1465  OUT << "}\n";
1466  if(!filterPath.second.status())
1467  POPCOMMENT;
1468  first = false;
1469  }
1470  }
1471 
1472  POPTAB;
1473 
1474  OUT << "]\n";
1475  OUT << "unique_label: " << monitorNode.getValue() << "\n";
1476  insertArtProcessBlock(out, tabStr, commentStr, parentPath, dispatcherArt);
1477 
1478  POPTAB;
1479  OUT << "}\n\n"; // end art
1480  }
1481  }
1482 
1483  //--------------------------------------
1484  // handle ALL add-on parameters
1485  parentPath = monitorNode.getParentTableName() + "/" +
1486  monitorNode.getParentRecordName() + "/" +
1487  monitorNode.getParentLinkColumnName() + ":" +
1488  monitorNode.getTableName() + "/" + monitorNode.getValue();
1489  __COUTT__ << "Inserting " << getTypeString(ARTDAQAppType::Monitor)
1490  << " add-on parameters... " << parentPath << __E__;
1491  insertParameters(out,
1492  tabStr,
1493  commentStr,
1494  parentPath,
1495  monitorNode.getNode("addOnParametersLink"),
1496  "daqParameter" /*parameterType*/,
1497  false /*onlyInsertAtTableParameters*/,
1498  true /*includeAtTableParameters*/);
1499 
1500  __COUTT__ << "outputOnlineMonitorFHICL DONE" << __E__;
1501  }
1502  catch(...)
1503  {
1504  __SS__ << "\n\nError while generating FHiCL for "
1505  << getTypeString(ARTDAQAppType::Monitor) << " node at filename '"
1506  << filename << "'" << __E__;
1507  try
1508  {
1509  throw;
1510  }
1511  catch(const std::runtime_error& e)
1512  {
1513  ss << " Here is the error: " << e.what() << __E__;
1514  }
1515  catch(const std::exception& e)
1516  {
1517  ss << " Here is the error: " << e.what() << __E__;
1518  }
1519  out << ss.str();
1520  out.close();
1521  __SS_THROW__;
1522  }
1523 
1524  out.close();
1525 } // end outputOnlineMonitorFHICL()
1526 
1527 //==============================================================================
1531  std::string& tabStr,
1532  std::string& commentStr,
1533  const std::string& parentPath,
1534  ConfigurationTree art,
1535  ConfigurationTree subsystemLink,
1536  size_t routingTimeoutMs,
1537  size_t routingRetryCount)
1538 {
1539  //--------------------------------------
1540  // handle services
1541  __COUTT__ << "Filling art.services parentPath =" << parentPath << __E__;
1542  auto services = art.getNode("servicesLink");
1543  if(!services.isDisconnected())
1544  {
1545  std::string localParentPath =
1546  parentPath + "/" + services.getParentLinkColumnName() + ":" +
1547  services.getTableName() + "/" +
1548  services.getValue(); //unique link so can go further
1549  __COUTT__ << "Inserting services... " << localParentPath << __E__;
1550  OUTCL("services: {", services.hasComment() ? services.getComment() : "");
1551  PUSHTAB;
1552 
1553  //--------------------------------------
1554  // handle services @table:: parameters
1555  __COUTT__ << "Inserting services parameters... " << localParentPath << __E__;
1556  insertParameters(out,
1557  tabStr,
1558  commentStr,
1559  localParentPath,
1560  services.getNode("ServicesParametersLink"),
1561  "daqParameter" /*parameterType*/,
1562  true /*onlyInsertAtTableParameters*/,
1563  false /*includeAtTableParameters*/);
1564 
1565  out << "\n";
1566  OUT << "ArtdaqSharedMemoryServiceInterface: {\n";
1567  PUSHTAB;
1568  OUT << "service_provider: "
1569  "ArtdaqSharedMemoryService \n";
1570 
1571  OUTCLF("waiting_time: " << services.getNode("sharedMemoryWaitingTime").getValue(),
1572  "" /* comment */,
1573  "sharedMemoryWaitingTime");
1574  OUTCLF("resume_after_timeout: "
1575  << (services.getNode("sharedMemoryResumeAfterTimeout").getValue<bool>()
1576  ? "true"
1577  : "false"),
1578  "" /* comment */,
1579  "sharedMemoryResumeAfterTimeout");
1580  POPTAB;
1581  OUT << "} # end ArtdaqSharedMemoryServiceInterface\n\n";
1582 
1583  OUT << "ArtdaqFragmentNamingServiceInterface: {\n";
1584  PUSHTAB;
1585  OUT << "service_provider: "
1586  "ArtdaqFragmentNamingService \n";
1587  OUTCLF("helper_plugin: "
1588  << services.getNode("fragmentNamingServiceProvider").getValue(),
1589  "" /* comment */,
1590  "fragmentNamingServiceProvider");
1591  POPTAB;
1592  OUT << "} # end ArtdaqFragmentNamingServiceInterface\n\n";
1593 
1594  //--------------------------------------
1595  // handle services NOT @table:: parameters
1596  __COUTT__ << "Inserting services parameters... " << localParentPath << __E__;
1597  insertParameters(out,
1598  tabStr,
1599  commentStr,
1600  localParentPath,
1601  services.getNode("ServicesParametersLink"),
1602  "daqParameter" /*parameterType*/,
1603  false /*onlyInsertAtTableParameters*/,
1604  false /*includeAtTableParameters*/);
1605 
1606  POPTAB;
1607  OUT << "} # end services\n\n"; // end services
1608  } //end services
1609  else
1610  {
1611  __COUTS__(3) << "No services found" << __E__;
1612  std::string localParentPath =
1613  parentPath + "/" + services.getParentLinkColumnName();
1614  OUTCL("# no services found", "" /* comment*/);
1615  }
1616 
1617  //--------------------------------------
1618  // handle outputs
1619  __COUTT__ << "Filling art.outputs parentPath =" << parentPath << __E__;
1620  auto outputs = art.getNode("outputsLink");
1621  if(!outputs.isDisconnected())
1622  {
1623  std::string localParentPath =
1624  parentPath + "/" +
1625  outputs.getParentLinkColumnName(); //group link so cannot go further
1626  __COUTT__ << "Inserting output... " << localParentPath << __E__;
1627  OUTCL("outputs: {", "" /* comment */);
1628  PUSHTAB;
1629 
1630  auto outputPlugins = outputs.getChildren();
1631  for(auto& outputPlugin : outputPlugins)
1632  {
1633  if(!outputPlugin.second.status())
1634  PUSHCOMMENT;
1635 
1636  __COUTT__ << "Inserting output parameters... " << localParentPath << __E__;
1637  std::string localParentPath2 =
1638  localParentPath + ":" + outputPlugin.second.getTableName() + ":" +
1639  outputs.getParentLinkIndex() + ":" + outputs.getParentLinkID() + "/" +
1640  outputPlugin.second.getValue();
1641  __COUTT__ << "Inserting output... " << localParentPath2 << __E__;
1642  OUTCL2F(
1643  outputPlugin.second.getNode("outputKey").getValue() << ": {",
1644  outputPlugin.second.hasComment() ? outputPlugin.second.getComment() : "",
1645  "outputKey");
1646  PUSHTAB;
1647 
1648  __COUTT__ << "insertModuleType... " << localParentPath2 << __E__;
1649  std::string moduleType =
1650  insertModuleType(out,
1651  tabStr,
1652  commentStr,
1653  localParentPath2,
1654  outputPlugin.second.getNode("outputModuleType"));
1655 
1656  //--------------------------------------
1657  // handle ALL output parameters
1658  __COUTT__ << "Inserting output parameters... " << localParentPath << __E__;
1659  insertParameters(out,
1660  tabStr,
1661  commentStr,
1662  localParentPath,
1663  outputPlugin.second.getNode("outputModuleParameterLink"),
1664  "outputParameter" /*parameterType*/,
1665  false /*onlyInsertAtTableParameters*/,
1666  true /*includeAtTableParameters*/);
1667 
1668  if(outputPlugin.second.getNode("outputModuleType").getValue() ==
1669  "BinaryNetOutput" ||
1670  outputPlugin.second.getNode("outputModuleType").getValue() ==
1671  "RootNetOutput")
1672  {
1673  OUT << "destinations: { # empty placeholder, '"
1674  << outputPlugin.second.getNode("outputModuleType").getValue()
1675  << "' destinations handled by artdaq interface\n";
1676  OUT << "}\n\n"; // end destinations
1677 
1678  OUT << "routing_table_config: {\n";
1679  PUSHTAB;
1680 
1681  auto mySubsystemID = 1;
1682  auto destinationSubsystemID = 1;
1683  if(!subsystemLink.isDisconnected())
1684  {
1685  mySubsystemID = getSubsytemId(subsystemLink);
1686  }
1687  destinationSubsystemID = info_.subsystems[mySubsystemID].destination;
1688  if(info_.subsystems[destinationSubsystemID].hasRoutingManager)
1689  {
1690  std::string localParentPath =
1691  parentPath + "/" + subsystemLink.getParentLinkColumnName() + ":" +
1692  subsystemLink.getTableName() + "/" + subsystemLink.getValue();
1693  __COUTT__ << "Inserting routing manager... " << localParentPath
1694  << __E__;
1695  OUTCL("use_routing_manager: true",
1696  "auto-generated because subsystem '" +
1697  std::to_string(destinationSubsystemID) +
1698  "' has Routing Manager added");
1699 
1700  OUTCLF(
1701  "routing_manager_hostname: \""
1702  << info_.subsystems[destinationSubsystemID].routingManagerHost
1703  << "\"",
1704  "" /* comment */,
1705  ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME);
1706  OUT << "table_update_port: 0\n";
1707  OUT << "table_update_address: \"0.0.0.0\"\n";
1708  OUT << "table_update_multicast_interface: \"0.0.0.0\"\n";
1709  OUT << "table_acknowledge_port : 0\n";
1710  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
1711  OUT << "routing_retry_count: " << routingRetryCount << "\n";
1712  }
1713  else
1714  {
1715  OUTCF("use_routing_manager: false",
1716  "auto-generated if subsystem '" +
1717  std::to_string(destinationSubsystemID) +
1718  "' has Routing Manager added",
1719  subsystemLink.getParentLinkColumnName());
1720  }
1721 
1722  if(outputPlugin.second.getNode("outputModuleType").getValue() ==
1723  "RootNetOutput")
1724  {
1725  info_.subsystems[mySubsystemID].eventMode = true;
1726  }
1727 
1728  POPTAB;
1729  OUT << "}\n"; // end routing_table_config
1730  }
1731  if(outputPlugin.second.getNode("outputModuleType").getValue() ==
1732  "TransferOutput" ||
1733  outputPlugin.second.getNode("outputModuleType").getValue() ==
1734  "TransferOutputReliable")
1735  {
1736  OUT << "transfer_plugin: @local::TransferPluginConfig \n";
1737  }
1738 
1739  POPTAB;
1740  OUT << "} # end " << outputPlugin.second.getNode("outputKey").getValue()
1741  << "\n\n"; // end output module
1742 
1743  if(!outputPlugin.second.status())
1744  POPCOMMENT;
1745  }
1746 
1747  POPTAB;
1748  OUT << "} # end outputs\n\n"; // end outputs
1749  } //end outputs
1750  else
1751  {
1752  __COUTS__(3) << "No outputs found" << __E__;
1753  std::string localParentPath =
1754  parentPath + "/" + outputs.getParentLinkColumnName();
1755  OUTCL("# no outputs found", "" /* comment*/);
1756  }
1757 
1758  //--------------------------------------
1759  // handle physics
1760  __COUTT__ << "Filling art.physics parentPath =" << parentPath << __E__;
1761  auto physics = art.getNode("physicsLink");
1762  if(!physics.isDisconnected())
1763  {
1764  __COUTT__ << "Inserting physics... " << parentPath << __E__;
1765  std::string localParentPath = parentPath + "/" +
1766  physics.getParentLinkColumnName() + ":" +
1767  physics.getTableName() + "/" +
1768  physics.getValue(); //unique link so can go further
1769 
1771  OUTCL("physics: {", physics.hasComment() ? services.getComment() : "");
1772 
1773  PUSHTAB;
1774 
1775  //--------------------------------------
1776  // handle only @table:: physics parameters
1777  __COUTT__ << "Inserting physics other parameters... " << localParentPath << __E__;
1778  insertParameters(out,
1779  tabStr,
1780  commentStr,
1781  localParentPath,
1782  physics.getNode("physicsOtherParametersLink"),
1783  "physicsParameter" /*parameterType*/,
1784  true /*onlyInsertAtTableParameters*/,
1785  false /*includeAtTableParameters*/);
1786 
1787  auto analyzers = physics.getNode("analyzersLink");
1788  if(!analyzers.isDisconnected())
1789  {
1790  __COUTT__ << "Inserting art.physics.analyzers... " << localParentPath
1791  << __E__;
1792  std::string localParentPath2 =
1793  localParentPath + "/" + analyzers.getParentLinkColumnName(); //group link
1794  __COUTT__ << "Inserting art.physics.analyzers... " << localParentPath2
1795  << __E__;
1796 
1798  out << "\n";
1799  OUTCL2("analyzers: {", "" /* comment */);
1800  PUSHTAB;
1801 
1802  bool first = true;
1803  auto modules = analyzers.getChildren();
1804  for(auto& module : modules)
1805  {
1806  if(!module.second.status())
1807  PUSHCOMMENT;
1808 
1809  if(!first)
1810  out << "\n";
1811  first = false;
1812 
1813  auto analyzerNodeParameterLink =
1814  module.second.getNode("analyzerModuleParameterLink");
1815  //--------------------------------------
1816  // handle only @table:: analyzer parameters
1817  __COUTT__ << "Inserting analyzer @table parameters... "
1818  << localParentPath2 << __E__;
1819  std::string localParentPath3 =
1820  localParentPath2 + ":" + module.second.getTableName() + ":" +
1821  analyzers.getParentLinkIndex() + ":" + analyzers.getParentLinkID() +
1822  "/" + module.second.getValue();
1823  __COUTT__ << "Inserting analyzer @table parameters... "
1824  << localParentPath3 << __E__;
1825  insertParameters(out,
1826  tabStr,
1827  commentStr,
1828  localParentPath3,
1829  analyzerNodeParameterLink,
1830  "analyzerParameter" /*parameterType*/,
1831  true /*onlyInsertAtTableParameters*/,
1832  false /*includeAtTableParameters*/);
1833 
1834  OUT << module.second.getNode("analyzerKey").getValue() << ": {\n";
1835  PUSHTAB;
1836  insertModuleType(out,
1837  tabStr,
1838  commentStr,
1839  localParentPath3,
1840  module.second.getNode("analyzerModuleType"));
1841 
1842  //--------------------------------------
1843  // handle NOT @table:: producer parameters
1844  __COUTT__ << "Inserting analayzer not @table parameters... "
1845  << localParentPath3 << __E__;
1846  insertParameters(out,
1847  tabStr,
1848  commentStr,
1849  localParentPath3,
1850  analyzerNodeParameterLink,
1851  "analyzerParameter" /*parameterType*/,
1852  false /*onlyInsertAtTableParameters*/,
1853  false /*includeAtTableParameters*/);
1854 
1855  POPTAB;
1856  OUT << "}\n"; // end analyzer module
1857 
1858  if(!module.second.status())
1859  POPCOMMENT;
1860  } //end analyzer module loop
1861  POPTAB;
1862  OUT << "} # end physics.analyzers\n\n"; // end analyzers
1863  }
1864  else
1865  {
1866  __COUTS__(3) << "No analyzers found" << __E__;
1867  std::string localParentPath2 =
1868  localParentPath + "/" + analyzers.getParentLinkColumnName();
1869  OUTCL2("# no analyzers found", "" /* comment*/);
1870  }
1871 
1872  auto producers = physics.getNode("producersLink");
1873  if(!producers.isDisconnected())
1874  {
1875  __COUTT__ << "Inserting art.physics.producers... " << localParentPath
1876  << __E__;
1877  std::string localParentPath2 =
1878  localParentPath + "/" + producers.getParentLinkColumnName(); //group link
1879 
1881  out << "\n";
1882  OUTCL2("producers: {", "" /* comment */);
1883  PUSHTAB;
1884 
1885  bool first = true;
1886  auto modules = producers.getChildren();
1887  for(auto& module : modules)
1888  {
1889  if(!module.second.status())
1890  PUSHCOMMENT;
1891 
1892  if(!first)
1893  out << "\n";
1894  first = false;
1895 
1896  auto producerNodeParameterLink =
1897  module.second.getNode("producerModuleParameterLink");
1898  //--------------------------------------
1899  // handle only @table:: producer parameters
1900  __COUTT__ << "Inserting producer @table parameters... "
1901  << localParentPath2 << __E__;
1902  std::string localParentPath3 =
1903  localParentPath2 + ":" + module.second.getTableName() + ":" +
1904  producers.getParentLinkIndex() + ":" + producers.getParentLinkID() +
1905  "/" + module.second.getValue();
1906  __COUTT__ << "Inserting producer @table parameters... "
1907  << localParentPath3 << __E__;
1908  insertParameters(out,
1909  tabStr,
1910  commentStr,
1911  localParentPath3,
1912  producerNodeParameterLink,
1913  "producerParameter" /*parameterType*/,
1914  true /*onlyInsertAtTableParameters*/,
1915  false /*includeAtTableParameters*/);
1916 
1917  if(module.second.status() &&
1918  module.second.getNode("producerModuleType").getValue() == "")
1919  {
1920  std::string tmp = localParentPath2;
1921  localParentPath2 = localParentPath3;
1922  OUTCL2F("# skipping '" << module.second.getValue()
1923  << "' with empty module type",
1924  "" /* comment */,
1925  "producerModuleType");
1926  localParentPath2 = tmp;
1927  continue;
1928  }
1929 
1930  OUT << module.second.getNode("producerKey").getValue() << ": {\n";
1931  PUSHTAB;
1932 
1933  insertModuleType(out,
1934  tabStr,
1935  commentStr,
1936  localParentPath3,
1937  module.second.getNode("producerModuleType"));
1938 
1939  //--------------------------------------
1940  // handle NOT @table:: producer parameters
1941  __COUTT__ << "Inserting producer not @table parameters... "
1942  << localParentPath3 << __E__;
1943  insertParameters(out,
1944  tabStr,
1945  commentStr,
1946  localParentPath3,
1947  producerNodeParameterLink,
1948  "producerParameter" /*parameterType*/,
1949  false /*onlyInsertAtTableParameters*/,
1950  false /*includeAtTableParameters*/);
1951 
1952  POPTAB;
1953  OUT << "}\n"; // end producer module
1954 
1955  if(!module.second.status())
1956  POPCOMMENT;
1957  } //end producer module loop
1958  POPTAB;
1959  OUT << "} # end physics.producers\n\n"; // end producers
1960  }
1961  else
1962  {
1963  __COUTS__(3) << "No producers found" << __E__;
1964  std::string localParentPath2 =
1965  localParentPath + "/" + producers.getParentLinkColumnName();
1966  OUTCL2("# no producers found", "" /* comment*/);
1967  }
1968 
1969  auto filters = physics.getNode("filtersLink");
1970  if(!filters.isDisconnected())
1971  {
1972  __COUTT__ << "Inserting art.physics.filters... " << localParentPath << __E__;
1973  std::string localParentPath2 =
1974  localParentPath + "/" + filters.getParentLinkColumnName(); //group link
1975 
1977  out << "\n";
1978  OUTCL2("filters: {", "" /* comment */);
1979  PUSHTAB;
1980 
1981  bool first = true;
1982  auto modules = filters.getChildren();
1983  for(auto& module : modules)
1984  {
1985  if(!module.second.status())
1986  PUSHCOMMENT;
1987 
1988  if(!first)
1989  out << "\n";
1990  first = false;
1991 
1992  auto filterNodeParameterLink =
1993  module.second.getNode("filterModuleParameterLink");
1994  //--------------------------------------
1995  // handle only @table:: filter parameters
1996  __COUTT__ << "Inserting filter @table parameters... " << localParentPath2
1997  << __E__;
1998  std::string localParentPath3 =
1999  localParentPath2 + ":" + module.second.getTableName() + ":" +
2000  filters.getParentLinkIndex() + ":" + filters.getParentLinkID() + "/" +
2001  module.second.getValue();
2002  __COUTT__ << "Inserting filter @table parameters... " << localParentPath3
2003  << __E__;
2004  insertParameters(out,
2005  tabStr,
2006  commentStr,
2007  localParentPath3,
2008  filterNodeParameterLink,
2009  "filterParameter" /*parameterType*/,
2010  true /*onlyInsertAtTableParameters*/,
2011  false /*includeAtTableParameters*/);
2012  if(module.second.status() &&
2013  module.second.getNode("filterModuleType").getValue() == "")
2014  {
2015  std::string tmp = localParentPath2;
2016  localParentPath2 = localParentPath3;
2017  OUTCL2F("# skipping '" << module.second.getValue()
2018  << "' with empty module type",
2019  "" /* comment */,
2020  "filterModuleType");
2021  localParentPath2 = tmp;
2022  continue;
2023  }
2024 
2025  OUT << module.second.getNode("filterKey").getValue() << ": {\n";
2026  PUSHTAB;
2027 
2028  insertModuleType(out,
2029  tabStr,
2030  commentStr,
2031  localParentPath3,
2032  module.second.getNode("filterModuleType"));
2033 
2034  //--------------------------------------
2035  // handle NOT @table:: filter parameters
2036  __COUTT__ << "Inserting filter not @table parameters... "
2037  << localParentPath3 << __E__;
2038  insertParameters(out,
2039  tabStr,
2040  commentStr,
2041  localParentPath3,
2042  filterNodeParameterLink,
2043  "filterParameter" /*parameterType*/,
2044  false /*onlyInsertAtTableParameters*/,
2045  false /*includeAtTableParameters*/);
2046 
2047  POPTAB;
2048  OUT << "}\n"; // end filter module
2049 
2050  if(!module.second.status())
2051  POPCOMMENT;
2052  } //end filter module loop
2053  POPTAB;
2054  OUT << "} # end physics.filters\n\n"; // end filters
2055  }
2056  else
2057  {
2058  __COUTS__(3) << "No filters found" << __E__;
2059  std::string localParentPath2 =
2060  localParentPath + "/" + services.getParentLinkColumnName();
2061  OUTCL2("# no filters found", "" /* comment*/);
2062  }
2063 
2064  //--------------------------------------
2065  // handle NOT @table:: physics parameters
2066  __COUTT__ << "Inserting art.physics not @table parameters... " << localParentPath
2067  << __E__;
2068  insertParameters(out,
2069  tabStr,
2070  commentStr,
2071  localParentPath,
2072  physics.getNode("physicsOtherParametersLink"),
2073  "physicsParameter" /*parameterType*/,
2074  false /*onlyInsertAtTableParameters*/,
2075  false /*includeAtTableParameters*/);
2076 
2077  POPTAB;
2078  OUT << "} # end physics\n\n"; // end physics
2079  }
2080  else
2081  {
2082  __COUTS__(3) << "No physics found" << __E__;
2083  std::string localParentPath =
2084  parentPath + "/" + physics.getParentLinkColumnName();
2085  OUTCL("# no physics found", "" /* comment*/);
2086  }
2087 
2088  //--------------------------------------
2089  // handle source
2090  __COUTT__ << "Filling art.source" << __E__;
2091  auto source = art.getNode("sourceLink");
2092  if(!source.isDisconnected())
2093  {
2094  __COUTT__ << "Inserting source... " << parentPath << __E__;
2095  std::string localParentPath = parentPath + "/" +
2096  source.getParentLinkColumnName() + ":" +
2097  source.getTableName() + "/" +
2098  source.getValue(); //unique link so can go further
2099  OUTCL("source: {", source.hasComment() ? source.getComment() : "");
2100  PUSHTAB;
2102  out, tabStr, commentStr, parentPath, source.getNode("sourceModuleType"));
2103  POPTAB;
2104  OUT << "}\n\n"; // end source
2105  }
2106  else
2107  {
2108  std::string localParentPath = parentPath + "/" + source.getParentLinkColumnName();
2109  OUTCL("source: { # auto-generated default, to change provide a source link",
2110  "" /* comment*/);
2111  PUSHTAB;
2112  OUT << "module_type: ArtdaqInput";
2113  POPTAB;
2114  OUT << "}\n\n"; // end source
2115  }
2116 
2117  //--------------------------------------
2118  // handle process_name
2119  __COUTT__ << "Writing art.process_name" << __E__;
2120  OUTCF("process_name: " << art.getNode(ARTDAQTableBase::colARTDAQArt_.colProcessName_),
2121  "",
2122  ARTDAQTableBase::colARTDAQArt_.colProcessName_);
2123 
2124  //--------------------------------------
2125  // handle art @table:: art add on parameters
2126  __COUTT__ << "Inserting art @table parameters... " << parentPath << __E__;
2127  insertParameters(out,
2128  tabStr,
2129  commentStr,
2130  parentPath,
2131  art.getNode("AddOnParametersLink"),
2132  "daqParameter" /*parameterType*/,
2133  false /*onlyInsertAtTableParameters*/,
2134  true /*includeAtTableParameters*/);
2135 
2136 } // end insertArtProcessBlock()
2137 
2138 //==============================================================================
2139 void ARTDAQTableBase::outputRoutingManagerFHICL(
2140  const ConfigurationTree& routingManagerNode,
2141  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
2142  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */)
2143 {
2144  if(ARTDAQ_DONOTWRITE_FCL)
2145  {
2146  __COUT__ << "Skipping fcl generation." << __E__;
2147  return;
2148  }
2149 
2150  std::string filename =
2151  getFHICLFilename(ARTDAQAppType::RoutingManager, routingManagerNode.getValue());
2152 
2154  // generate xdaq run parameter file
2155  std::fstream out;
2156 
2157  std::string tabStr = "";
2158  std::string commentStr = "";
2159 
2160  __COUTV__(filename);
2161  out.open(filename, std::fstream::out | std::fstream::trunc);
2162  if(out.fail())
2163  {
2164  __SS__ << "Failed to open ARTDAQ RoutingManager fcl file: " << filename << __E__;
2165  __SS_THROW__;
2166  }
2167 
2168  try //catch and give error in fcl file if issue!
2169  {
2170  //--------------------------------------
2171  // header
2172  OUT << "###########################################################" << __E__;
2173  OUT << "#" << __E__;
2174  OUT << "# artdaq " << getTypeString(ARTDAQAppType::RoutingManager)
2175  << " fcl configuration file produced by otsdaq." << __E__;
2176  OUT << "# Creation time: \t"
2177  << StringMacros::getTimestampString() << __E__;
2178  OUT << "# Original filename: \t" << filename << __E__;
2179  OUT << "# otsdaq-ARTDAQ RoutingManager UID:\t" << routingManagerNode.getValue()
2180  << __E__;
2181  OUT << "#" << __E__;
2182  OUT << "###########################################################" << __E__;
2183  OUT << "\n\n";
2184 
2185  // no primary link to table tree for reader node!
2186  try
2187  {
2188  if(routingManagerNode.isDisconnected())
2189  {
2190  // create empty fcl
2191  OUT << "{}\n\n";
2192  out.close();
2193  return;
2194  }
2195  }
2196  catch(const std::runtime_error&)
2197  {
2198  //__COUT__ << "Ignoring error, assume this a valid UID node." << __E__;
2199  // error is expected here for UIDs.. so just ignore
2200  // this check is valuable if source node is a unique-Link node, rather than UID
2201  }
2202 
2203  //--------------------------------------
2204  // handle daq
2205  OUT << "daq: {\n";
2206  PUSHTAB;
2207 
2208  OUT << "policy: {\n";
2209  PUSHTAB;
2210  auto policyName =
2211  routingManagerNode.getNode("routingPolicyPluginType").getValue();
2212  if(policyName == "DEFAULT")
2213  policyName = "NoOp";
2214  OUT << "policy: " << policyName << "\n";
2215  OUT << "receiver_ranks: []\n";
2216 
2217  // shared and unique parameters
2218  auto parametersLink = routingManagerNode.getNode("routingPolicyParametersLink");
2219  if(!parametersLink.isDisconnected())
2220  {
2221  auto parameters = parametersLink.getChildren();
2222  for(auto& parameter : parameters)
2223  {
2224  if(!parameter.second.status())
2225  PUSHCOMMENT;
2226 
2227  // __COUT__ <<
2228  // parameter.second.getNode("daqParameterKey").getValue() <<
2229  // ": " <<
2230  // parameter.second.getNode("daqParameterValue").getValue()
2231  // <<
2232  // "\n";
2233 
2234  auto comment =
2235  parameter.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT);
2236  OUT << parameter.second.getNode("daqParameterKey").getValue() << ": "
2237  << parameter.second.getNode("daqParameterValue").getValue()
2238  << (comment.isDefaultValue() ? "" : ("\t # " + comment.getValue()))
2239  << "\n";
2240 
2241  if(!parameter.second.status())
2242  POPCOMMENT;
2243  }
2244  }
2245 
2246  POPTAB;
2247  OUT << "}\n";
2248 
2249  OUT << "use_routing_manager: true\n";
2250 
2251  auto routingManagerSubsystemID = 1;
2252  auto routingManagerSubsystemLink = routingManagerNode.getNode("SubsystemLink");
2253  std::string rmHost = "localhost";
2254  if(!routingManagerSubsystemLink.isDisconnected())
2255  {
2256  routingManagerSubsystemID = getSubsytemId(routingManagerSubsystemLink);
2257  rmHost = info_.subsystems[routingManagerSubsystemID].routingManagerHost;
2258  }
2259  if(rmHost == "localhost" || rmHost == "127.0.0.1")
2260  {
2261  char hostbuf[HOST_NAME_MAX + 1];
2262  gethostname(hostbuf, HOST_NAME_MAX);
2263  rmHost = std::string(hostbuf);
2264  }
2265 
2266  // Bookkept parameters
2267  OUT << "routing_manager_hostname: \"" << rmHost << "\"\n";
2268  OUT << "sender_ranks: []\n";
2269  OUT << "table_update_port: 0\n";
2270  OUT << "table_update_address: \"0.0.0.0\"\n";
2271  OUT << "table_acknowledge_port: 0\n";
2272  OUT << "token_receiver: {\n";
2273  PUSHTAB;
2274 
2275  OUT << "routing_token_port: 0\n";
2276 
2277  POPTAB;
2278  OUT << "}\n";
2279 
2280  // Optional parameters
2281  auto tableUpdateIntervalMs =
2282  routingManagerNode.getNode("tableUpdateIntervalMs").getValue();
2283  if(tableUpdateIntervalMs != "DEFAULT")
2284  {
2285  OUT << "table_update_interval_ms: " << tableUpdateIntervalMs << "\n";
2286  }
2287  auto tableAckRetryCount =
2288  routingManagerNode.getNode("tableAckRetryCount").getValue();
2289  if(tableAckRetryCount != "DEFAULT")
2290  {
2291  OUT << "table_ack_retry_count: " << tableAckRetryCount << "\n";
2292  }
2293 
2294  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
2295  OUT << "routing_retry_count: " << routingRetryCount << "\n";
2296 
2297  std::string parentPath = routingManagerNode.getParentTableName() + "/" +
2298  routingManagerNode.getParentRecordName() + "/" +
2299  routingManagerNode.getParentLinkColumnName() + ":" +
2300  routingManagerNode.getTableName() + "/" +
2301  routingManagerNode.getValue();
2302  insertMetricsBlock(OUT, tabStr, commentStr, parentPath, routingManagerNode);
2303 
2304  POPTAB;
2305  OUT << "}\n\n"; // end daq
2306  __COUTT__ << "outputReaderFHICL DONE" << __E__;
2307  }
2308  catch(...)
2309  {
2310  __SS__ << "\n\nError while generating FHiCL for "
2311  << getTypeString(ARTDAQAppType::RoutingManager) << " node at filename '"
2312  << filename << "'" << __E__;
2313  try
2314  {
2315  throw;
2316  }
2317  catch(const std::runtime_error& e)
2318  {
2319  ss << " Here is the error: " << e.what() << __E__;
2320  }
2321  catch(const std::exception& e)
2322  {
2323  ss << " Here is the error: " << e.what() << __E__;
2324  }
2325  out << ss.str();
2326  out.close();
2327  __SS_THROW__;
2328  }
2329  out.close();
2330 } // end outputReaderFHICL()
2331 
2332 //==============================================================================
2333 const ARTDAQTableBase::ARTDAQInfo& ARTDAQTableBase::extractARTDAQInfo(
2334  ConfigurationTree artdaqSupervisorNode,
2335  bool getStatusFalseNodes /* = false */,
2336  bool doWriteFHiCL /* = false */,
2337  size_t maxFragmentSizeBytes /* = DEFAULT_MAX_FRAGMENT_SIZE*/,
2338  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
2339  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */,
2340  ProgressBar* progressBar /* = 0 */)
2341 {
2342  if(progressBar)
2343  progressBar->step();
2344 
2345  // reset info every time, because it could be called after configuration manipulations
2346  info_.subsystems.clear();
2347  info_.processes.clear();
2348 
2349  if(progressBar)
2350  progressBar->step();
2351 
2352  info_.subsystems[NULL_SUBSYSTEM_DESTINATION].id = NULL_SUBSYSTEM_DESTINATION;
2353  info_.subsystems[NULL_SUBSYSTEM_DESTINATION].label = NULL_SUBSYSTEM_DESTINATION_LABEL;
2354 
2355  // if no supervisor, then done
2356  if(artdaqSupervisorNode.isDisconnected())
2357  {
2358  __COUT__ << "artdaqSupervisorNode is disconnected." << __E__;
2359  return info_;
2360  }
2361 
2362  // We do RoutingManagers first so we can properly fill in routing tables later
2363  extractRoutingManagersInfo(artdaqSupervisorNode,
2364  getStatusFalseNodes,
2365  doWriteFHiCL,
2366  routingTimeoutMs,
2367  routingRetryCount);
2368  __COUT__ << "artdaqSupervisorNode RoutingManager size: "
2369  << info_.processes.at(ARTDAQAppType::RoutingManager).size() << __E__;
2370 
2371  if(progressBar)
2372  progressBar->step();
2373 
2374  extractBoardReadersInfo(artdaqSupervisorNode,
2375  getStatusFalseNodes,
2376  doWriteFHiCL,
2377  maxFragmentSizeBytes,
2378  routingTimeoutMs,
2379  routingRetryCount);
2380  __COUT__ << "artdaqSupervisorNode BoardReader size: "
2381  << info_.processes.at(ARTDAQAppType::BoardReader).size() << __E__;
2382 
2383  if(progressBar)
2384  progressBar->step();
2385 
2386  extractEventBuildersInfo(
2387  artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
2388  __COUT__ << "artdaqSupervisorNode EventBuilder size: "
2389  << info_.processes.at(ARTDAQAppType::EventBuilder).size() << __E__;
2390 
2391  if(progressBar)
2392  progressBar->step();
2393 
2394  extractDataLoggersInfo(
2395  artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
2396  __COUT__ << "artdaqSupervisorNode DataLogger size: "
2397  << info_.processes.at(ARTDAQAppType::DataLogger).size() << __E__;
2398 
2399  if(progressBar)
2400  progressBar->step();
2401 
2402  extractDispatchersInfo(
2403  artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
2404  __COUT__ << "artdaqSupervisorNode Dispatcher size: "
2405  << info_.processes.at(ARTDAQAppType::Dispatcher).size() << __E__;
2406 
2407  if(progressBar)
2408  progressBar->step();
2409 
2410  return info_;
2411 } // end extractARTDAQInfo()
2412 
2413 //==============================================================================
2414 void ARTDAQTableBase::extractRoutingManagersInfo(ConfigurationTree artdaqSupervisorNode,
2415  bool getStatusFalseNodes,
2416  bool doWriteFHiCL,
2417  size_t routingTimeoutMs,
2418  size_t routingRetryCount)
2419 {
2420  __COUT__ << "Checking for Routing Managers..." << __E__;
2421  info_.processes[ARTDAQAppType::RoutingManager].clear();
2422 
2423  ConfigurationTree rmsLink =
2424  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToRoutingManagers_);
2425  if(!rmsLink.isDisconnected() && rmsLink.getChildren().size() > 0)
2426  {
2427  std::vector<std::pair<std::string, ConfigurationTree>> routingManagers =
2428  rmsLink.getChildren();
2429 
2430  __COUT__ << "There are " << routingManagers.size()
2431  << " configured Routing Managers" << __E__;
2432 
2433  for(auto& routingManager : routingManagers)
2434  {
2435  const std::string& rmUID = routingManager.first;
2436 
2437  if(getStatusFalseNodes || routingManager.second.status())
2438  {
2439  std::string rmHost =
2440  routingManager.second
2441  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
2442  .getValueWithDefault("localhost");
2443  if(rmHost == "localhost" || rmHost == "127.0.0.1")
2444  {
2445  char hostbuf[HOST_NAME_MAX + 1];
2446  gethostname(hostbuf, HOST_NAME_MAX);
2447  rmHost = std::string(hostbuf);
2448  }
2449 
2450  std::string rmAP =
2451  routingManager.second
2452  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS)
2453  .getValueWithDefault("");
2454 
2455  int routingManagerSubsystemID = 1;
2456  ConfigurationTree routingManagerSubsystemLink =
2457  routingManager.second.getNode(
2458  ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
2459  if(!routingManagerSubsystemLink.isDisconnected())
2460  {
2461  routingManagerSubsystemID =
2462  getSubsytemId(routingManagerSubsystemLink);
2463 
2464  //__COUTV__(routingManagerSubsystemID);
2465  info_.subsystems[routingManagerSubsystemID].id =
2466  routingManagerSubsystemID;
2467 
2468  const std::string& routingManagerSubsystemName =
2469  routingManagerSubsystemLink.getUIDAsString();
2470  //__COUTV__(routingManagerSubsystemName);
2471 
2472  info_.subsystems[routingManagerSubsystemID].label =
2473  routingManagerSubsystemName;
2474 
2475  if(info_.subsystems[routingManagerSubsystemID].hasRoutingManager)
2476  {
2477  __SS__ << "Error: You cannot have multiple Routing Managers in a "
2478  "subsystem!";
2479  __SS_THROW__;
2480  return;
2481  }
2482 
2483  auto routingManagerSubsystemDestinationLink =
2484  routingManagerSubsystemLink.getNode(
2485  colARTDAQSubsystem_.colLinkToDestination_);
2486  if(routingManagerSubsystemDestinationLink.isDisconnected())
2487  {
2488  // default to no destination when no link
2489  info_.subsystems[routingManagerSubsystemID].destination = 0;
2490  }
2491  else
2492  {
2493  // get destination subsystem id
2494  info_.subsystems[routingManagerSubsystemID].destination =
2495  getSubsytemId(routingManagerSubsystemDestinationLink);
2496  }
2497  //__COUTV__(info_.subsystems[routingManagerSubsystemID].destination);
2498 
2499  // add this subsystem to destination subsystem's sources, if not
2500  // there
2501  if(!info_.subsystems.count(
2502  info_.subsystems[routingManagerSubsystemID].destination) ||
2503  !info_
2504  .subsystems[info_.subsystems[routingManagerSubsystemID]
2505  .destination]
2506  .sources.count(routingManagerSubsystemID))
2507  {
2508  info_
2509  .subsystems[info_.subsystems[routingManagerSubsystemID]
2510  .destination]
2511  .sources.insert(routingManagerSubsystemID);
2512  }
2513 
2514  } // end subsystem instantiation
2515 
2516  __COUT__ << "Found Routing Manager with UID " << rmUID
2517  << ", DAQInterface Hostname " << rmHost << ", and Subsystem "
2518  << routingManagerSubsystemID << __E__;
2519  info_.processes[ARTDAQAppType::RoutingManager].emplace_back(
2520  rmUID,
2521  rmHost,
2522  rmAP,
2523  routingManagerSubsystemID,
2524  ARTDAQAppType::RoutingManager,
2525  routingManager.second.status());
2526 
2527  info_.subsystems[routingManagerSubsystemID].hasRoutingManager = true;
2528  info_.subsystems[routingManagerSubsystemID].routingManagerHost = rmHost;
2529 
2530  if(doWriteFHiCL)
2531  {
2532  outputRoutingManagerFHICL(
2533  routingManager.second, routingTimeoutMs, routingRetryCount);
2534 
2535  flattenFHICL(ARTDAQAppType::RoutingManager,
2536  routingManager.second.getValue());
2537  }
2538  }
2539  else // disabled
2540  {
2541  __COUT__ << "Routing Manager " << rmUID << " is disabled." << __E__;
2542  }
2543  } // end routing manager loop
2544  }
2545 } // end extractRoutingManagersInfo()
2546 
2547 //==============================================================================
2548 void ARTDAQTableBase::extractBoardReadersInfo(ConfigurationTree artdaqSupervisorNode,
2549  bool getStatusFalseNodes,
2550  bool doWriteFHiCL,
2551  size_t maxFragmentSizeBytes,
2552  size_t routingTimeoutMs,
2553  size_t routingRetryCount)
2554 {
2555  __COUT__ << "Checking for Board Readers..." << __E__;
2556  info_.processes[ARTDAQAppType::BoardReader].clear();
2557 
2558  ConfigurationTree readersLink =
2559  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToBoardReaders_);
2560  if(!readersLink.isDisconnected() && readersLink.getChildren().size() > 0)
2561  {
2562  std::vector<std::pair<std::string, ConfigurationTree>> readers =
2563  readersLink.getChildren();
2564  __COUT__ << "There are " << readers.size() << " configured Board Readers."
2565  << __E__;
2566 
2567  for(auto& reader : readers)
2568  {
2569  const std::string& readerUID = reader.first;
2570 
2571  if(getStatusFalseNodes || reader.second.status())
2572  {
2573  std::string readerHost =
2574  reader.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
2575  .getValueWithDefault("localhost");
2576  std::string readerAP =
2577  reader.second
2578  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS)
2579  .getValueWithDefault("");
2580 
2581  int readerSubsystemID = 1;
2582  ConfigurationTree readerSubsystemLink =
2583  reader.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
2584  if(!readerSubsystemLink.isDisconnected())
2585  {
2586  readerSubsystemID = getSubsytemId(readerSubsystemLink);
2587  //__COUTV__(readerSubsystemID);
2588  info_.subsystems[readerSubsystemID].id = readerSubsystemID;
2589 
2590  const std::string& readerSubsystemName =
2591  readerSubsystemLink.getUIDAsString();
2592  //__COUTV__(readerSubsystemName);
2593 
2594  info_.subsystems[readerSubsystemID].label = readerSubsystemName;
2595 
2596  auto readerSubsystemDestinationLink = readerSubsystemLink.getNode(
2597  colARTDAQSubsystem_.colLinkToDestination_);
2598  if(readerSubsystemDestinationLink.isDisconnected())
2599  {
2600  // default to no destination when no link
2601  info_.subsystems[readerSubsystemID].destination = 0;
2602  }
2603  else
2604  {
2605  // get destination subsystem id
2606  info_.subsystems[readerSubsystemID].destination =
2607  getSubsytemId(readerSubsystemDestinationLink);
2608  }
2609  //__COUTV__(info_.subsystems[readerSubsystemID].destination);
2610 
2611  // add this subsystem to destination subsystem's sources, if not
2612  // there
2613  if(!info_.subsystems.count(
2614  info_.subsystems[readerSubsystemID].destination) ||
2615  !info_.subsystems[info_.subsystems[readerSubsystemID].destination]
2616  .sources.count(readerSubsystemID))
2617  {
2618  info_.subsystems[info_.subsystems[readerSubsystemID].destination]
2619  .sources.insert(readerSubsystemID);
2620  }
2621 
2622  } // end subsystem instantiation
2623 
2624  __COUT__ << "Found Board Reader with UID " << readerUID
2625  << ", DAQInterface Hostname " << readerHost << ", and Subsystem "
2626  << readerSubsystemID << __E__;
2627  info_.processes[ARTDAQAppType::BoardReader].emplace_back(
2628  readerUID,
2629  readerHost,
2630  readerAP,
2631  readerSubsystemID,
2632  ARTDAQAppType::BoardReader,
2633  reader.second.status());
2634 
2635  if(doWriteFHiCL)
2636  {
2637  outputBoardReaderFHICL(reader.second,
2638  maxFragmentSizeBytes,
2639  routingTimeoutMs,
2640  routingRetryCount);
2641 
2642  flattenFHICL(ARTDAQAppType::BoardReader, reader.second.getValue());
2643  }
2644  }
2645  else // disabled
2646  {
2647  __COUT__ << "Board Reader " << readerUID << " is disabled." << __E__;
2648  }
2649  } // end reader loop
2650  }
2651  else
2652  {
2653  __COUT_WARN__ << "There should be at least one Board Reader!";
2654  //__SS_THROW__;
2655  // return;
2656  }
2657 } // end extractBoardReadersInfo()
2658 
2659 //==============================================================================
2660 void ARTDAQTableBase::extractEventBuildersInfo(ConfigurationTree artdaqSupervisorNode,
2661  bool getStatusFalseNodes,
2662  bool doWriteFHiCL,
2663  size_t maxFragmentSizeBytes)
2664 {
2665  __COUT__ << "Checking for Event Builders..." << __E__;
2666  info_.processes[ARTDAQAppType::EventBuilder].clear();
2667 
2668  ConfigurationTree buildersLink =
2669  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToEventBuilders_);
2670  if(!buildersLink.isDisconnected() && buildersLink.getChildren().size() > 0)
2671  {
2672  std::vector<std::pair<std::string, ConfigurationTree>> builders =
2673  buildersLink.getChildren();
2674 
2675  std::string lastBuilderFcl[2],
2676  flattenedLastFclParts
2677  [2]; //same handling as otsdaq/otsdaq/TablePlugins/ARTDAQEventBuilderTable_table.cc:69
2678  for(auto& builder : builders)
2679  {
2680  const std::string& builderUID = builder.first;
2681  __COUTV__(builderUID);
2682 
2683  if(getStatusFalseNodes || builder.second.status())
2684  {
2685  std::string builderHost =
2686  builder.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
2687  .getValueWithDefault("localhost");
2688  std::string builderAP =
2689  builder.second
2690  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS)
2691  .getValueWithDefault("");
2692 
2693  int builderSubsystemID = 1;
2694  ConfigurationTree builderSubsystemLink =
2695  builder.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
2696  if(!builderSubsystemLink.isDisconnected())
2697  {
2698  builderSubsystemID = getSubsytemId(builderSubsystemLink);
2699  //__COUTV__(builderSubsystemID);
2700 
2701  info_.subsystems[builderSubsystemID].id = builderSubsystemID;
2702 
2703  const std::string& builderSubsystemName =
2704  builderSubsystemLink.getUIDAsString();
2705  //__COUTV__(builderSubsystemName);
2706 
2707  info_.subsystems[builderSubsystemID].label = builderSubsystemName;
2708 
2709  auto builderSubsystemDestinationLink = builderSubsystemLink.getNode(
2710  colARTDAQSubsystem_.colLinkToDestination_);
2711  if(builderSubsystemDestinationLink.isDisconnected())
2712  {
2713  // default to no destination when no link
2714  info_.subsystems[builderSubsystemID].destination = 0;
2715  }
2716  else
2717  {
2718  // get destination subsystem id
2719  info_.subsystems[builderSubsystemID].destination =
2720  getSubsytemId(builderSubsystemDestinationLink);
2721  }
2722  //__COUTV__(info_.subsystems[builderSubsystemID].destination);
2723 
2724  // add this subsystem to destination subsystem's sources, if not
2725  // there
2726  if(!info_.subsystems.count(
2727  info_.subsystems[builderSubsystemID].destination) ||
2728  !info_.subsystems[info_.subsystems[builderSubsystemID].destination]
2729  .sources.count(builderSubsystemID))
2730  {
2731  info_.subsystems[info_.subsystems[builderSubsystemID].destination]
2732  .sources.insert(builderSubsystemID);
2733  }
2734 
2735  } // end subsystem instantiation
2736 
2737  __COUT__ << "Found Event Builder with UID " << builderUID
2738  << ", on Hostname " << builderHost << ", in Subsystem "
2739  << builderSubsystemID << __E__;
2740  info_.processes[ARTDAQAppType::EventBuilder].emplace_back(
2741  builderUID,
2742  builderHost,
2743  builderAP,
2744  builderSubsystemID,
2745  ARTDAQAppType::EventBuilder,
2746  builder.second.status());
2747 
2748  if(doWriteFHiCL)
2749  {
2750  std::string returnFcl, processName;
2751  bool needToFlatten = true;
2752  bool captureAsLastFcl =
2753  builders
2754  .size() && //init to true if multiple builders left to handle
2755  (&builder != &builders.back());
2756  outputDataReceiverFHICL(builder.second,
2757  ARTDAQAppType::EventBuilder,
2758  maxFragmentSizeBytes,
2759  DEFAULT_ROUTING_TIMEOUT_MS,
2760  DEFAULT_ROUTING_RETRY_COUNT,
2761  captureAsLastFcl ? &returnFcl : nullptr);
2762 
2763  //Speed-up Philosophy:
2764  // flattenFHICL is expensive, so try to identify multinodes with fcl that only differ by process_name,
2765  // i.e., ignore starting comments and process name, then compare fcl.
2766  // Note: not much gain for any other node types but Event Builders, which tend to only differ by process_name in their fcl
2767 
2768  auto cmi = returnFcl.find(
2769  "# otsdaq-ARTDAQ builder UID:"); //find starting comments
2770  if(cmi != std::string::npos)
2771  cmi = returnFcl.find('\n', cmi);
2772  if(cmi != std::string::npos)
2773  {
2774  size_t pnj = std::string::npos;
2775  auto pni =
2776  returnFcl.find("\tprocess_name: ", cmi); //find process name
2777  if(pni != std::string::npos)
2778  {
2779  pni += std::string("\tprocess_name: ")
2780  .size(); //move past field name
2781  pnj = returnFcl.find('\n', pni);
2782  }
2783  if(pnj != std::string::npos)
2784  {
2785  processName = returnFcl.substr(pni, pnj - pni);
2786  __COUT__ << "Found process name = " << processName << __E__;
2787 
2788  bool sameFirst = false;
2789  //check before process name (ignoring comments)
2790  std::string newPiece = returnFcl.substr(cmi, pni - cmi);
2791  if(flattenedLastFclParts[0].size() &&
2792  lastBuilderFcl[0].size() && lastBuilderFcl[0] == newPiece)
2793  {
2794  __COUT__ << "Same first fcl" << __E__;
2795  sameFirst = true;
2796  }
2797  else if(TTEST(20))
2798  {
2799  __COUTVS__(20, lastBuilderFcl[0]);
2800  __COUTVS__(20, newPiece);
2801  for(size_t i = 0, j = 0;
2802  i < lastBuilderFcl[0].size() && j < newPiece.size();
2803  ++i, ++j)
2804  {
2805  if(lastBuilderFcl[0][i] != newPiece[j])
2806  {
2807  __COUTVS__(20, i);
2808  __COUTVS__(20, j);
2809  __COUTVS__(20, lastBuilderFcl[0].substr(i, 30));
2810  __COUTVS__(20, newPiece.substr(j, 30));
2811  break;
2812  }
2813  }
2814  }
2815  if(captureAsLastFcl) //if more, save piece
2816  lastBuilderFcl[0] = newPiece;
2817 
2818  //check after process name
2819  newPiece = returnFcl.substr(pnj);
2820  if(lastBuilderFcl[0].size() && lastBuilderFcl[1] == newPiece)
2821  {
2822  __COUT__ << "Same second fcl" << __E__;
2823  if(sameFirst) //found opportunity for shortcut-to-flatten!
2824  {
2825  std::chrono::steady_clock::time_point startClock =
2826  std::chrono::steady_clock::now();
2827  __COUT__ << "Found fcl match! Reuse for "
2828  << builderUID << __E__;
2829  captureAsLastFcl =
2830  false; //do not overwrite current last fcl now!
2831  needToFlatten = false;
2832 
2833  //do rapid flatten here
2834  std::string outFile = getFlatFHICLFilename(
2835  ARTDAQAppType::EventBuilder, builderUID);
2836  __COUTVS__(3, outFile);
2837  std::ofstream ofs{outFile};
2838  if(!ofs)
2839  {
2840  __SS__ << "Failed to open fhicl output file '"
2841  << outFile << "!'" << __E__;
2842  __SS_THROW__;
2843  }
2844  ofs << flattenedLastFclParts[0] << "process_name: \""
2845  << processName << "\""
2846  << flattenedLastFclParts[1];
2847  __COUTT__
2848  << builderUID << " Flatten Clock time = "
2849  << artdaq::TimeUtils::GetElapsedTime(startClock)
2850  << __E__;
2851  continue; //done with shortcut-to-flatten
2852  } //end shortcut-to-flatten handling
2853  }
2854  if(captureAsLastFcl) //if interesting for more, save piece
2855  lastBuilderFcl[1] = newPiece;
2856  }
2857  }
2858 
2859  if(needToFlatten)
2860  ARTDAQTableBase::flattenFHICL(
2861  ARTDAQAppType::EventBuilder,
2862  builderUID,
2863  captureAsLastFcl ? &returnFcl : nullptr);
2864  else
2865  __COUT__ << "Skipping full flatten for " << builderUID << __E__;
2866 
2867  //save parts without process name
2868  __COUTV__(captureAsLastFcl);
2869  if(captureAsLastFcl)
2870  {
2871  size_t pnj = std::string::npos;
2872  auto pni = returnFcl.find("process_name:"); //find process name
2873  if(pni != std::string::npos)
2874  {
2875  //enforce white space before process name
2876  if(pni &&
2877  (returnFcl[pni - 1] == ' ' || returnFcl[pni - 1] == '\n' ||
2878  returnFcl[pni - 1] == '\t'))
2879  pnj = returnFcl.find('\n', pni);
2880  }
2881  if(pnj != std::string::npos)
2882  {
2883  __COUT__
2884  << "Found flattened '" //Note: returnFcl.substr(pni, pnj - pni) includes "process_name:"
2885  << returnFcl.substr(pni, pnj - pni) << "' at pos " << pni
2886  << " of " << returnFcl.size() << __E__;
2887  flattenedLastFclParts[0] = returnFcl.substr(0, pni);
2888  flattenedLastFclParts[1] = returnFcl.substr(pnj);
2889  }
2890  else
2891  {
2892  __COUT_WARN__ << "Failed to capture fcl for " << processName
2893  << "!" << __E__;
2894  }
2895  }
2896  } //end doWriteFHiCL
2897  }
2898  else // disabled
2899  {
2900  __COUT__ << "Event Builder " << builderUID << " is disabled." << __E__;
2901  }
2902  } // end builder loop
2903  }
2904  else
2905  {
2906  __COUT_WARN__ << "There should be at least one Event Builder!";
2907  //__SS_THROW__;
2908  // return;
2909  }
2910 } // end extractEventBuildersInfo()
2911 
2912 //==============================================================================
2913 void ARTDAQTableBase::extractDataLoggersInfo(ConfigurationTree artdaqSupervisorNode,
2914  bool getStatusFalseNodes,
2915  bool doWriteFHiCL,
2916  size_t maxFragmentSizeBytes)
2917 {
2918  __COUT__ << "Checking for Data Loggers..." << __E__;
2919  info_.processes[ARTDAQAppType::DataLogger].clear();
2920 
2921  ConfigurationTree dataloggersLink =
2922  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToDataLoggers_);
2923  if(!dataloggersLink.isDisconnected())
2924  {
2925  std::vector<std::pair<std::string, ConfigurationTree>> dataloggers =
2926  dataloggersLink.getChildren();
2927 
2928  for(auto& datalogger : dataloggers)
2929  {
2930  const std::string& loggerUID = datalogger.first;
2931 
2932  if(getStatusFalseNodes || datalogger.second.status())
2933  {
2934  std::string loggerHost =
2935  datalogger.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
2936  .getValueWithDefault("localhost");
2937  std::string loggerAP =
2938  datalogger.second
2939  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS)
2940  .getValueWithDefault("");
2941 
2942  int loggerSubsystemID = 1;
2943  ConfigurationTree loggerSubsystemLink =
2944  datalogger.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
2945  if(!loggerSubsystemLink.isDisconnected())
2946  {
2947  loggerSubsystemID = getSubsytemId(loggerSubsystemLink);
2948  //__COUTV__(loggerSubsystemID);
2949  info_.subsystems[loggerSubsystemID].id = loggerSubsystemID;
2950 
2951  const std::string& loggerSubsystemName =
2952  loggerSubsystemLink.getUIDAsString();
2953  //__COUTV__(loggerSubsystemName);
2954 
2955  info_.subsystems[loggerSubsystemID].label = loggerSubsystemName;
2956 
2957  auto loggerSubsystemDestinationLink = loggerSubsystemLink.getNode(
2958  colARTDAQSubsystem_.colLinkToDestination_);
2959  if(loggerSubsystemDestinationLink.isDisconnected())
2960  {
2961  // default to no destination when no link
2962  info_.subsystems[loggerSubsystemID].destination = 0;
2963  }
2964  else
2965  {
2966  // get destination subsystem id
2967  info_.subsystems[loggerSubsystemID].destination =
2968  getSubsytemId(loggerSubsystemDestinationLink);
2969  }
2970  //__COUTV__(info_.subsystems[loggerSubsystemID].destination);
2971 
2972  // add this subsystem to destination subsystem's sources, if not
2973  // there
2974  if(!info_.subsystems.count(
2975  info_.subsystems[loggerSubsystemID].destination) ||
2976  !info_.subsystems[info_.subsystems[loggerSubsystemID].destination]
2977  .sources.count(loggerSubsystemID))
2978  {
2979  info_.subsystems[info_.subsystems[loggerSubsystemID].destination]
2980  .sources.insert(loggerSubsystemID);
2981  }
2982 
2983  } // end subsystem instantiation
2984 
2985  __COUT__ << "Found Data Logger with UID " << loggerUID
2986  << ", DAQInterface Hostname " << loggerHost << ", and Subsystem "
2987  << loggerSubsystemID << __E__;
2988  info_.processes[ARTDAQAppType::DataLogger].emplace_back(
2989  loggerUID,
2990  loggerHost,
2991  loggerAP,
2992  loggerSubsystemID,
2993  ARTDAQAppType::DataLogger,
2994  datalogger.second.status());
2995 
2996  if(doWriteFHiCL)
2997  {
2998  outputDataReceiverFHICL(datalogger.second,
2999  ARTDAQAppType::DataLogger,
3000  maxFragmentSizeBytes);
3001 
3002  flattenFHICL(ARTDAQAppType::DataLogger, datalogger.second.getValue());
3003  }
3004  }
3005  else // disabled
3006  {
3007  __COUT__ << "Data Logger " << loggerUID << " is disabled." << __E__;
3008  }
3009  } // end logger loop
3010  }
3011  else
3012  {
3013  __COUT_WARN__ << "There were no Data Loggers found!";
3014  }
3015 } // end extractDataLoggersInfo()
3016 
3017 //==============================================================================
3018 void ARTDAQTableBase::extractDispatchersInfo(ConfigurationTree artdaqSupervisorNode,
3019  bool getStatusFalseNodes,
3020  bool doWriteFHiCL,
3021  size_t maxFragmentSizeBytes)
3022 {
3023  __COUT__ << "Checking for Dispatchers..." << __E__;
3024  info_.processes[ARTDAQAppType::Dispatcher].clear();
3025 
3026  ConfigurationTree dispatchersLink =
3027  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToDispatchers_);
3028  if(!dispatchersLink.isDisconnected())
3029  {
3030  std::vector<std::pair<std::string, ConfigurationTree>> dispatchers =
3031  dispatchersLink.getChildren();
3032 
3033  for(auto& dispatcher : dispatchers)
3034  {
3035  const std::string& dispatcherUID = dispatcher.first;
3036 
3037  if(getStatusFalseNodes || dispatcher.second.status())
3038  {
3039  std::string dispatcherHost =
3040  dispatcher.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME)
3041  .getValueWithDefault("localhost");
3042  std::string dispatcherAP =
3043  dispatcher.second
3044  .getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS)
3045  .getValueWithDefault("");
3046  int dispatcherPort =
3047  dispatcher.second.getNode("DispatcherPort").getValue<int>();
3048 
3049  auto dispatcherSubsystemID = 1;
3050  ConfigurationTree dispatcherSubsystemLink =
3051  dispatcher.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
3052  if(!dispatcherSubsystemLink.isDisconnected())
3053  {
3054  dispatcherSubsystemID = getSubsytemId(dispatcherSubsystemLink);
3055  //__COUTV__(dispatcherSubsystemID);
3056  info_.subsystems[dispatcherSubsystemID].id = dispatcherSubsystemID;
3057 
3058  const std::string& dispatcherSubsystemName =
3059  dispatcherSubsystemLink.getUIDAsString();
3060  //__COUTV__(dispatcherSubsystemName);
3061 
3062  info_.subsystems[dispatcherSubsystemID].label =
3063  dispatcherSubsystemName;
3064 
3065  auto dispatcherSubsystemDestinationLink =
3066  dispatcherSubsystemLink.getNode(
3067  colARTDAQSubsystem_.colLinkToDestination_);
3068  if(dispatcherSubsystemDestinationLink.isDisconnected())
3069  {
3070  // default to no destination when no link
3071  info_.subsystems[dispatcherSubsystemID].destination = 0;
3072  }
3073  else
3074  {
3075  // get destination subsystem id
3076  info_.subsystems[dispatcherSubsystemID].destination =
3077  getSubsytemId(dispatcherSubsystemDestinationLink);
3078  }
3079  //__COUTV__(info_.subsystems[dispatcherSubsystemID].destination);
3080 
3081  // add this subsystem to destination subsystem's sources, if not
3082  // there
3083  if(!info_.subsystems.count(
3084  info_.subsystems[dispatcherSubsystemID].destination) ||
3085  !info_
3086  .subsystems[info_.subsystems[dispatcherSubsystemID]
3087  .destination]
3088  .sources.count(dispatcherSubsystemID))
3089  {
3090  info_
3091  .subsystems[info_.subsystems[dispatcherSubsystemID]
3092  .destination]
3093  .sources.insert(dispatcherSubsystemID);
3094  }
3095  }
3096 
3097  __COUT__ << "Found Dispatcher with UID " << dispatcherUID
3098  << ", DAQInterface Hostname " << dispatcherHost
3099  << ", and Subsystem " << dispatcherSubsystemID << __E__;
3100  info_.processes[ARTDAQAppType::Dispatcher].emplace_back(
3101  dispatcherUID,
3102  dispatcherHost,
3103  dispatcherAP,
3104  dispatcherSubsystemID,
3105  ARTDAQAppType::Dispatcher,
3106  dispatcher.second.status(),
3107  dispatcherPort);
3108 
3109  if(doWriteFHiCL)
3110  {
3111  outputDataReceiverFHICL(dispatcher.second,
3112  ARTDAQAppType::Dispatcher,
3113  maxFragmentSizeBytes);
3114 
3115  flattenFHICL(ARTDAQAppType::Dispatcher, dispatcher.second.getValue());
3116  }
3117  }
3118  else // disabled
3119  {
3120  __COUT__ << "Dispatcher " << dispatcherUID << " is disabled." << __E__;
3121  }
3122  } // end dispatcher loop
3123  }
3124  else
3125  {
3126  __COUT_WARN__ << "There were no Dispatchers found!";
3127  }
3128 } // end extractDispatchersInfo()
3129 
3130 //==============================================================================
3133 {
3134  auto contexts =
3135  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME).getChildren();
3136  for(auto context : contexts)
3137  {
3138  if(!context.second.isEnabled())
3139  continue;
3140 
3141  auto apps = context.second
3142  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
3143  .getChildren();
3144  for(auto app : apps)
3145  {
3146  // __COUTV__(app.second.getNode(XDAQContextTable::colApplication_.colClass_).getValue());
3147  if(app.second.getNode(XDAQContextTable::colApplication_.colClass_)
3148  .getValue() == ARTDAQ_SUPERVISOR_CLASS &&
3149  app.second.isEnabled())
3150  return true;
3151  }
3152  }
3153  return false;
3154 } // end isARTDAQEnabled()
3155 
3156 //==============================================================================
3166  ConfigurationManagerRW* cfgMgr,
3167  std::map<std::string /*type*/,
3168  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>&
3169  nodeTypeToObjectMap,
3170  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>&
3171  subsystemObjectMap,
3172  std::vector<std::string /*property*/>& artdaqSupervisoInfo)
3173 {
3174  __COUT__ << "getARTDAQSystem()" << __E__;
3175 
3176  artdaqSupervisoInfo.clear(); // init
3177 
3178  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
3179 
3180  // for each artdaq context, output all artdaq apps
3181 
3182  const XDAQContextTable::XDAQContext* artdaqContext =
3183  contextTable->getTheARTDAQSupervisorContext();
3184 
3185  // return empty info
3186  if(!artdaqContext)
3187  return ARTDAQTableBase::info_;
3188 
3189  __COUTV__(artdaqContext->contextUID_);
3190  __COUTV__(artdaqContext->applications_.size());
3191 
3192  // load artdaq node layout as multi-node printer-syntax guide
3193  std::map<std::string /*type*/, std::set<std::string /*node-names*/>> nodeLayoutNames;
3194  { //copied from handleLoadArtdaqNodeLayoutXML() at otsdaq-utilities/otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.cc:8054
3195  const std::string& finalContextGroupName =
3196  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE);
3197  const TableGroupKey& finalContextGroupKey =
3198  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE);
3199  const std::string& finalConfigGroupName = cfgMgr->getActiveGroupName(
3200  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
3201  const TableGroupKey& finalConfigGroupKey = cfgMgr->getActiveGroupKey(
3202  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
3203 
3204  FILE* fp = nullptr;
3205  //first try context+config name only
3206  {
3207  std::stringstream layoutPath;
3208  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH
3209  << finalContextGroupName << "_" << finalContextGroupKey << "."
3210  << finalConfigGroupName << "_" << finalConfigGroupKey << ".dat";
3211 
3212  fp = fopen(layoutPath.str().c_str(), "r");
3213  if(!fp)
3214  {
3215  __COUT__ << "Layout file not found for '" << finalContextGroupName << "("
3216  << finalContextGroupKey << ") + " << finalConfigGroupName << "("
3217  << finalConfigGroupKey << ")': " << layoutPath.str() << __E__;
3218  // return; //try context only!
3219  }
3220  else
3221  __COUTV__(layoutPath.str());
3222  }
3223  //last try context name only
3224  {
3225  std::stringstream layoutPath;
3226  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH
3227  << finalContextGroupName << "_" << finalContextGroupKey << ".dat";
3228  __COUTV__(layoutPath.str());
3229 
3230  fp = fopen(layoutPath.str().c_str(), "r");
3231  if(!fp)
3232  {
3233  __COUT__ << "Layout file not found for '" << finalContextGroupName << "("
3234  << finalContextGroupKey << ")': " << layoutPath.str() << __E__;
3235  }
3236  else
3237  __COUTV__(layoutPath.str());
3238  }
3239 
3240  if(!fp) //since exact context name was not found, see if there is a best match layout file
3241  {
3242  DIR* pDIR;
3243  struct dirent* entry;
3244  bool isDir;
3245  std::string name;
3246  int type;
3247 
3248  float bestScore = 0, score; //high score wins
3249  std::string bestName = "";
3250 
3251  if(!(pDIR = opendir((ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH).c_str())))
3252  {
3253  __SS__ << "Path '" << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH
3254  << "' could not be opened!" << __E__;
3255  __SS_THROW__;
3256  }
3257 
3258  // else directory good, get all folders, .h, .cc, .txt files
3259  while((entry = readdir(pDIR)))
3260  {
3261  name = std::string(entry->d_name);
3262  type = int(entry->d_type);
3263 
3264  __COUTS__(2) << type << " " << name << "\n" << std::endl;
3265 
3266  if(name[0] != '.' &&
3267  (type == 0 || // 0 == UNKNOWN (which can happen - seen in SL7 VM)
3268  type == 4 || // directory type
3269  type == 8 || // file type
3270  type ==
3271  10 // 10 == link (could be directory or file, treat as unknown)
3272  ))
3273  {
3274  isDir = false;
3275 
3276  if(type == 0 || type == 10)
3277  {
3278  // unknown type .. determine if directory
3279  DIR* pTmpDIR = opendir(
3280  (ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH + "/" + name)
3281  .c_str());
3282  if(pTmpDIR)
3283  {
3284  isDir = true;
3285  closedir(pTmpDIR);
3286  }
3287  else //assume file
3288  __COUTS__(2) << "Unable to open path as directory: "
3289  << (ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH +
3290  "/" + name)
3291  << __E__;
3292  }
3293 
3294  if(type == 4)
3295  isDir = true; // flag directory types
3296 
3297  // handle directories and files
3298 
3299  if(isDir)
3300  {
3301  __COUTS__(2) << "Directory: " << type << " " << name << __E__;
3302  }
3303  else
3304  {
3305  __COUTS__(2) << "File: " << type << " " << name << "\n"
3306  << std::endl;
3307  if(name.find(".dat") !=
3308  name.size() - 4) //skip if not proper file extension
3309  continue;
3310 
3311  __COUTS__(2) << "Contender: " << name << "\n" << std::endl;
3312  score = 0; //reset for score calc
3313 
3314  auto nameSplit = StringMacros::getVectorFromString(name, {'.'});
3315 
3316  if(nameSplit.size() > 1) //include config group in score
3317  {
3318  //match key to right of decimal and name to left of decimal
3319  auto keyi = nameSplit[1].rfind('_');
3320  if(keyi != std::string::npos)
3321  {
3322  int key =
3323  atoi(nameSplit[1]
3324  .substr(keyi, nameSplit[1].size() - 4 - keyi)
3325  .c_str());
3326  __COUTVS__(2, key);
3327  float tmpscore =
3328  finalConfigGroupKey.key() -
3329  key; //will be negative if comparing to newer key
3330  if(tmpscore < 0)
3331  tmpscore =
3332  -1 * tmpscore -
3333  1; //give penalty for newer keys (favor older keys)
3334  __COUTVS__(2, tmpscore);
3335  tmpscore =
3336  1.0 /
3337  tmpscore; //make high score be closest, and put value in decimal
3338  __COUTVS__(2, tmpscore);
3339 
3340  //now for each matching letter +1, for matching size +3
3341  std::string nameToCompare = nameSplit[1].substr(0, keyi);
3342  __COUTVS__(2, nameToCompare);
3343  size_t i = 0, j = 0;
3344  //match with both strings driving in case of jumps in the words
3345  for(; i < nameToCompare.size() &&
3346  j < finalConfigGroupName.size();
3347  ++i)
3348  {
3349  if(nameToCompare[i] == finalConfigGroupName[j])
3350  {
3351  tmpscore += 1.0;
3352  ++j;
3353  }
3354  }
3355  __COUTVS__(2, tmpscore);
3356  i = 0, j = 0;
3357  for(; i < nameToCompare.size() &&
3358  j < finalConfigGroupName.size();
3359  ++j)
3360  {
3361  if(nameToCompare[i] == finalConfigGroupName[j])
3362  {
3363  tmpscore += 1.0;
3364  ++i;
3365  }
3366  }
3367  __COUTVS__(2, tmpscore);
3368  score += tmpscore;
3369  }
3370  __COUTVS__(2, score);
3371  } //end config group score calc
3372  if(nameSplit.size() > 0) //include context group in score
3373  {
3374  //match key to right of decimal and name to left of decimal
3375  auto keyi = nameSplit[0].rfind('_');
3376  if(keyi != std::string::npos)
3377  {
3378  int key =
3379  atoi(nameSplit[0]
3380  .substr(keyi, nameSplit[0].size() - 4 - keyi)
3381  .c_str());
3382  __COUTVS__(2, key);
3383  float tmpscore =
3384  finalContextGroupKey.key() -
3385  key; //will be negative if comparing to newer key
3386  if(tmpscore < 0)
3387  tmpscore =
3388  -1 * tmpscore -
3389  1; //give penalty for newer keys (favor older keys)
3390  __COUTVS__(2, tmpscore);
3391  tmpscore =
3392  1.0 /
3393  tmpscore; //make high score be closest, and put value in decimal
3394  __COUTVS__(2, tmpscore);
3395 
3396  //now for each matching letter +1, for matching size +3
3397  std::string nameToCompare = nameSplit[0].substr(0, keyi);
3398  __COUTVS__(2, nameToCompare);
3399  size_t i = 0, j = 0;
3400  //match with both strings driving in case of jumps in the words
3401  for(; i < nameToCompare.size() &&
3402  j < finalContextGroupName.size();
3403  ++i)
3404  {
3405  if(nameToCompare[i] == finalContextGroupName[j])
3406  {
3407  tmpscore += 1.0;
3408  ++j;
3409  }
3410  }
3411  __COUTVS__(2, tmpscore);
3412  i = 0, j = 0;
3413  for(; i < nameToCompare.size() &&
3414  j < finalContextGroupName.size();
3415  ++j)
3416  {
3417  if(nameToCompare[i] == finalContextGroupName[j])
3418  {
3419  tmpscore += 1.0;
3420  ++i;
3421  }
3422  }
3423  __COUTVS__(2, tmpscore);
3424  score += tmpscore;
3425  }
3426  __COUTVS__(2, score);
3427  } //end context group score calc
3428 
3429  if(score > bestScore)
3430  {
3431  bestScore = score;
3432  bestName = name;
3433  __COUTVS__(2, bestName);
3434  }
3435  } //end score handling
3436  } //end file handling
3437  } //end directory search loop for best layout file
3438 
3439  if(bestName != "")
3440  {
3441  __COUT__ << "Found closest layout file name: " << bestName << ".dat"
3442  << __E__;
3443  std::stringstream layoutPath;
3444  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << bestName
3445  << ".dat";
3446  __COUTV__(layoutPath.str());
3447  fp = fopen(layoutPath.str().c_str(), "r");
3448  if(!fp)
3449  {
3450  __COUT__ << "Closest layout file not found for '" << bestName << "'"
3451  << __E__;
3452  }
3453  }
3454 
3455  //if(!fp) just ignore that file does not exist, and generate printer syntax from 1st principles
3456  } //end no layout file handling
3457 
3458  if(fp) //else if(!fp) just ignore that file does not exist, and generate printer syntax from 1st principles
3459  {
3460  __COUT__ << "Extract info from layout file.." << __E__;
3461 
3462  // file format is line by line
3463  // line 0 -- grid: <rows> <cols>
3464  // line 1-N -- node: <type> <name> <x-grid> <y-grid>
3465 
3466  const size_t maxLineSz = 1000;
3467  char line[maxLineSz];
3468  if(!fgets(line, maxLineSz, fp))
3469  {
3470  fclose(fp);
3471  __COUT__ << "No layout naming info found." << __E__;
3472  }
3473  else
3474  {
3475  // ignore grid hint and extract grid
3476 
3477  char name[maxLineSz];
3478  char type[maxLineSz];
3479  unsigned int x, y;
3480  while(fgets(line, maxLineSz, fp))
3481  {
3482  // extract node
3483  sscanf(line, "%s %s %u %u", type, name, &x, &y);
3484  nodeLayoutNames[type].emplace(name);
3485  } // end node extraction loop
3486 
3487  fclose(fp);
3488  }
3489 
3490  __COUTTV__(StringMacros::mapToString(nodeLayoutNames));
3491  }
3492  } //end load node layout helper guide
3493 
3494  //Strategy:
3495  // - Check for a count that match layout names
3496  // -- if any match, then keep those matching with that node layout name
3497  // -- otherwise allow auto-deduction of multinodes
3498 
3499  for(auto& artdaqApp : artdaqContext->applications_)
3500  {
3501  if(artdaqApp.class_ != ARTDAQ_SUPERVISOR_CLASS)
3502  continue;
3503 
3504  __COUTV__(artdaqApp.applicationUID_);
3505  artdaqSupervisoInfo.push_back(artdaqApp.applicationUID_);
3506  artdaqSupervisoInfo.push_back(
3507  (artdaqContext->status_ && artdaqApp.status_) ? "1" : "0");
3508  artdaqSupervisoInfo.push_back(artdaqContext->address_);
3509  artdaqSupervisoInfo.push_back(std::to_string(artdaqContext->port_));
3510 
3511  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::extractARTDAQInfo(
3512  XDAQContextTable::getSupervisorConfigNode(/*artdaqSupervisorNode*/
3513  cfgMgr,
3514  artdaqContext->contextUID_,
3515  artdaqApp.applicationUID_),
3516  true /*getStatusFalseNodes*/);
3517 
3518  __COUT__ << "========== "
3519  << "Found " << info.subsystems.size() << " subsystems." << __E__;
3520 
3521  // build subsystem desintation map
3522  for(auto& subsystem : info.subsystems)
3523  subsystemObjectMap.emplace(std::make_pair(
3524  subsystem.second.label, std::to_string(subsystem.second.destination)));
3525 
3526  __COUT__ << "========== "
3527  << "Found " << info.processes.size() << " process types." << __E__;
3528 
3529  for(auto& nameTypePair : ARTDAQTableBase::processTypes_.mapToType_)
3530  {
3531  const std::string& typeString = nameTypePair.first;
3532  __COUTV__(typeString);
3533 
3534  nodeTypeToObjectMap.emplace(
3535  std::make_pair(typeString,
3536  std::map<std::string /*record*/,
3537  std::vector<std::string /*property*/>>()));
3538 
3539  auto it = info.processes.find(nameTypePair.second);
3540  if(it == info.processes.end())
3541  {
3542  __COUT__ << "\t"
3543  << "Found 0 " << typeString << __E__;
3544  continue;
3545  }
3546  __COUT__ << "\t"
3547  << "Found " << it->second.size() << " " << typeString << "(s)"
3548  << __E__;
3549 
3550  auto tableIt = processTypes_.mapToTable_.find(typeString);
3551  if(tableIt == processTypes_.mapToTable_.end())
3552  {
3553  __SS__ << "Invalid artdaq node type '" << typeString << "' attempted!"
3554  << __E__;
3555  __SS_THROW__;
3556  }
3557  __COUTV__(tableIt->second);
3558 
3559  {
3560  std::stringstream ss;
3561  cfgMgr->getTableByName(tableIt->second)->getView().print(ss);
3562  __COUT_MULTI__(1, ss.str());
3563  }
3564 
3565  auto allNodes = cfgMgr->getNode(tableIt->second).getChildren();
3566 
3567  std::set<
3568  std::
3569  string /* encodeURI nodeName */> //use StringMacros::encodeURIComponent because dashes will confuse printer syntax later!
3570  skipSet; // use to skip nodes when constructing multi-nodes
3571 
3572  const std::set<std::string /*colName*/> skipColumns(
3573  {ARTDAQ_TYPE_TABLE_HOSTNAME,
3574  ARTDAQ_TYPE_TABLE_ALLOWED_PROCESSORS,
3575  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK,
3576  colARTDAQReader_
3577  .colDaqFragmentIDs_, //for board readers, skip the 'unique' fragment IDs when considering for multinode
3578  TableViewColumnInfo::COL_NAME_COMMENT,
3579  TableViewColumnInfo::COL_NAME_AUTHOR,
3580  TableViewColumnInfo::
3581  COL_NAME_CREATION}); // note: also skip UID and Status
3582 
3583  if(TTEST(1) && nodeLayoutNames.find(typeString) != nodeLayoutNames.end())
3584  {
3585  __COUTTV__(StringMacros::setToString(nodeLayoutNames.at(typeString)));
3586  }
3587 
3588  // loop through all nodes of this type
3589  for(auto& artdaqNode : it->second)
3590  {
3591  // check skip set
3592  if(skipSet.find(StringMacros::encodeURIComponent(artdaqNode.label)) !=
3593  skipSet.end())
3594  continue;
3595 
3596  __COUT__ << "\t\t"
3597  << "Found '" << artdaqNode.label << "' " << typeString << __E__;
3598 
3599  std::string nodeName = artdaqNode.label;
3600  bool status = artdaqNode.status;
3601  std::string hostname = artdaqNode.hostname;
3602  std::string subsystemId = std::to_string(artdaqNode.subsystem);
3603  std::string subsystemName =
3604  info.subsystems.at(artdaqNode.subsystem).label;
3605 
3606  ConfigurationTree thisNode =
3607  cfgMgr->getNode(tableIt->second).getNode(nodeName);
3608  auto thisNodeColumns = thisNode.getChildren();
3609 
3610  // check for multi-node
3611  // Steps:
3612  // - search for other records to include with same values/links except hostname/name
3613  // - if match to layout nodes, then maintain layout template
3614 
3615  std::vector<std::string> multiNodeNames, hostnameArray;
3616  // unsigned int hostnameFixedWidth = 0;
3617 
3618  skipSet
3619  .emplace( //emplace self into skipset since this node is handled now (and should not be considered in future multinode instances)
3620  StringMacros::encodeURIComponent(nodeName));
3621 
3622  __COUTV__(allNodes.size());
3623  for(auto& otherNode : allNodes) // start multi-node search loop
3624  {
3625  if(skipSet.find(StringMacros::encodeURIComponent(otherNode.first)) !=
3626  skipSet.end() ||
3627  otherNode.second.status() != status) // skip if status mismatch
3628  continue; // skip unless 'other' and not in skip set
3629 
3630  //__COUTV__(subsystemName);
3631  //__COUTV__(otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID).getValue());
3632 
3633  if(subsystemName ==
3634  otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID)
3635  .getValue())
3636  {
3637  // possible multi-node situation
3638  //__COUT__ << "Checking for multi-node..." << __E__;
3639 
3640  //__COUTV__(thisNode.getNodeRow());
3641  //__COUTV__(otherNode.second.getNodeRow());
3642 
3643  auto otherNodeColumns = otherNode.second.getChildren();
3644 
3645  bool isMultiNode = true;
3646  for(unsigned int i = 0;
3647  i < thisNodeColumns.size() && i < otherNodeColumns.size();
3648  ++i)
3649  {
3650  // skip columns that do not need to be checked for multi-node consideration
3651  if(skipColumns.find(thisNodeColumns[i].first) !=
3652  skipColumns.end() ||
3653  thisNodeColumns[i].second.isLinkNode())
3654  continue;
3655 
3656  // at this point must match for multinode
3657 
3658  //__COUTV__(thisNodeColumns[i].first);
3659  //__COUTV__(otherNodeColumns[i].first);
3660 
3661  //__COUTV__(thisNodeColumns[i].second.getValue());
3662  //__COUTV__(otherNodeColumns[i].second.getValue());
3663 
3664  if(thisNodeColumns[i].second.getValue() !=
3665  otherNodeColumns[i].second.getValue())
3666  {
3667  __COUT__ << "Mismatch, not multi-node member." << __E__;
3668  isMultiNode = false;
3669  break;
3670  }
3671  }
3672 
3673  if(isMultiNode)
3674  {
3675  __COUT__ << "Found '" << nodeName
3676  << "' multi-node member candidate '"
3677  << otherNode.first << "'" << __E__;
3678 
3679  //use StringMacros::encodeURIComponent because dashes will confuse printer syntax later!
3680  if(!multiNodeNames.size()) // add this node first!
3681  {
3682  multiNodeNames.push_back(
3683  StringMacros::encodeURIComponent(nodeName));
3684  hostnameArray.push_back(
3685  StringMacros::encodeURIComponent(hostname));
3686  }
3687  multiNodeNames.push_back(
3688  StringMacros::encodeURIComponent(otherNode.first));
3689  hostnameArray.push_back(StringMacros::encodeURIComponent(
3690  otherNode.second.getNode(ARTDAQ_TYPE_TABLE_HOSTNAME)
3691  .getValue()));
3692 
3693  __COUTV__(hostnameArray.back());
3694  skipSet.emplace(
3695  StringMacros::encodeURIComponent(otherNode.first));
3696  }
3697  }
3698  } // end loop to search for multi-node members
3699 
3700  unsigned int nodeFixedWildcardLength = 0, hostFixedWildcardLength = 0;
3701  std::string multiNodeString = "", hostArrayString = "";
3702 
3703  __COUTV__(nodeName);
3704 
3705  if(multiNodeNames.size() > 1)
3706  {
3707  __COUT__ << "Handling multi-node printer syntax" << __E__;
3708 
3709  __COUTTV__(StringMacros::vectorToString(multiNodeNames));
3710  __COUTTV__(StringMacros::vectorToString(hostnameArray));
3711  __COUTTV__(StringMacros::setToString(skipSet));
3712 
3713  // - if match to layout nodes, then maintain layout template, and trim outliers from skipset
3714  if(nodeLayoutNames.find(typeString) != nodeLayoutNames.end())
3715  {
3716  __COUTTV__(
3717  StringMacros::setToString(nodeLayoutNames.at(typeString)));
3718 
3719  // Strategy to use node layout as guide:
3720  // - do two passes
3721  // - find best node layout name based on narrowest match (i.e. smallest match count)
3722  std::string bestNodeLayoutName = "";
3723  size_t bestNodeLayoutMatchCount =
3724  multiNodeNames.size() + 1; //init to 'infinite'
3725  //first pass
3726  for(const auto& layoutNameFull : nodeLayoutNames.at(typeString))
3727  {
3728  __COUTTV__(layoutNameFull);
3729  size_t statusPos = layoutNameFull.find(";status=");
3730  std::string layoutName = layoutNameFull.substr(0, statusPos);
3731  bool layoutStatus = true;
3732  if(statusPos ==
3733  std::string::
3734  npos) //not specified in layout, so take this status and hope!
3735  layoutStatus = status;
3736  else if("0" ==
3737  layoutNameFull.substr(statusPos +
3738  std::string(";status=").size()))
3739  layoutStatus = false;
3740 
3741  __COUTTV__(layoutStatus);
3742 
3743  if(layoutStatus != status)
3744  {
3745  __COUTT__ << "Status mismatch for template" << __E__;
3746  break;
3747  }
3748 
3749  auto layoutSplit =
3750  StringMacros::getVectorFromString(layoutName, {'*'});
3751  __COUTTV__(StringMacros::vectorToString(layoutSplit));
3752 
3753  bool exactMatch = true;
3754  size_t pos = 0;
3755  for(const auto& layoutSeg : layoutSplit)
3756  if((pos = nodeName.find(layoutSeg, pos)) ==
3757  std::string::npos)
3758  {
3759  __COUTT__ << "Did not find '" << layoutSeg << "' in '"
3760  << nodeName << "'" << __E__;
3761  exactMatch = false;
3762  break;
3763  }
3764 
3765  __COUTTV__(exactMatch);
3766  if(exactMatch)
3767  {
3768  size_t nodeLayoutMatchCount = 1;
3769 
3770  __COUT__ << "Found layout template name match! '"
3771  << layoutName << "' for node '" << nodeName
3772  << ".' Trimming multinode candidates to match..."
3773  << __E__;
3774 
3775  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3776  {
3777  __COUTTV__(multiNodeNames[i]);
3778  std::string multiNodeName =
3780  multiNodeNames[i]);
3781  __COUTTV__(multiNodeName);
3782  bool exactMatch = true;
3783  size_t pos = 0;
3784  for(const auto& layoutSeg : layoutSplit)
3785  if((pos = multiNodeName.find(layoutSeg, pos)) ==
3786  std::string::npos)
3787  {
3788  __COUTT__ << "Did not find '" << layoutSeg
3789  << "' in '" << multiNodeName << "'"
3790  << __E__;
3791  exactMatch = false;
3792  break;
3793  }
3794 
3795  if(exactMatch)
3796  {
3797  ++nodeLayoutMatchCount;
3798  __COUTT__ << "Found '" << layoutName << "' in '"
3799  << multiNodeName << "'" << __E__;
3800  }
3801 
3802  } //end loop to trim multinode candidates
3803 
3804  __COUTTV__(nodeLayoutMatchCount);
3805  if(nodeLayoutMatchCount < bestNodeLayoutMatchCount)
3806  {
3807  bestNodeLayoutName = layoutNameFull;
3808  bestNodeLayoutMatchCount = nodeLayoutMatchCount;
3809  __COUTTV__(bestNodeLayoutName);
3810  __COUTTV__(bestNodeLayoutMatchCount);
3811  }
3812  }
3813  } //end first loop to find best layout node
3814 
3815  __COUTV__(nodeName);
3816  __COUTV__(StringMacros::vectorToString(multiNodeNames));
3817  __COUTV__(StringMacros::vectorToString(hostnameArray));
3818  __COUTV__(StringMacros::setToString(skipSet));
3819 
3820  //second pass, remove from skipSet
3821  if(bestNodeLayoutMatchCount > 0)
3822  {
3823  __COUTV__(bestNodeLayoutName);
3824  std::string layoutNameFull = bestNodeLayoutName;
3825  __COUTTV__(layoutNameFull);
3826  size_t statusPos = layoutNameFull.find(";status=");
3827  std::string layoutName = layoutNameFull.substr(0, statusPos);
3828 
3829  auto layoutSplit =
3830  StringMacros::getVectorFromString(layoutName, {'*'});
3831  __COUTTV__(StringMacros::vectorToString(layoutSplit));
3832 
3833  __COUT__ << "Found layout template name match! '"
3834  << layoutName << "' for node '" << nodeName
3835  << ".' Trimming multinode candidates to match..."
3836  << __E__;
3837 
3838  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3839  {
3840  __COUTTV__(multiNodeNames[i]);
3841  std::string multiNodeName =
3842  StringMacros::decodeURIComponent(multiNodeNames[i]);
3843  __COUTTV__(multiNodeName);
3844  bool exactMatch = true;
3845  size_t pos = 0;
3846  for(const auto& layoutSeg : layoutSplit)
3847  if((pos = multiNodeName.find(layoutSeg, pos)) ==
3848  std::string::npos)
3849  {
3850  __COUTT__ << "Did not find '" << layoutSeg
3851  << "' in '" << multiNodeName << "'"
3852  << __E__;
3853  exactMatch = false;
3854  break;
3855  }
3856 
3857  if(!exactMatch)
3858  {
3859  __COUT__ << "Trimming multinode candidate '"
3860  << multiNodeName << "'" << __E__;
3861  skipSet.erase(multiNodeNames[i]);
3862  multiNodeNames.erase(multiNodeNames.begin() + i);
3863  hostnameArray.erase(hostnameArray.begin() + i);
3864  --i; //rewind for multiNodeNames[i] erase
3865  }
3866  } //end loop to trim multinode candidates
3867  } //end applying layout template name rule
3868  } //end match to layout name templates
3869 
3870  __COUTV__(nodeName);
3871  __COUTV__(StringMacros::vectorToString(multiNodeNames));
3872  __COUTV__(StringMacros::vectorToString(hostnameArray));
3873  __COUTV__(StringMacros::setToString(skipSet));
3874 
3875  {
3876  // check for alpha-based similarity groupings (ignore numbers and special characters)
3877  unsigned int maxScore = 0;
3878  unsigned int score;
3879  unsigned int minScore = -1;
3880  std::vector<unsigned int> scoreVector;
3881  scoreVector.push_back(-1); // for 0 index (it's perfect)
3882  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3883  {
3884  score = 0;
3885 
3886  __COUTS__(3) << multiNodeNames[0] << " vs "
3887  << multiNodeNames[i] << __E__;
3888 
3889  // start forward score loop
3890  for(unsigned int j = 0, k = 0; j < multiNodeNames[0].size() &&
3891  k < multiNodeNames[i].size();
3892  ++j, ++k)
3893  {
3894  while(j < multiNodeNames[0].size() &&
3895  !(multiNodeNames[0][j] >= 'a' &&
3896  multiNodeNames[0][j] <= 'z') &&
3897  !(multiNodeNames[0][j] >= 'A' &&
3898  multiNodeNames[0][j] <= 'Z'))
3899  ++j; // skip non-alpha characters
3900  while(k < multiNodeNames[i].size() &&
3901  !(multiNodeNames[i][k] >= 'a' &&
3902  multiNodeNames[i][k] <= 'z') &&
3903  !(multiNodeNames[i][k] >= 'A' &&
3904  multiNodeNames[i][k] <= 'Z'))
3905  ++k; // skip non-alpha characters
3906 
3907  while(k < multiNodeNames[i].size() &&
3908  multiNodeNames[0][j] != multiNodeNames[i][k])
3909  ++k; // skip non-matching alpha characters
3910 
3911  __COUTS__(3)
3912  << j << "-" << k << " of " << multiNodeNames[0].size()
3913  << "-" << multiNodeNames[i].size() << __E__;
3914 
3915  if(j < multiNodeNames[0].size() &&
3916  k < multiNodeNames[i].size())
3917  ++score; // found a matching letter!
3918  } // end forward score loop
3919 
3920  __COUTVS__(3, score);
3921 
3922  // start backward score loop
3923  for(unsigned int j = multiNodeNames[0].size() - 1,
3924  k = multiNodeNames[i].size() - 1;
3925  j < multiNodeNames[0].size() &&
3926  k < multiNodeNames[i].size();
3927  --j, --k)
3928  {
3929  while(j < multiNodeNames[0].size() &&
3930  !(multiNodeNames[0][j] >= 'a' &&
3931  multiNodeNames[0][j] <= 'z') &&
3932  !(multiNodeNames[0][j] >= 'A' &&
3933  multiNodeNames[0][j] <= 'Z'))
3934  --j; // skip non-alpha characters
3935  while(k < multiNodeNames[i].size() &&
3936  !(multiNodeNames[i][k] >= 'a' &&
3937  multiNodeNames[i][k] <= 'z') &&
3938  !(multiNodeNames[i][k] >= 'A' &&
3939  multiNodeNames[i][k] <= 'Z'))
3940  --k; // skip non-alpha characters
3941 
3942  while(k < multiNodeNames[i].size() &&
3943  multiNodeNames[0][j] != multiNodeNames[i][k])
3944  --k; // skip non-matching alpha characters
3945 
3946  __COUTS__(3) << "BACK" << j << "-" << k << " of "
3947  << multiNodeNames[0].size() << "-"
3948  << multiNodeNames[i].size() << __E__;
3949 
3950  if(j < multiNodeNames[0].size() &&
3951  k < multiNodeNames[i].size())
3952  ++score; // found a matching letter!
3953  } // end backward score loop
3954 
3955  __COUTVS__(3, score / 2.0);
3956 
3957  scoreVector.push_back(score);
3958 
3959  if(score > maxScore)
3960  {
3961  maxScore = score;
3962  }
3963 
3964  if(score < minScore)
3965  {
3966  minScore = score;
3967  }
3968 
3969  } // end multi-node member scoring loop
3970 
3971  __COUTVS__(2, minScore);
3972  __COUTVS__(2, maxScore);
3973 
3974  __COUT__ << "Trimming multi-node members with low match score..."
3975  << __E__;
3976 
3977  // go backwards, to not mess up indices as deleted
3978  // do not delete index 0
3979  for(unsigned int i = multiNodeNames.size() - 1;
3980  i > 0 && i < multiNodeNames.size();
3981  --i)
3982  {
3983  //__COUTV__(scoreVector[i]);
3984  //__COUTV__(i);
3985  if(maxScore > multiNodeNames[0].size() &&
3986  scoreVector[i] >= maxScore)
3987  continue;
3988 
3989  // else trim
3990  __COUT__ << "Trimming low score match " << multiNodeNames[i]
3991  << " for node name " << nodeName << __E__;
3992 
3993  skipSet.erase(multiNodeNames[i]);
3994  multiNodeNames.erase(multiNodeNames.begin() + i);
3995  hostnameArray.erase(hostnameArray.begin() + i);
3996 
3997  } // end multi-node trim loop
3998 
3999  } // done with multi-node member trim
4000 
4001  __COUTV__(nodeName);
4002  __COUTV__(StringMacros::vectorToString(multiNodeNames));
4003  __COUTV__(StringMacros::vectorToString(hostnameArray));
4004  __COUTV__(StringMacros::setToString(skipSet));
4005 
4006  //set of names fully defined, reorder alphabettically
4007  {
4008  __COUT__ << "Reorganizing multinode '" << nodeName
4009  << "' alphabetically..." << __E__;
4010  std::set<
4011  std::pair<std::string /* node */, std::string /* host */>>
4012  reorderSet;
4013  for(unsigned int i = 0; i < multiNodeNames.size(); ++i)
4014  reorderSet.emplace(
4015  std::make_pair(multiNodeNames[i], hostnameArray[i]));
4016 
4017  __COUTV__(StringMacros::setToString(reorderSet));
4018  //skipset is unchanged, multiNodeNames and hostnameArray are reordered
4019 
4020  multiNodeNames.clear();
4021  hostnameArray.clear();
4022  for(const auto& orderedPair : reorderSet)
4023  {
4024  multiNodeNames.push_back(orderedPair.first);
4025  hostnameArray.push_back(orderedPair.second);
4026  }
4027 
4028  } //end reorder alphabetically
4029  __COUTV__(nodeName);
4030  __COUTV__(StringMacros::vectorToString(multiNodeNames));
4031  __COUTV__(StringMacros::vectorToString(hostnameArray));
4032  __COUTV__(StringMacros::setToString(skipSet));
4033 
4034  // from set of nodename wildcards, make printer syntax
4035  if(multiNodeNames.size() > 1)
4036  {
4037  std::vector<std::string> commonChunks;
4038  std::vector<std::string> wildcards;
4039 
4040  //can not change the order of wildcards for node names! or the names will not keep pairing with host
4041 
4042  bool wildcardsNeeded =
4043  StringMacros::extractCommonChunks(multiNodeNames,
4044  commonChunks,
4045  wildcards,
4046  nodeFixedWildcardLength);
4047 
4048  if(!wildcardsNeeded || wildcards.size() != multiNodeNames.size())
4049  {
4050  __SS__
4051  << "Impossible extractCommonChunks result! Please notify "
4052  "admins or try to simplify record naming convention."
4053  << __E__;
4054  __SS_THROW__;
4055  }
4056 
4057  __COUTV__(StringMacros::vectorToString(commonChunks));
4058  __COUTV__(StringMacros::vectorToString(wildcards));
4059 
4060  nodeName = "";
4061  bool first = true;
4062  for(auto& commonChunk : commonChunks)
4063  {
4064  nodeName += (!first ? "*" : "") + commonChunk;
4065  if(first)
4066  first = false;
4067  }
4068  if(commonChunks.size() == 1)
4069  nodeName += '*';
4070 
4071  __COUTV__(nodeName);
4072 
4073  // steps:
4074  // determine if all unsigned ints
4075  // if int, then order and attempt to hyphenate
4076  // if not ints, then comma separated
4077 
4078  bool allIntegers = true;
4079  for(auto& wildcard : wildcards)
4080  if(!allIntegers)
4081  break;
4082  else if(wildcard.size() == 0) // emtpy string is not a number
4083  {
4084  allIntegers = false;
4085  break;
4086  }
4087  else
4088  for(unsigned int i = 0; i < wildcard.size(); ++i)
4089  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
4090  {
4091  allIntegers = false;
4092  break;
4093  }
4094 
4095  __COUTV__(allIntegers);
4096  if(allIntegers)
4097  {
4098  __COUTV__(StringMacros::vectorToString(wildcards));
4099 
4100  // need ints in vector for random access to for hyphenating
4101  std::vector<unsigned int> intWildcards;
4102  for(auto& wildcard : wildcards)
4103  intWildcards.push_back(strtol(wildcard.c_str(), 0, 10));
4104 
4105  __COUTV__(StringMacros::vectorToString(intWildcards));
4106 
4107  unsigned int hyphenLo = -1;
4108  bool isFirst = true;
4109  for(unsigned int i = 0; i < intWildcards.size(); ++i)
4110  {
4111  if(i + 1 < intWildcards.size() &&
4112  intWildcards[i] + 1 == intWildcards[i + 1])
4113  {
4114  if(i < hyphenLo)
4115  hyphenLo = i; // start hyphen
4116  //else continue hyphen
4117  }
4118  else // new comma
4119  {
4120  if(i < hyphenLo)
4121  {
4122  // single number
4123  multiNodeString +=
4124  (isFirst ? "" : ",") +
4125  std::to_string(intWildcards[i]);
4126  }
4127  else
4128  {
4129  // if only 1 number apart, then comma
4130  if(intWildcards[hyphenLo] + 1 == intWildcards[i])
4131  multiNodeString +=
4132  (isFirst ? "" : ",") +
4133  std::to_string(intWildcards[hyphenLo]) +
4134  "," + std::to_string(intWildcards[i]);
4135  else // else hyphen numbers
4136  multiNodeString +=
4137  (isFirst ? "" : ",") +
4138  std::to_string(intWildcards[hyphenLo]) +
4139  "-" + std::to_string(intWildcards[i]);
4140  hyphenLo = -1; // reset for next
4141  }
4142  isFirst = false;
4143  }
4144  }
4145  } // end all integer handling
4146  else // not all integers, so csv
4147  {
4148  multiNodeString = StringMacros::vectorToString(wildcards);
4149  nodeFixedWildcardLength =
4150  0; //wipe out fixed length rule if not all numbers
4151  } // end not-all integer handling
4152 
4153  __COUTV__(multiNodeString);
4154  __COUTV__(nodeFixedWildcardLength);
4155  } // end node name printer syntax handling
4156 
4157  if(hostnameArray.size() > 1)
4158  {
4159  std::vector<std::string> commonChunks;
4160  std::vector<std::string> wildcards;
4161 
4162  //can not change the order of wildcards for hostname! or the names will not keep pairing with host
4163 
4164  bool wildcardsNeeded =
4165  StringMacros::extractCommonChunks(hostnameArray,
4166  commonChunks,
4167  wildcards,
4168  hostFixedWildcardLength);
4169 
4170  __COUTV__(wildcardsNeeded);
4171  __COUTV__(StringMacros::vectorToString(commonChunks));
4172  __COUTV__(StringMacros::vectorToString(wildcards));
4173 
4174  hostname = "";
4175  bool first = true;
4176  for(auto& commonChunk : commonChunks)
4177  {
4178  hostname += (!first ? "*" : "") + commonChunk;
4179  if(first)
4180  first = false;
4181  }
4182  if(wildcardsNeeded && commonChunks.size() == 1)
4183  hostname += '*';
4184 
4185  __COUTV__(hostname);
4186 
4187  if(wildcardsNeeded)
4188  // else if not wildcards needed, then do not make hostname array string
4189  {
4190  // steps:
4191  // determine if all unsigned ints
4192  // if int, then order and attempt to hyphenate
4193  // if not ints, then comma separated
4194 
4195  bool allIntegers = true;
4196  for(auto& wildcard : wildcards)
4197  for(unsigned int i = 0; i < wildcard.size(); ++i)
4198  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
4199  {
4200  allIntegers = false;
4201  break;
4202  }
4203 
4204  __COUTV__(allIntegers);
4205 
4206  if(allIntegers)
4207  {
4208  __COUTV__(StringMacros::vectorToString(wildcards));
4209 
4210  // need ints in vector for random access to for hyphenating
4211  std::vector<unsigned int> intWildcards;
4212  for(auto& wildcard : wildcards)
4213  intWildcards.push_back(
4214  strtol(wildcard.c_str(), 0, 10));
4215 
4216  __COUTV__(StringMacros::vectorToString(intWildcards));
4217 
4218  unsigned int hyphenLo = -1;
4219  bool isFirst = true;
4220  for(unsigned int i = 0; i < intWildcards.size(); ++i)
4221  {
4222  if(i + 1 < intWildcards.size() &&
4223  intWildcards[i] + 1 == intWildcards[i + 1])
4224  {
4225  if(i < hyphenLo)
4226  hyphenLo = i; // start hyphen
4227  //else continue hyphen
4228  }
4229  else // new comma
4230  {
4231  if(i < hyphenLo)
4232  {
4233  // single number
4234  hostArrayString +=
4235  (isFirst ? "" : ",") +
4236  std::to_string(intWildcards[i]);
4237  }
4238  else
4239  {
4240  // if only 1 number apart, then comma
4241  if(intWildcards[hyphenLo] + 1 ==
4242  intWildcards[i])
4243  hostArrayString +=
4244  (isFirst ? "" : ",") +
4245  std::to_string(
4246  intWildcards[hyphenLo]) +
4247  "," + std::to_string(intWildcards[i]);
4248  else // else hyphen numbers
4249  hostArrayString +=
4250  (isFirst ? "" : ",") +
4251  std::to_string(
4252  intWildcards[hyphenLo]) +
4253  "-" + std::to_string(intWildcards[i]);
4254  hyphenLo = -1; // reset for next
4255  }
4256  isFirst = false;
4257  }
4258  }
4259  } // end all integer handling
4260  else // not all integers, so csv
4261  {
4262  hostArrayString = StringMacros::vectorToString(wildcards);
4263  hostFixedWildcardLength =
4264  0; //wipe out fixed length rule if not all numbers
4265  } // end not-all integer handling
4266  } // end wildcard need handling
4267  __COUTV__(hostArrayString);
4268  __COUTV__(hostFixedWildcardLength);
4269  } // end node name printer syntax handling
4270 
4271  } // end multi node printer syntax handling
4272 
4273  nodeName +=
4274  ";status=" +
4275  std::string(status
4276  ? "1"
4277  : "0"); //include status in name to avoid collissions
4278  auto result = nodeTypeToObjectMap.at(typeString)
4279  .emplace(std::make_pair(
4280  nodeName, std::vector<std::string /*property*/>()));
4281  if(!result.second)
4282  {
4283  __SS__ << "Impossible printer syntax handling result! Collision of "
4284  "base names. Please notify "
4285  "admins or try to simplify record naming convention."
4286  << __E__;
4287  __SS_THROW__;
4288  }
4289 
4290  nodeTypeToObjectMap.at(typeString)
4291  .at(nodeName)
4292  .push_back(status ? "1" : "0");
4293 
4294  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(hostname);
4295 
4296  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(subsystemId);
4297  if(multiNodeNames.size() > 1)
4298  {
4299  nodeTypeToObjectMap.at(typeString)
4300  .at(nodeName)
4301  .push_back(multiNodeString);
4302 
4303  nodeTypeToObjectMap.at(typeString)
4304  .at(nodeName)
4305  .push_back(std::to_string(nodeFixedWildcardLength));
4306 
4307  if(hostnameArray.size() > 1)
4308  {
4309  nodeTypeToObjectMap.at(typeString)
4310  .at(nodeName)
4311  .push_back(hostArrayString);
4312 
4313  nodeTypeToObjectMap.at(typeString)
4314  .at(nodeName)
4315  .push_back(std::to_string(hostFixedWildcardLength));
4316  }
4317  } // done adding multinode parameters
4318 
4319  __COUTV__(multiNodeString);
4320  __COUTV__(StringMacros::decodeURIComponent(hostname));
4321  __COUTV__(hostArrayString);
4322  __COUT__ << "Done with extraction of node '" << nodeName << "'" << __E__;
4323  } //end main node extraction loop
4324  } // end processor type handling
4325 
4326  } // end artdaq app loop
4327 
4328  __COUT__ << "Done getting artdaq nodes." << __E__;
4329 
4330  return ARTDAQTableBase::info_;
4331 } // end getARTDAQSystem()
4332 
4333 //==============================================================================
4343  ConfigurationManagerRW* cfgMgr,
4344  const std::map<std::string /*type*/,
4345  std::map<std::string /*record*/,
4346  std::vector<std::string /*property*/>>>& nodeTypeToObjectMap,
4347  const std::map<std::string /*subsystemName*/,
4348  std::string /*destinationSubsystemName*/>& subsystemObjectMap)
4349 {
4350  __COUT__ << "setAndActivateARTDAQSystem()" << __E__;
4351 
4352  const std::string& author = cfgMgr->getUsername();
4353 
4354  // Steps:
4355  // 0. Check for one and only artdaq Supervisor
4356  // 1. create/verify subsystems and destinations
4357  // 2. for each node
4358  // create/verify records
4359 
4360  //------------------------
4361  // 0. Check for one and only artdaq Supervisor
4362 
4363  GroupEditStruct configGroupEdit(ConfigurationManager::GroupType::CONFIGURATION_TYPE,
4364  cfgMgr);
4365 
4366  unsigned int artdaqSupervisorRow = TableView::INVALID;
4367 
4368  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
4369 
4370  const XDAQContextTable::XDAQContext* artdaqContext =
4371  contextTable->getTheARTDAQSupervisorContext();
4372 
4373  bool needArtdaqSupervisorParents = true;
4374  bool needArtdaqSupervisorCreation = false;
4375 
4376  __COUTV__(artdaqContext);
4377  if(artdaqContext) // check for full connection to supervisor
4378  {
4379  try
4380  {
4381  const std::string& activeContextGroupName =
4382  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE);
4383  const TableGroupKey& activeContextGroupKey =
4384  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE);
4385  const std::string& activeConfigGroupName = cfgMgr->getActiveGroupName(
4386  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
4387  const TableGroupKey& activeConfigGroupKey = cfgMgr->getActiveGroupKey(
4388  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
4389 
4390  __COUTV__(activeContextGroupName);
4391  __COUTV__(activeContextGroupKey);
4392  __COUTV__(activeConfigGroupName);
4393  __COUTV__(activeConfigGroupKey);
4394  __COUTV__(cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4395  .getNode(artdaqContext->contextUID_)
4396  .getValueAsString());
4397  __COUTV__(
4398  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4399  .getNode(artdaqContext->contextUID_)
4400  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4401  .getValueAsString());
4402  __COUTV__(
4403  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4404  .getNode(artdaqContext->contextUID_)
4405  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4406  .getNode(artdaqContext->applications_[0].applicationUID_)
4407  .getValueAsString());
4408  __COUTV__(artdaqContext->applications_[0].applicationUID_);
4409  __COUTV__(XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
4410  __COUTV__(
4411  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4412  .getNode(artdaqContext->contextUID_)
4413  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4414  .getNode(artdaqContext->applications_[0].applicationUID_)
4415  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_)
4416  .getValueAsString());
4417 
4418  ConfigurationTree artdaqSupervisorNode =
4419  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4420  .getNode(artdaqContext->contextUID_)
4421  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4422  .getNode(artdaqContext->applications_[0].applicationUID_)
4423  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
4424 
4425  __COUTV__(artdaqSupervisorNode.isDisconnected());
4426 
4427  if(artdaqSupervisorNode.isDisconnected())
4428  needArtdaqSupervisorCreation = true;
4429  else
4430  artdaqSupervisorRow = artdaqSupervisorNode.getRow();
4431 
4432  needArtdaqSupervisorParents = false;
4433  }
4434  catch(...) // parents are a problem if error
4435  {
4436  needArtdaqSupervisorCreation = true;
4437  }
4438  __COUTV__(needArtdaqSupervisorCreation);
4439  }
4440 
4441  if(!artdaqContext || needArtdaqSupervisorCreation)
4442  {
4443  __COUT__ << "No artdaq Supervisor found! Creating..." << __E__;
4444  __COUTV__(needArtdaqSupervisorParents);
4445 
4446  std::string artdaqSupervisorUID;
4447  unsigned int row;
4448 
4449  // create record in ARTDAQ Supervisor table
4450  // connect to an App in a Context
4451 
4452  // now create artdaq Supervisor in configuration group
4453  {
4454  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(
4455  ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
4456 
4457  if(TTEST(0))
4458  {
4459  std::stringstream ss;
4460  artdaqSupervisorTable.tableView_->print(ss);
4461  __COUT_MULTI__(0, ss.str());
4462  }
4463 
4464  // create artdaq Supervisor context record
4465  row = artdaqSupervisorTable.tableView_->addRow(
4466  author, true /*incrementUniqueData*/, "artdaqSupervisor");
4467 
4468  // get UID
4469  artdaqSupervisorUID =
4470  artdaqSupervisorTable.tableView_
4471  ->getDataView()[row][artdaqSupervisorTable.tableView_->getColUID()];
4472  artdaqSupervisorRow = row;
4473 
4474  __COUTV__(artdaqSupervisorRow);
4475  __COUTV__(artdaqSupervisorUID);
4476 
4477  // set DAQInterfaceDebugLevel
4478  artdaqSupervisorTable.tableView_->setValueAsString(
4479  "1",
4480  row,
4481  artdaqSupervisorTable.tableView_->findCol(
4482  colARTDAQSupervisor_.colDAQInterfaceDebugLevel_));
4483  // set DAQSetupScript
4484  artdaqSupervisorTable.tableView_->setValueAsString(
4485  "${MRB_BUILDDIR}/../setup_ots.sh",
4486  row,
4487  artdaqSupervisorTable.tableView_->findCol(
4488  colARTDAQSupervisor_.colDAQSetupScript_));
4489 
4490  // create group link to board readers
4491  artdaqSupervisorTable.tableView_->setValueAsString(
4492  ARTDAQ_READER_TABLE,
4493  row,
4494  artdaqSupervisorTable.tableView_->findCol(
4495  colARTDAQSupervisor_.colLinkToBoardReaders_));
4496  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4497 
4498  row,
4499  artdaqSupervisorTable.tableView_->findCol(
4500  colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
4501  artdaqSupervisorUID +
4502  processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
4503  // create group link to event builders
4504  artdaqSupervisorTable.tableView_->setValueAsString(
4505  ARTDAQ_BUILDER_TABLE,
4506  row,
4507  artdaqSupervisorTable.tableView_->findCol(
4508  colARTDAQSupervisor_.colLinkToEventBuilders_));
4509  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4510  row,
4511  artdaqSupervisorTable.tableView_->findCol(
4512  colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
4513  artdaqSupervisorUID +
4514  processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
4515  // create group link to data loggers
4516  artdaqSupervisorTable.tableView_->setValueAsString(
4517  ARTDAQ_LOGGER_TABLE,
4518  row,
4519  artdaqSupervisorTable.tableView_->findCol(
4520  colARTDAQSupervisor_.colLinkToDataLoggers_));
4521  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4522  row,
4523  artdaqSupervisorTable.tableView_->findCol(
4524  colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
4525  artdaqSupervisorUID +
4526  processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
4527  // create group link to dispatchers
4528  artdaqSupervisorTable.tableView_->setValueAsString(
4529  ARTDAQ_DISPATCHER_TABLE,
4530  row,
4531  artdaqSupervisorTable.tableView_->findCol(
4532  colARTDAQSupervisor_.colLinkToDispatchers_));
4533  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4534  row,
4535  artdaqSupervisorTable.tableView_->findCol(
4536  colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
4537  artdaqSupervisorUID +
4538  processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
4539 
4540  // create group link to routing managers
4541  artdaqSupervisorTable.tableView_->setValueAsString(
4542  ARTDAQ_ROUTER_TABLE,
4543  row,
4544  artdaqSupervisorTable.tableView_->findCol(
4545  colARTDAQSupervisor_.colLinkToRoutingManagers_));
4546  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4547  row,
4548  artdaqSupervisorTable.tableView_->findCol(
4549  colARTDAQSupervisor_.colLinkToRoutingManagersGroupID_),
4550  artdaqSupervisorUID +
4551  processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
4552 
4553  if(TTEST(0))
4554  {
4555  std::stringstream ss;
4556  artdaqSupervisorTable.tableView_->print(ss);
4557  __COUT_MULTI__(0, ss.str());
4558  }
4559  } // end create artdaq Supervisor in configuration group
4560 
4561  // now create artdaq Supervisor parents in context group
4562  {
4563  GroupEditStruct contextGroupEdit(
4564  ConfigurationManager::GroupType::CONTEXT_TYPE, cfgMgr);
4565 
4566  TableEditStruct& contextTable = contextGroupEdit.getTableEditStruct(
4567  ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME, true /*markModified*/);
4568  TableEditStruct& appTable = contextGroupEdit.getTableEditStruct(
4569  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME, true /*markModified*/);
4570  TableEditStruct& appPropertyTable = contextGroupEdit.getTableEditStruct(
4571  ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME,
4572  true /*markModified*/);
4573 
4574  // open try for decorating errors and for clean code scope
4575  std::string appUID;
4576  try
4577  {
4578  std::string contextUID;
4579  std::string contextAppGroupID;
4580 
4581  if(needArtdaqSupervisorParents)
4582  {
4583  // create artdaq Supervisor context record
4584  row = contextTable.tableView_->addRow(
4585  author, true /*incrementUniqueData*/, "artdaqContext");
4586  // set context status true
4587  contextTable.tableView_->setValueAsString(
4588  "1", row, contextTable.tableView_->getColStatus());
4589 
4590  contextUID =
4591  contextTable.tableView_
4592  ->getDataView()[row][contextTable.tableView_->getColUID()];
4593 
4594  __COUTV__(row);
4595  __COUTV__(contextUID);
4596 
4597  // set address/port
4598  contextTable.tableView_->setValueAsString(
4599  "http://${HOSTNAME}",
4600  row,
4601  contextTable.tableView_->findCol(
4602  XDAQContextTable::colContext_.colAddress_));
4603  contextTable.tableView_->setUniqueColumnValue(
4604  row,
4605  contextTable.tableView_->findCol(
4606  XDAQContextTable::colContext_.colPort_),
4607  "${OTS_MAIN_PORT}",
4608  true /*doMathAppendStrategy*/);
4609 
4610  // create group link to artdaq Supervisor app
4611  contextTable.tableView_->setValueAsString(
4612  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME,
4613  row,
4614  contextTable.tableView_->findCol(
4615  XDAQContextTable::colContext_.colLinkToApplicationTable_));
4616  contextAppGroupID = contextTable.tableView_->setUniqueColumnValue(
4617  row,
4618  contextTable.tableView_->findCol(
4619  XDAQContextTable::colContext_.colLinkToApplicationGroupID_),
4620  "artdaqContextApps");
4621 
4622  __COUTV__(contextAppGroupID);
4623 
4624  } // end create context entry
4625 
4626  std::string appPropertiesGroupID;
4627 
4628  // create artdaq Supervisor app
4629  {
4630  unsigned int row;
4631 
4632  if(needArtdaqSupervisorParents)
4633  {
4634  // first disable any existing artdaq supervisor apps
4635  {
4636  unsigned int c = appTable.tableView_->findCol(
4637  XDAQContextTable::colApplication_.colClass_);
4638  for(unsigned int r = 0;
4639  r < appTable.tableView_->getNumberOfRows();
4640  ++r)
4641  if(appTable.tableView_->getDataView()[r][c] ==
4642  ARTDAQ_SUPERVISOR_CLASS)
4643  {
4644  __COUT_WARN__
4645  << "Found partially existing artdaq Supervisor "
4646  "application '"
4647  << appTable.tableView_->getDataView()
4648  [r][appTable.tableView_->getColUID()]
4649  << "'... Disabling it." << __E__;
4650  appTable.tableView_->setValueAsString(
4651  "0", r, appTable.tableView_->getColStatus());
4652  }
4653  }
4654 
4655  // create artdaq Supervisor context record
4656  row = appTable.tableView_->addRow(
4657  author, true /*incrementUniqueData*/, "artdaqSupervisor");
4658  // set app status true
4659  appTable.tableView_->setValueAsString(
4660  "1", row, appTable.tableView_->getColStatus());
4661 
4662  appUID =
4663  appTable.tableView_
4664  ->getDataView()[row][appTable.tableView_->getColUID()];
4665 
4666  __COUTV__(row);
4667  __COUTV__(appUID);
4668 
4669  // set class
4670  appTable.tableView_->setValueAsString(
4671  ARTDAQ_SUPERVISOR_CLASS,
4672  row,
4673  appTable.tableView_->findCol(
4674  XDAQContextTable::colApplication_.colClass_));
4675  // set module
4676  appTable.tableView_->setValueAsString(
4677  "${OTSDAQ_LIB}/libARTDAQSupervisor.so",
4678  row,
4679  appTable.tableView_->findCol(
4680  XDAQContextTable::colApplication_.colModule_));
4681  // set groupid
4682  appTable.tableView_->setValueAsString(
4683  contextAppGroupID,
4684  row,
4685  appTable.tableView_->findCol(XDAQContextTable::colApplication_
4686  .colApplicationGroupID_));
4687 
4688  // create group link to artdaq Supervisor app properties
4689  appTable.tableView_->setValueAsString(
4690  ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME,
4691  row,
4692  appTable.tableView_->findCol(XDAQContextTable::colApplication_
4693  .colLinkToPropertyTable_));
4694  appPropertiesGroupID = appTable.tableView_->setUniqueColumnValue(
4695  row,
4696  appTable.tableView_->findCol(XDAQContextTable::colApplication_
4697  .colLinkToPropertyGroupID_),
4698  appUID + "Properties");
4699 
4700  __COUTV__(appPropertiesGroupID);
4701  }
4702  else
4703  {
4704  __COUT__ << "Getting row of existing parent supervisor." << __E__;
4705 
4706  // get row of current artdaq supervisor app
4707  row =
4708  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4709  .getNode(artdaqContext->contextUID_)
4710  .getNode(XDAQContextTable::colContext_
4711  .colLinkToApplicationTable_)
4712  .getNode(artdaqContext->applications_[0].applicationUID_)
4713  .getRow();
4714  __COUTV__(row);
4715  }
4716 
4717  // create group link to artdaq Supervisor app properties
4718  // create link whether or not parents were created
4719  // because, if here, then artdaq supervisor record was created.
4720  appTable.tableView_->setValueAsString(
4721  ARTDAQ_SUPERVISOR_TABLE,
4722  row,
4723  appTable.tableView_->findCol(
4724  XDAQContextTable::colApplication_.colLinkToSupervisorTable_));
4725  appTable.tableView_->setValueAsString(
4726  artdaqSupervisorUID,
4727  row,
4728  appTable.tableView_->findCol(
4729  XDAQContextTable::colApplication_.colLinkToSupervisorUID_));
4730 
4731  } // end create app entry
4732 
4733  // create artdaq Supervisor properties
4734  if(needArtdaqSupervisorParents)
4735  {
4736  unsigned int row;
4737 
4738  const std::vector<std::string> propertyUIDs = {"Partition0",
4739  "ProductsDir",
4740  "FragmentSize",
4741  "BoardReaderTimeout",
4742  "EventBuilderTimeout",
4743  "DataLoggerTimeout",
4744  "DispatcherTimeout"};
4745  const std::vector<std::string> propertyNames = {
4746  "partition", //"Partition0",
4747  "productsdir_for_bash_scripts", //"ProductsDir",
4748  "max_fragment_size_bytes", //"FragmentSize",
4749  "boardreader_timeout", //"BoardReaderTimeout",
4750  "eventbuilder_timeout", //"EventBuilderTimeout",
4751  "datalogger_timeout", //"DataLoggerTimeout",
4752  "dispatcher_timeout" //"DispatcherTimeout"
4753  };
4754  const std::vector<std::string> propertyValues = {
4755  "0", //"Partition0",
4756  "${OTS_PRODUCTS}", //"ProductsDir",
4757  "1284180560", //"FragmentSize",
4758  "600", //"BoardReaderTimeout",
4759  "600", //"EventBuilderTimeout",
4760  "600", //"DataLoggerTimeout",
4761  "600" //"DispatcherTimeout"
4762  };
4763 
4764  for(unsigned int i = 0; i < propertyNames.size(); ++i)
4765  {
4766  // create artdaq Supervisor property record
4767  row = appPropertyTable.tableView_->addRow(
4768  author,
4769  true /*incrementUniqueData*/,
4770  appUID + propertyUIDs[i]);
4771  // set app status true
4772  appPropertyTable.tableView_->setValueAsString(
4773  "1", row, appPropertyTable.tableView_->getColStatus());
4774 
4775  // set type
4776  appPropertyTable.tableView_->setValueAsString(
4777  "ots::SupervisorProperty",
4778  row,
4779  appPropertyTable.tableView_->findCol(
4780  XDAQContextTable::colAppProperty_.colPropertyType_));
4781  // set name
4782  appPropertyTable.tableView_->setValueAsString(
4783  propertyNames[i],
4784  row,
4785  appPropertyTable.tableView_->findCol(
4786  XDAQContextTable::colAppProperty_.colPropertyName_));
4787  // set value
4788  appPropertyTable.tableView_->setValueAsString(
4789  propertyValues[i],
4790  row,
4791  appPropertyTable.tableView_->findCol(
4792  XDAQContextTable::colAppProperty_.colPropertyValue_));
4793  // set groupid
4794  appPropertyTable.tableView_->setValueAsString(
4795  appPropertiesGroupID,
4796  row,
4797  appPropertyTable.tableView_->findCol(
4798  XDAQContextTable::colAppProperty_.colPropertyGroupID_));
4799  } // end property create loop
4800  } // end create app property entries
4801 
4802  {
4803  std::stringstream ss;
4804  contextTable.tableView_->print(ss);
4805  __COUT_MULTI__(0, ss.str());
4806  }
4807  {
4808  std::stringstream ss;
4809  appTable.tableView_->print(ss);
4810  __COUT_MULTI__(0, ss.str());
4811  }
4812  {
4813  std::stringstream ss;
4814  appPropertyTable.tableView_->print(ss);
4815  __COUT_MULTI__(0, ss.str());
4816  }
4817 
4818  contextTable.tableView_
4819  ->init(); // verify new table (throws runtime_errors)
4820  appTable.tableView_->init(); // verify new table (throws runtime_errors)
4821  appPropertyTable.tableView_
4822  ->init(); // verify new table (throws runtime_errors)
4823  }
4824  catch(...)
4825  {
4826  __COUT__
4827  << "Table errors while creating ARTDAQ Supervisor. Erasing all newly "
4828  "created table versions."
4829  << __E__;
4830  throw; // re-throw
4831  } // end catch
4832 
4833  __COUT_INFO__ << "Edits complete for new artdaq Supervisor! Created '"
4834  << appUID << "'" << __E__;
4835 
4836  if(0) //keep for debugging save process
4837  {
4838  __SS__ << "DEBUG blocking artdaq supervisor save!" << __E__;
4839  __SS_THROW__;
4840  }
4841  TableGroupKey newContextGroupKey;
4842  contextGroupEdit.saveChanges(contextGroupEdit.originalGroupName_,
4843  newContextGroupKey,
4844  nullptr /*foundEquivalentGroupKey*/,
4845  true /*activateNewGroup*/,
4846  true /*updateGroupAliases*/,
4847  true /*updateTableAliases*/);
4848 
4849  } // end create artdaq Supervisor in context group
4850 
4851  } // end artdaq Supervisor verification
4852  else
4853  {
4854  artdaqSupervisorRow =
4855  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4856  .getNode(artdaqContext->contextUID_)
4857  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4858  .getNode(artdaqContext->applications_[0].applicationUID_)
4859  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_)
4860  .getRow();
4861  }
4862 
4863  __COUT__ << "------------------------- artdaq nodes to save:" << __E__;
4864  for(auto& subsystemPair : subsystemObjectMap)
4865  {
4866  __COUTV__(subsystemPair.first);
4867 
4868  } // end subsystem loop
4869 
4870  for(auto& nodeTypePair : nodeTypeToObjectMap)
4871  {
4872  __COUTV__(nodeTypePair.first);
4873 
4874  for(auto& nodePair : nodeTypePair.second)
4875  {
4876  __COUTV__(nodePair.first);
4877  }
4878 
4879  } // end node type loop
4880  __COUT__ << "------------------------- end artdaq nodes to save." << __E__;
4881 
4882  //==================================
4883  // at this point artdaqSupervisor is verified and we have row
4884  __COUTV__(artdaqSupervisorRow);
4885  if(artdaqSupervisorRow >= TableView::INVALID)
4886  {
4887  __SS__ << "Invalid artdaq Supervisor row " << artdaqSupervisorRow << " found!"
4888  << __E__;
4889  __SS_THROW__;
4890  }
4891 
4892  // Remaining steps:
4893  // Step 1. create/verify subsystems and destinations
4894  // Step 2. for each node, create/verify records
4895 
4896  // open try for decorating configuration group errors and for clean code scope
4897  try
4898  {
4899  unsigned int row;
4900 
4901  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(
4902  ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
4903 
4904  // for any NO_LINK links in artdaqSupervisor record, fix them
4905  {
4906  std::string artdaqSupervisorUID =
4907  artdaqSupervisorTable.tableView_
4908  ->getDataView()[artdaqSupervisorRow]
4909  [artdaqSupervisorTable.tableView_->getColUID()];
4910 
4911  // create group link to board readers
4912  if(artdaqSupervisorTable.tableView_
4913  ->getDataView()[artdaqSupervisorRow]
4914  [artdaqSupervisorTable.tableView_->findCol(
4915  colARTDAQSupervisor_.colLinkToBoardReaders_)] ==
4916  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4917  {
4918  __COUT__ << "Fixing missing link to Readers" << __E__;
4919  artdaqSupervisorTable.tableView_->setValueAsString(
4920  ARTDAQ_READER_TABLE,
4921  artdaqSupervisorRow,
4922  artdaqSupervisorTable.tableView_->findCol(
4923  colARTDAQSupervisor_.colLinkToBoardReaders_));
4924  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4925  artdaqSupervisorRow,
4926  artdaqSupervisorTable.tableView_->findCol(
4927  colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
4928  artdaqSupervisorUID +
4929  processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
4930  }
4931 
4932  // create group link to event builders
4933  if(artdaqSupervisorTable.tableView_
4934  ->getDataView()[artdaqSupervisorRow]
4935  [artdaqSupervisorTable.tableView_->findCol(
4936  colARTDAQSupervisor_.colLinkToEventBuilders_)] ==
4937  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4938  {
4939  __COUT__ << "Fixing missing link to Builders" << __E__;
4940  artdaqSupervisorTable.tableView_->setValueAsString(
4941  ARTDAQ_BUILDER_TABLE,
4942  artdaqSupervisorRow,
4943  artdaqSupervisorTable.tableView_->findCol(
4944  colARTDAQSupervisor_.colLinkToEventBuilders_));
4945  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4946  artdaqSupervisorRow,
4947  artdaqSupervisorTable.tableView_->findCol(
4948  colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
4949  artdaqSupervisorUID +
4950  processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
4951  }
4952 
4953  // create group link to data loggers
4954  if(artdaqSupervisorTable.tableView_
4955  ->getDataView()[artdaqSupervisorRow]
4956  [artdaqSupervisorTable.tableView_->findCol(
4957  colARTDAQSupervisor_.colLinkToDataLoggers_)] ==
4958  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4959  {
4960  __COUT__ << "Fixing missing link to Loggers" << __E__;
4961  artdaqSupervisorTable.tableView_->setValueAsString(
4962  ARTDAQ_LOGGER_TABLE,
4963  artdaqSupervisorRow,
4964  artdaqSupervisorTable.tableView_->findCol(
4965  colARTDAQSupervisor_.colLinkToDataLoggers_));
4966  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4967  artdaqSupervisorRow,
4968  artdaqSupervisorTable.tableView_->findCol(
4969  colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
4970  artdaqSupervisorUID +
4971  processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
4972  }
4973 
4974  // create group link to dispatchers
4975  if(artdaqSupervisorTable.tableView_
4976  ->getDataView()[artdaqSupervisorRow]
4977  [artdaqSupervisorTable.tableView_->findCol(
4978  colARTDAQSupervisor_.colLinkToDispatchers_)] ==
4979  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
4980  {
4981  __COUT__ << "Fixing missing link to Dispatchers" << __E__;
4982  artdaqSupervisorTable.tableView_->setValueAsString(
4983  ARTDAQ_DISPATCHER_TABLE,
4984  artdaqSupervisorRow,
4985  artdaqSupervisorTable.tableView_->findCol(
4986  colARTDAQSupervisor_.colLinkToDispatchers_));
4987  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
4988  artdaqSupervisorRow,
4989  artdaqSupervisorTable.tableView_->findCol(
4990  colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
4991  artdaqSupervisorUID +
4992  processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
4993  }
4994 
4995  // create group link to routing managers
4996  if(artdaqSupervisorTable.tableView_
4997  ->getDataView()[artdaqSupervisorRow]
4998  [artdaqSupervisorTable.tableView_->findCol(
4999  colARTDAQSupervisor_.colLinkToRoutingManagers_)] ==
5000  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5001  {
5002  __COUT__ << "Fixing missing link to Routers" << __E__;
5003  artdaqSupervisorTable.tableView_->setValueAsString(
5004  ARTDAQ_ROUTER_TABLE,
5005  artdaqSupervisorRow,
5006  artdaqSupervisorTable.tableView_->findCol(
5007  colARTDAQSupervisor_.colLinkToRoutingManagers_));
5008  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5009  artdaqSupervisorRow,
5010  artdaqSupervisorTable.tableView_->findCol(
5011  colARTDAQSupervisor_.colLinkToRoutingManagersGroupID_),
5012  artdaqSupervisorUID +
5013  processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
5014  }
5015 
5016  {
5017  std::stringstream ss;
5018  artdaqSupervisorTable.tableView_->print(ss);
5019  __COUT_MULTI__(0, ss.str());
5020  }
5021  } // end fixing links
5022 
5023  // Step 1. create/verify subsystems and destinations
5024  TableEditStruct& artdaqSubsystemTable = configGroupEdit.getTableEditStruct(
5025  ARTDAQ_SUBSYSTEM_TABLE, true /*markModified*/);
5026 
5027  // clear all records
5028  artdaqSubsystemTable.tableView_->deleteAllRows();
5029 
5030  for(auto& subsystemPair : subsystemObjectMap)
5031  {
5032  __COUTV__(subsystemPair.first);
5033  __COUTV__(subsystemPair.second);
5034 
5035  // create artdaq Subsystem record
5036  row = artdaqSubsystemTable.tableView_->addRow(
5037  author, true /*incrementUniqueData*/, subsystemPair.first);
5038 
5039  if(subsystemPair.second != "" &&
5040  subsystemPair.second != TableViewColumnInfo::DATATYPE_STRING_DEFAULT &&
5041  subsystemPair.second != NULL_SUBSYSTEM_DESTINATION_LABEL)
5042  {
5043  // set subsystem link
5044  artdaqSubsystemTable.tableView_->setValueAsString(
5045  ARTDAQ_SUBSYSTEM_TABLE,
5046  row,
5047  artdaqSubsystemTable.tableView_->findCol(
5048  colARTDAQSubsystem_.colLinkToDestination_));
5049  artdaqSubsystemTable.tableView_->setValueAsString(
5050  subsystemPair.second,
5051  row,
5052  artdaqSubsystemTable.tableView_->findCol(
5053  colARTDAQSubsystem_.colLinkToDestinationUID_));
5054  }
5055  // else leave disconnected link
5056 
5057  } // end subsystem loop
5058 
5059  // Step 2. for each node, create/verify records
5060  for(auto& nodeTypePair : nodeTypeToObjectMap)
5061  {
5062  __COUTV__(nodeTypePair.first);
5063 
5064  //__COUTV__(StringMacros::mapToString(processTypes_.mapToTable_));
5065 
5066  auto it = processTypes_.mapToTable_.find(nodeTypePair.first);
5067  if(it == processTypes_.mapToTable_.end())
5068  {
5069  __SS__ << "Invalid artdaq node type '" << nodeTypePair.first
5070  << "' attempted!" << __E__;
5071  __SS_THROW__;
5072  }
5073  __COUTV__(it->second);
5074 
5075  // test the table before getting for real
5076  try
5077  {
5078  /* TableEditStruct& tmpTypeTable = */ configGroupEdit.getTableEditStruct(
5079  it->second, true /*markModified*/);
5080  }
5081  catch(...)
5082  {
5083  if(nodeTypePair.second.size())
5084  throw; // do not ignore if user was trying to save records
5085 
5086  __COUT__ << "Ignoring missing table '" << it->second
5087  << "' since there were no user records attempted of type '"
5088  << nodeTypePair.first << ".'" << __E__;
5089  continue;
5090  }
5091  TableEditStruct& typeTable =
5092  configGroupEdit.getTableEditStruct(it->second, true /*markModified*/);
5093 
5094  TableEditStruct* artTable = nullptr;
5095  bool hasArtProcessName = false;
5096  unsigned int artProcessNameCol = -1;
5097  if(nodeTypePair.first != ARTDAQTableBase::processTypes_.READER &&
5098  nodeTypePair.first != ARTDAQTableBase::processTypes_.ROUTER)
5099  {
5100  __COUT__ << "Identified non-Reader, no-Router type '"
5101  << nodeTypePair.first
5102  << "' that has an art link and thus Process Name, so creating "
5103  "table edit structure to ART table."
5104  << __E__;
5105  artTable = &configGroupEdit.getTableEditStruct(
5106  ARTDAQTableBase::ARTDAQ_ART_TABLE, true /*markModified*/);
5107  if(TTEST(1))
5108  {
5109  std::stringstream ss;
5110  artTable->tableView_->print(ss);
5111  __COUT_MULTI__(1, ss.str());
5112  }
5113  artProcessNameCol = artTable->tableView_->findCol(
5114  ARTDAQTableBase::colARTDAQArt_.colProcessName_);
5115  __COUTTV__(artProcessNameCol);
5116 
5117  hasArtProcessName = true;
5118  }
5119  __COUTV__(hasArtProcessName);
5120 
5121  const unsigned int commentCol =
5122  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_COMMENT);
5123  const unsigned int authorCol =
5124  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_AUTHOR);
5125  const unsigned int timestampCol =
5126  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
5127 
5128  // keep track of records to delete, initialize to all in current table
5129  std::map<unsigned int /*type record row*/, bool /*doDelete*/> deleteRecordMap;
5130  unsigned int maxRowToDelete = typeTable.tableView_->getNumberOfRows();
5131  for(unsigned int r = 0; r < typeTable.tableView_->getNumberOfRows(); ++r)
5132  deleteRecordMap.emplace(std::make_pair(
5133  r, // typeTable.tableView_->getDataView()[i][typeTable.tableView_->getColUID()],
5134  true)); // init to delete
5135  __COUTTV__(maxRowToDelete);
5136 
5137  // keep a map of original multinode values, to maintain node specific links
5138  // (emplace when original node is deleted)
5139  // Note special (hierarchical) columns are defined as follows:
5140  // [-1] := ARTDAQTableBase::colARTDAQNotReader_.colLinkToArt_ / ARTDAQTableBase::colARTDAQArt_.colProcessName_
5141  const unsigned int ORIG_MAP_ART_PROC_NAME_COL = -1;
5142  std::map<std::string /*originalMultiNode name*/,
5143  std::map<unsigned int /*col*/, std::string /*value*/>>
5144  originalMultinodeValues;
5145  std::map<std::string /*multinode key*/,
5146  std::map<unsigned int /*col*/,
5147  std::pair<bool /* all siblings have same value */,
5148  std::string /* sameValue */>>>
5149  originalMultinodeSameSiblingValues;
5150  std::map<
5151  std::string /*multinode key*/,
5152  std::map<unsigned int /*col*/,
5153  std::pair<bool /* all siblings have embedded name */,
5154  std::vector<std::string /* splitForEmbeddedValue */>>>>
5155  originalMultinodeAllSiblingEmbeddedName;
5156  std::map<
5157  std::string /*multinode key*/,
5158  std::map<unsigned int /*col*/,
5159  std::pair<bool /* all siblings have embedded printer index */,
5160  std::vector<std::string /* splitForEmbeddedIndex */>>>>
5161  originalMultinodeAllSiblingEmbeddedPrinterIndex;
5162 
5163  // node instance loop
5164  for(auto& nodePair : nodeTypePair.second)
5165  {
5166  __COUTV__(nodePair.first); //new name
5167 
5168  // default multi-node and array hostname info to empty
5169  std::vector<std::string> nodeIndices, hostnameIndices;
5170  unsigned int hostnameFixedWidth = 0, nodeNameFixedWidth = 0;
5171  std::string hostname;
5172 
5173  // if original record is found, then commandeer that record
5174  // else create a new record
5175  // Node properties: {originalName,hostname,subsystemName,(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)}
5176 
5177  // node parameter loop
5178  for(unsigned int i = 0; i < nodePair.second.size(); ++i)
5179  {
5180  __COUTV__(nodePair.second[i]); //original name
5181 
5182  if(i == 0) // original UID
5183  {
5184  std::string nodeName;
5185  // Steps:
5186  // if original was multi-node,
5187  // then delete all but one
5188  // else
5189  // take over the row, or create new
5190  if(nodePair.second[i][0] == ':')
5191  {
5192  __COUT__ << "Handling original multi-node." << __E__;
5193 
5194  // format:
5195  // :<nodeNameFixedWidth>:<nodeVectorIndexString>:<nodeNameTemplate>
5196 
5197  std::string lastOriginalName;
5198  std::vector<std::string> originalParameterArr =
5200  &(nodePair.second[i].c_str()[1]),
5201  {':'} /*delimiter*/);
5202 
5203  if(originalParameterArr.size() != 3)
5204  {
5205  __SS__ << "Illegal original name parameter string '"
5206  << nodePair.second[i] << "!'" << __E__;
5207  __SS_THROW__;
5208  }
5209  __COUTTV__(
5210  StringMacros::vectorToString(originalParameterArr));
5211 
5212  unsigned int fixedWidth;
5213  sscanf(originalParameterArr[0].c_str(), "%u", &fixedWidth);
5214  __COUTV__(fixedWidth);
5215 
5216  std::vector<std::string> printerSyntaxArr =
5217  StringMacros::getVectorFromString(originalParameterArr[1],
5218  {','} /*delimiter*/);
5219 
5220  // unsigned int count = 0;
5221  std::vector<std::string> originalNodeIndices;
5222  for(auto& printerSyntaxValue : printerSyntaxArr)
5223  {
5224  __COUTV__(printerSyntaxValue);
5225 
5226  std::vector<std::string> printerSyntaxRange =
5228  printerSyntaxValue, {'-'} /*delimiter*/);
5229 
5230  if(printerSyntaxRange.size() == 0 ||
5231  printerSyntaxRange.size() > 2)
5232  {
5233  __SS__ << "Illegal multi-node printer syntax string '"
5234  << printerSyntaxValue << "!'" << __E__;
5235  __SS_THROW__;
5236  }
5237  else if(printerSyntaxRange.size() == 1)
5238  {
5239  __COUTV__(printerSyntaxRange[0]);
5240  originalNodeIndices.push_back(printerSyntaxRange[0]);
5241  }
5242  else // printerSyntaxRange.size() == 2
5243  {
5244  unsigned int lo, hi;
5245  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
5246  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
5247  if(hi < lo) // swap
5248  {
5249  lo = hi;
5250  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
5251  }
5252  for(; lo <= hi; ++lo)
5253  {
5254  __COUTTV__(lo);
5255  originalNodeIndices.push_back(std::to_string(lo));
5256  }
5257  }
5258  } // end printer syntax loop
5259 
5260  __COUTTV__(originalParameterArr[2]);
5261  //remove ;status=
5262  originalParameterArr[2] = originalParameterArr[2].substr(
5263  0, originalParameterArr[2].find(";status="));
5264  __COUTV__(originalParameterArr[2]);
5265  std::vector<std::string> originalNamePieces =
5266  StringMacros::getVectorFromString(originalParameterArr[2],
5267  {'*'} /*delimiter*/);
5268  __COUTV__(StringMacros::vectorToString(originalNamePieces));
5269 
5270  if(originalNamePieces.size() < 2)
5271  {
5272  __SS__ << "Illegal original multi-node name template - "
5273  "please use * to indicate where the multi-node "
5274  "index should be inserted!"
5275  << __E__;
5276  __SS_THROW__;
5277  }
5278 
5279  if(TTEST(1))
5280  {
5281  std::stringstream ss;
5282  typeTable.tableView_->print(ss);
5283  __COUT_MULTI__(1, ss.str());
5284  }
5285 
5286  //create matching bools to decide copy stategy
5287  __COUT__
5288  << "originalMultinodeSameSiblingValues init col map for "
5289  << nodePair.first << __E__;
5290  originalMultinodeSameSiblingValues.emplace(std::make_pair(
5291  nodePair.first,
5292  std::map<
5293  unsigned int /*col*/,
5294  std::pair<bool /* all siblings have same value */,
5295  std::string /* sameValue */>>()));
5296  __COUT__ << "originalMultinodeAllSiblingEmbeddedName init "
5297  "col map for "
5298  << nodePair.first << __E__;
5299  originalMultinodeAllSiblingEmbeddedName.emplace(std::make_pair(
5300  nodePair.first,
5301  std::map<
5302  unsigned int /*col*/,
5303  std::pair<
5304  bool /* all siblings have embedded name */,
5305  std::vector<
5306  std::
5307  string /* splitForEmbeddedValue */>>>()));
5308  __COUT__ << "originalMultinodeAllSiblingEmbeddedPrinterIndex "
5309  "init col map for "
5310  << nodePair.first << __E__;
5311  originalMultinodeAllSiblingEmbeddedPrinterIndex.emplace(
5312  std::make_pair(
5313  nodePair.first,
5314  std::map<
5315  unsigned int /*col*/,
5316  std::pair<
5317  bool /* all siblings have embedded printed index */
5318  ,
5319  std::vector<
5320  std::
5321  string /* splitForEmbeddedIndex */>>>()));
5322 
5323  // bool isFirst = true;
5324  unsigned int originalRow = TableView::INVALID,
5325  lastOriginalRow = TableView::INVALID,
5326  lastArtProcessRow = TableView::INVALID;
5327  for(unsigned int i = 0; i < originalNodeIndices.size(); ++i)
5328  {
5329  std::string originalName = originalNamePieces[0];
5330  std::string nodeNameIndex;
5331  for(unsigned int p = 1; p < originalNamePieces.size();
5332  ++p)
5333  {
5334  nodeNameIndex = originalNodeIndices[i];
5335  if(fixedWidth > 1)
5336  {
5337  if(nodeNameIndex.size() > fixedWidth)
5338  {
5339  __SS__ << "Illegal original node name index '"
5340  << nodeNameIndex
5341  << "' - length is longer than fixed "
5342  "width requirement of "
5343  << fixedWidth << "!" << __E__;
5344  __SS_THROW__;
5345  }
5346 
5347  // 0 prepend as needed
5348  while(nodeNameIndex.size() < fixedWidth)
5349  nodeNameIndex = "0" + nodeNameIndex;
5350  } // end fixed width handling
5351 
5352  originalName += nodeNameIndex + originalNamePieces[p];
5353  }
5354  __COUTTV__(originalName);
5355  originalRow = typeTable.tableView_->findRow(
5356  typeTable.tableView_->getColUID(),
5357  originalName,
5358  0 /*offsetRow*/,
5359  true /*doNotThrow*/);
5360  __COUTTV__(originalRow);
5361 
5362  // if have a new 'seed' valid row, then delete last valid row
5363  // before deleting, record all customizing values to draw from when creating new multinode records
5364  auto result = originalMultinodeValues.emplace(
5365  std::make_pair(originalName,
5366  std::map<unsigned int /*col*/,
5367  std::string /*value*/>()));
5368  if(!result.second)
5369  __COUT__
5370  << "originalName '" << originalName
5371  << "' already in original multinode value cache."
5372  << __E__;
5373  else //keep original cache values
5374  {
5375  __COUT__ << "Saving multinode value " << originalName
5376  << "[" << originalRow
5377  << "][*] with row count = "
5378  << typeTable.tableView_->getNumberOfRows()
5379  << __E__;
5380 
5381  // save all link values
5382  for(unsigned int col = 0;
5383  col < typeTable.tableView_->getNumberOfColumns();
5384  ++col)
5385  {
5386  if(typeTable.tableView_->getColumnInfo(col)
5387  .getName() ==
5388  ARTDAQTableBase::
5389  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK ||
5390  typeTable.tableView_->getColumnInfo(col)
5391  .getName() ==
5392  ARTDAQTableBase::
5393  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID ||
5394  typeTable.tableView_->getColumnInfo(col)
5395  .getName() ==
5396  ARTDAQTableBase::
5397  ARTDAQ_TYPE_TABLE_HOSTNAME ||
5398  typeTable.tableView_->getColumnInfo(col)
5399  .isUID() ||
5400  col == typeTable.tableView_->getColStatus() ||
5401  typeTable.tableView_->getColumnInfo(col)
5402  .isGroupID() ||
5403  col == timestampCol ||
5404  col ==
5405  authorCol) //always go with now author/timestamp on touched records (too easy to misidentify change vs nochange)
5406  continue; // skip subsystem link, etc that is modified by fields maintained in the GUI
5407  else
5408  {
5409  __COUTT__
5410  << "Caching node value: " << originalName
5411  << "[" << originalRow << "][" << col
5412  << "/"
5413  << typeTable.tableView_
5414  ->getColumnInfo(col)
5415  .getName()
5416  << "] = "
5417  << typeTable.tableView_
5418  ->getDataView()[originalRow][col]
5419  << __E__;
5420  originalMultinodeValues.at(originalName)
5421  .emplace(std::make_pair(
5422  col,
5423  typeTable.tableView_
5424  ->getDataView()[originalRow]
5425  [col]));
5426 
5427  //the first time, set to true and then prove wrong
5428 
5429  for(const auto& pair :
5430  originalMultinodeSameSiblingValues)
5431  __COUTT__ << "originalMultinodeSameSiblin"
5432  "gValues["
5433  << pair.first << "]" << __E__;
5434  auto result2 =
5435  originalMultinodeSameSiblingValues
5436  .at(nodePair.first)
5437  .emplace(std::make_pair(
5438  col,
5439  //same value
5440  std::make_pair(
5441  true,
5442  typeTable.tableView_
5443  ->getDataView()
5444  [originalRow][col])));
5445 
5446  for(const auto& pair :
5447  originalMultinodeAllSiblingEmbeddedName)
5448  __COUTT__ << "originalMultinodeAllSibling"
5449  "EmbeddedName["
5450  << pair.first << "]" << __E__;
5451  originalMultinodeAllSiblingEmbeddedName
5452  .at(nodePair.first)
5453  .emplace(std::make_pair(
5454  col,
5455  std::make_pair( //bool
5456  typeTable.tableView_
5457  ->getDataView()
5458  [originalRow][col]
5459  .find(originalName) !=
5460  std::string::npos,
5461  //split string
5462  std::vector<std::string>())));
5463 
5464  for(const auto& pair :
5465  originalMultinodeAllSiblingEmbeddedPrinterIndex)
5466  __COUTT__ << "originalMultinodeAllSibling"
5467  "EmbeddedPrinterIndex["
5468  << pair.first << "]" << __E__;
5469  originalMultinodeAllSiblingEmbeddedPrinterIndex
5470  .at(nodePair.first)
5471  .emplace(std::make_pair(
5472  col,
5473  std::make_pair( //bool
5474  typeTable.tableView_
5475  ->getDataView()
5476  [originalRow][col]
5477  .find(nodeNameIndex) !=
5478  std::string::npos,
5479  //split string
5480  std::vector<std::string>())));
5481 
5482  if(result2
5483  .second) //emplace always should work first time
5484  {
5485  __COUTTV__(
5486  originalMultinodeSameSiblingValues
5487  .at(nodePair.first)
5488  .at(col)
5489  .second);
5490 
5491  __COUTTV__(
5492  originalMultinodeAllSiblingEmbeddedName
5493  .at(nodePair.first)
5494  .at(col)
5495  .first);
5496  if(originalMultinodeAllSiblingEmbeddedName
5497  .at(nodePair.first)
5498  .at(col)
5499  .first)
5500  {
5501  __COUTT__
5502  << "Determine string splits for "
5503  "embedded name"
5504  << __E__;
5505  const std::string& val =
5506  typeTable.tableView_
5507  ->getDataView()[originalRow]
5508  [col];
5509  size_t pos = val.find(originalName);
5510  originalMultinodeAllSiblingEmbeddedName
5511  .at(nodePair.first)
5512  .at(col)
5513  .second.push_back(
5514  val.substr(0, pos));
5515  originalMultinodeAllSiblingEmbeddedName
5516  .at(nodePair.first)
5517  .at(col)
5518  .second.push_back(val.substr(
5519  pos + originalName.size()));
5520  __COUTTV__(StringMacros::vectorToString(
5521  originalMultinodeAllSiblingEmbeddedName
5522  .at(nodePair.first)
5523  .at(col)
5524  .second));
5525  }
5526  __COUTTV__(
5527  originalMultinodeAllSiblingEmbeddedPrinterIndex
5528  .at(nodePair.first)
5529  .at(col)
5530  .first);
5531  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
5532  .at(nodePair.first)
5533  .at(col)
5534  .first)
5535  {
5536  __COUTT__ << "Determine string "
5537  "splits for embedded "
5538  "printer syntax index: "
5539  << nodeNameIndex << __E__;
5540  const std::string& val =
5541  typeTable.tableView_
5542  ->getDataView()[originalRow]
5543  [col];
5544  size_t pos = val.find(nodeNameIndex);
5545  originalMultinodeAllSiblingEmbeddedPrinterIndex
5546  .at(nodePair.first)
5547  .at(col)
5548  .second.push_back(
5549  val.substr(0, pos));
5550  originalMultinodeAllSiblingEmbeddedPrinterIndex
5551  .at(nodePair.first)
5552  .at(col)
5553  .second.push_back(val.substr(
5554  pos + nodeNameIndex.size()));
5555  __COUTTV__(StringMacros::vectorToString(
5556  originalMultinodeAllSiblingEmbeddedPrinterIndex
5557  .at(nodePair.first)
5558  .at(col)
5559  .second));
5560  }
5561  }
5562  else //not first time, so prove wrong
5563  {
5564  if(originalMultinodeSameSiblingValues
5565  .at(nodePair.first)
5566  .at(col)
5567  .first)
5568  {
5569  __COUTT__ << "Checking sibling same "
5570  "values... for "
5571  << nodePair.first << __E__;
5572  if(typeTable.tableView_
5573  ->getDataView()[originalRow]
5574  [col] !=
5575  typeTable.tableView_->getDataView()
5576  [lastOriginalRow][col])
5577  {
5578  __COUT__
5579  << "Found different sibling "
5580  "values at col="
5581  << col << " for "
5582  << nodePair.first << __E__;
5583  originalMultinodeSameSiblingValues
5584  .at(nodePair.first)
5585  .at(col)
5586  .first = false;
5587  }
5588  }
5589  if(originalMultinodeAllSiblingEmbeddedName
5590  .at(nodePair.first)
5591  .at(col)
5592  .first)
5593  {
5594  __COUTT__ << "Checking sibling "
5595  "embedded name... for "
5596  << nodePair.first << ":"
5597  << originalName << __E__;
5598  if(typeTable.tableView_
5599  ->getDataView()[originalRow]
5600  [col]
5601  .find(originalName) ==
5602  std::string::npos)
5603  {
5604  __COUT__ << "Found no embedded "
5605  "name at col="
5606  << col << " looking for "
5607  << originalName << __E__;
5608  originalMultinodeAllSiblingEmbeddedName
5609  .at(nodePair.first)
5610  .at(col)
5611  .first = false;
5612  }
5613  }
5614  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
5615  .at(nodePair.first)
5616  .at(col)
5617  .first)
5618  {
5619  __COUTT__
5620  << "Checking sibling embedded "
5621  "printer syntax index... for "
5622  << nodePair.first << ":"
5623  << nodeNameIndex << __E__;
5624  if(typeTable.tableView_
5625  ->getDataView()[originalRow]
5626  [col]
5627  .find(nodeNameIndex) ==
5628  std::string::npos)
5629  {
5630  __COUT__ << "Found no embedded "
5631  "printer syntax "
5632  "index at col="
5633  << col << " looking for "
5634  << nodeNameIndex
5635  << __E__;
5636  originalMultinodeAllSiblingEmbeddedPrinterIndex
5637  .at(nodePair.first)
5638  .at(col)
5639  .first = false;
5640  }
5641  }
5642  }
5643 
5644  __COUTT__
5645  << "originalMultinodeSameSiblingValues["
5646  << nodePair.first << "][" << col << "] = "
5647  << originalMultinodeSameSiblingValues
5648  .at(nodePair.first)
5649  .at(col)
5650  .first
5651  << __E__;
5652  __COUTT__
5653  << "originalMultinodeAllSiblingEmbeddedNa"
5654  "me["
5655  << nodePair.first << "][" << col << "] = "
5656  << originalMultinodeAllSiblingEmbeddedName
5657  .at(nodePair.first)
5658  .at(col)
5659  .first
5660  << __E__;
5661  __COUTT__
5662  << "originalMultinodeAllSiblingEmbeddedPr"
5663  "interIndex["
5664  << nodePair.first << "][" << col << "] = "
5665  << originalMultinodeAllSiblingEmbeddedPrinterIndex
5666  .at(nodePair.first)
5667  .at(col)
5668  .first
5669  << __E__;
5670 
5671  if(hasArtProcessName && artTable &&
5672  typeTable.tableView_->getColumnInfo(col)
5673  .getName() ==
5674  ARTDAQTableBase::colARTDAQNotReader_
5675  .colLinkToArtUID_)
5676  {
5677  //note at this point, col = Link to art record
5678  __COUT__
5679  << "Checking ART Process Name... for "
5680  "originalName='"
5681  << originalName << "' / "
5682  << typeTable.tableView_
5683  ->getDataView()[originalRow]
5684  [col]
5685  << __E__;
5686  unsigned int artRow =
5687  artTable->tableView_->findRow(
5688  artTable->tableView_->getColUID(),
5689  /* art UID record name */
5690  typeTable.tableView_
5691  ->getDataView()[originalRow]
5692  [col]);
5693  __COUTTV__(artRow);
5694 
5695  __COUTT__
5696  << "Found ART Process Name = "
5697  << artTable->tableView_->getDataView()
5698  [artRow][artProcessNameCol]
5699  << __E__;
5700 
5701  //original value tracking/emplace handling copied from above L4284
5702  originalMultinodeValues.at(originalName)
5703  .emplace(std::make_pair(
5704  ORIG_MAP_ART_PROC_NAME_COL,
5705  artTable->tableView_
5706  ->getDataView()
5707  [artRow]
5708  [artProcessNameCol]));
5709  __COUTTV__(
5710  originalMultinodeValues
5711  .at(originalName)
5712  .at(ORIG_MAP_ART_PROC_NAME_COL));
5713 
5714  //the first time, set to true and then prove wrong
5715  originalMultinodeSameSiblingValues
5716  .at(nodePair.first)
5717  .emplace(std::make_pair(
5718  ORIG_MAP_ART_PROC_NAME_COL,
5719  //same value
5720  std::make_pair(
5721  true,
5722  artTable->tableView_
5723  ->getDataView()
5724  [artRow]
5725  [artProcessNameCol])));
5726  originalMultinodeAllSiblingEmbeddedName
5727  .at(nodePair.first)
5728  .emplace(std::make_pair(
5729  ORIG_MAP_ART_PROC_NAME_COL,
5730  std::make_pair( //bool
5731  artTable->tableView_
5732  ->getDataView()
5733  [artRow]
5734  [artProcessNameCol]
5735  .find(originalName) !=
5736  std::string::npos,
5737  //split string
5738  std::vector<std::string>())));
5739  originalMultinodeAllSiblingEmbeddedPrinterIndex
5740  .at(nodePair.first)
5741  .emplace(std::make_pair(
5742  ORIG_MAP_ART_PROC_NAME_COL,
5743  std::make_pair( //bool
5744  artTable->tableView_
5745  ->getDataView()
5746  [artRow]
5747  [artProcessNameCol]
5748  .find(
5749  nodeNameIndex) !=
5750  std::string::npos,
5751  //split string
5752  std::vector<std::string>())));
5753 
5754  if(result2
5755  .second) //emplace always should work first time
5756  {
5757  __COUTTV__(
5758  originalMultinodeSameSiblingValues
5759  .at(nodePair.first)
5760  .at(ORIG_MAP_ART_PROC_NAME_COL)
5761  .second);
5762 
5763  __COUTTV__(
5764  originalMultinodeAllSiblingEmbeddedName
5765  .at(nodePair.first)
5766  .at(ORIG_MAP_ART_PROC_NAME_COL)
5767  .first);
5768  if(originalMultinodeAllSiblingEmbeddedName
5769  .at(nodePair.first)
5770  .at(ORIG_MAP_ART_PROC_NAME_COL)
5771  .first)
5772  {
5773  __COUTT__
5774  << "Determine string splits "
5775  "for embedded name"
5776  << __E__;
5777  const std::string& val =
5778  artTable->tableView_
5779  ->getDataView()
5780  [artRow]
5781  [artProcessNameCol];
5782  size_t pos =
5783  val.find(originalName);
5784  originalMultinodeAllSiblingEmbeddedName
5785  .at(nodePair.first)
5786  .at(ORIG_MAP_ART_PROC_NAME_COL)
5787  .second.push_back(
5788  val.substr(0, pos));
5789  originalMultinodeAllSiblingEmbeddedName
5790  .at(nodePair.first)
5791  .at(ORIG_MAP_ART_PROC_NAME_COL)
5792  .second.push_back(val.substr(
5793  pos +
5794  originalName.size()));
5795  __COUTTV__(StringMacros::vectorToString(
5796  originalMultinodeAllSiblingEmbeddedName
5797  .at(nodePair.first)
5798  .at(ORIG_MAP_ART_PROC_NAME_COL)
5799  .second));
5800  }
5801  __COUTTV__(
5802  originalMultinodeAllSiblingEmbeddedPrinterIndex
5803  .at(nodePair.first)
5804  .at(ORIG_MAP_ART_PROC_NAME_COL)
5805  .first);
5806  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
5807  .at(nodePair.first)
5808  .at(ORIG_MAP_ART_PROC_NAME_COL)
5809  .first)
5810  {
5811  __COUTT__
5812  << "Determine string splits "
5813  "for embedded printer "
5814  "syntax index: "
5815  << nodeNameIndex << __E__;
5816  const std::string& val =
5817  artTable->tableView_
5818  ->getDataView()
5819  [artRow]
5820  [artProcessNameCol];
5821  size_t pos =
5822  val.find(nodeNameIndex);
5823  originalMultinodeAllSiblingEmbeddedPrinterIndex
5824  .at(nodePair.first)
5825  .at(ORIG_MAP_ART_PROC_NAME_COL)
5826  .second.push_back(
5827  val.substr(0, pos));
5828  originalMultinodeAllSiblingEmbeddedPrinterIndex
5829  .at(nodePair.first)
5830  .at(ORIG_MAP_ART_PROC_NAME_COL)
5831  .second.push_back(val.substr(
5832  pos +
5833  nodeNameIndex.size()));
5834  __COUTTV__(StringMacros::vectorToString(
5835  originalMultinodeAllSiblingEmbeddedPrinterIndex
5836  .at(nodePair.first)
5837  .at(ORIG_MAP_ART_PROC_NAME_COL)
5838  .second));
5839  }
5840  }
5841  else //not first time, so prove wrong
5842  {
5843  if(originalMultinodeSameSiblingValues
5844  .at(nodePair.first)
5845  .at(ORIG_MAP_ART_PROC_NAME_COL)
5846  .first)
5847  {
5848  __COUTT__ << "Checking sibling "
5849  "same values... for "
5850  << nodePair.first
5851  << __E__;
5852  if(artTable->tableView_
5853  ->getDataView()
5854  [artRow]
5855  [artProcessNameCol] !=
5856  artTable->tableView_
5857  ->getDataView()
5858  [lastArtProcessRow]
5859  [artProcessNameCol])
5860  {
5861  __COUT__
5862  << "Found different "
5863  "sibling values "
5864  "at artProcessNameCol="
5865  << artProcessNameCol
5866  << " for "
5867  << nodePair.first
5868  << __E__;
5869  originalMultinodeSameSiblingValues
5870  .at(nodePair.first)
5871  .at(ORIG_MAP_ART_PROC_NAME_COL)
5872  .first = false;
5873  }
5874  }
5875  if(originalMultinodeAllSiblingEmbeddedName
5876  .at(nodePair.first)
5877  .at(ORIG_MAP_ART_PROC_NAME_COL)
5878  .first)
5879  {
5880  __COUTT__
5881  << "Checking sibling "
5882  "embedded name... for "
5883  << nodePair.first << ":"
5884  << originalName << __E__;
5885  if(artTable->tableView_
5886  ->getDataView()
5887  [artRow]
5888  [artProcessNameCol]
5889  .find(originalName) ==
5890  std::string::npos)
5891  {
5892  __COUT__
5893  << "Found no embedded "
5894  "name at "
5895  "artProcessNameCol="
5896  << artProcessNameCol
5897  << " looking for "
5898  << originalName << __E__;
5899  originalMultinodeAllSiblingEmbeddedName
5900  .at(nodePair.first)
5901  .at(ORIG_MAP_ART_PROC_NAME_COL)
5902  .first = false;
5903  }
5904  }
5905  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
5906  .at(nodePair.first)
5907  .at(ORIG_MAP_ART_PROC_NAME_COL)
5908  .first)
5909  {
5910  __COUTT__
5911  << "Checking sibling "
5912  "embedded printer syntax "
5913  "index... for "
5914  << nodePair.first << ":"
5915  << nodeNameIndex << __E__;
5916  if(artTable->tableView_
5917  ->getDataView()
5918  [artRow]
5919  [artProcessNameCol]
5920  .find(nodeNameIndex) ==
5921  std::string::npos)
5922  {
5923  __COUT__
5924  << "Found no embedded "
5925  "printer syntax index "
5926  "at artProcessNameCol="
5927  << artProcessNameCol
5928  << " looking for "
5929  << nodeNameIndex << __E__;
5930  originalMultinodeAllSiblingEmbeddedPrinterIndex
5931  .at(nodePair.first)
5932  .at(ORIG_MAP_ART_PROC_NAME_COL)
5933  .first = false;
5934  }
5935  }
5936  }
5937 
5938  __COUTT__
5939  << "originalMultinodeSameSiblingValue"
5940  "s["
5941  << nodePair.first << "]["
5942  << ORIG_MAP_ART_PROC_NAME_COL
5943  << "] = "
5944  << originalMultinodeSameSiblingValues
5945  .at(nodePair.first)
5946  .at(ORIG_MAP_ART_PROC_NAME_COL)
5947  .first
5948  << __E__;
5949  __COUTT__
5950  << "originalMultinodeAllSiblingEmbedd"
5951  "edName["
5952  << nodePair.first << "]["
5953  << ORIG_MAP_ART_PROC_NAME_COL
5954  << "] = "
5955  << originalMultinodeAllSiblingEmbeddedName
5956  .at(nodePair.first)
5957  .at(ORIG_MAP_ART_PROC_NAME_COL)
5958  .first
5959  << __E__;
5960  __COUTT__
5961  << "originalMultinodeAllSiblingEmbedd"
5962  "edPrinterIndex["
5963  << nodePair.first << "]["
5964  << ORIG_MAP_ART_PROC_NAME_COL
5965  << "] = "
5966  << originalMultinodeAllSiblingEmbeddedPrinterIndex
5967  .at(nodePair.first)
5968  .at(ORIG_MAP_ART_PROC_NAME_COL)
5969  .first
5970  << __E__;
5971 
5972  __COUT__
5973  << "Checking ART Process Name "
5974  "complete for originalName='"
5975  << originalName << "' / "
5976  << typeTable.tableView_
5977  ->getDataView()[originalRow]
5978  [col]
5979  << __E__;
5980  lastArtProcessRow =
5981  artRow; //save for next comparison
5982  } //end ART Process Name cache handling
5983 
5984  } //end col caching handling
5985  } //end col loop
5986  } //end cache handling
5987 
5988  if(originalRow !=
5989  TableView::
5990  INVALID) // save last original valid row for future cache/deletion
5991  lastOriginalRow = originalRow;
5992 
5993  __COUTTV__(lastOriginalRow);
5994  lastOriginalName = originalName;
5995  } // end loop through multi-node instances
5996 
5997  for(const auto& pair :
5998  originalMultinodeSameSiblingValues.at(nodePair.first))
5999  __COUTT__ << "originalMultinodeSameSiblingValues["
6000  << nodePair.first << "][" << pair.first
6001  << "] = " << pair.second.first << __E__;
6002  for(const auto& pair :
6003  originalMultinodeAllSiblingEmbeddedName.at(
6004  nodePair.first))
6005  __COUTT__ << "originalMultinodeAllSiblingEmbeddedName["
6006  << nodePair.first << "][" << pair.first
6007  << "] = " << pair.second.first << __E__;
6008  for(const auto& pair :
6009  originalMultinodeAllSiblingEmbeddedPrinterIndex.at(
6010  nodePair.first))
6011  __COUTT__
6012  << "originalMultinodeAllSiblingEmbeddedPrinterIndex["
6013  << nodePair.first << "][" << pair.first
6014  << "] = " << pair.second.first << __E__;
6015 
6016  __COUTTV__(lastOriginalRow);
6017  row = lastOriginalRow; // take last valid row to proceed
6018  __COUTV__(row);
6019  } // end handling of original multinode
6020  else
6021  {
6022  std::string originalName = nodePair.second[i].substr(
6023  0, nodePair.second[i].find(";status="));
6024  __COUTV__(originalName);
6025 
6026  // attempt to find original 'single' node name
6027  row = typeTable.tableView_->findRow(
6028  typeTable.tableView_->getColUID(),
6029  originalName,
6030  0 /*offsetRow*/,
6031  true /*doNotThrow*/);
6032  __COUTV__(row);
6033  }
6034 
6035  //if no original nodes, there may be *'s in node name, so remove them
6036  {
6037  nodeName = nodePair.first; // take new node name
6038  __COUTV__(nodeName);
6039  //remove ;status=
6040  nodeName = nodeName.substr(0, nodeName.find(";status="));
6041 
6042  //remove stars for seed nodename
6043  std::string tmpNodeName = nodeName;
6044  nodeName = ""; //clear
6045  for(size_t c = 0; c < tmpNodeName.size(); ++c)
6046  if(tmpNodeName[c] != '*')
6047  nodeName += tmpNodeName[c];
6048  } //end removing *'s from node name
6049 
6050  __COUTV__(nodeName);
6051  if(row == TableView::INVALID)
6052  {
6053  // No original record, so create artdaq type instance record
6054  row = typeTable.tableView_->addRow(
6055  author, true /*incrementUniqueData*/, nodeName);
6056 
6057  // fill defaults properties/parameters here!
6058  if(nodeTypePair.first == processTypes_.READER)
6059  {
6060  __COUT__ << "Handling new " << nodeTypePair.first
6061  << " defaults!" << __E__;
6062  TableEditStruct& daqParameterTable =
6063  configGroupEdit.getTableEditStruct(
6064  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6065  true /*markModified*/);
6066 
6067  // create group link to daq parameter table
6068  typeTable.tableView_->setValueAsString(
6069  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6070  row,
6071  typeTable.tableView_->findCol(
6072  ARTDAQTableBase::colARTDAQReader_
6073  .colLinkToDaqParameters_));
6074  std::string daqParameterGroupID =
6075  typeTable.tableView_->setUniqueColumnValue(
6076  row,
6077  typeTable.tableView_->findCol(
6078  ARTDAQTableBase::colARTDAQReader_
6079  .colLinkToDaqParametersGroupID_),
6080  nodeName + "DaqParameters");
6081 
6082  {
6083  std::stringstream ss;
6084  typeTable.tableView_->print(ss);
6085  __COUT_MULTI__(1, ss.str());
6086  }
6087 
6088  // now create parameters at target link
6089  const std::vector<std::string> parameterUIDs = {
6090  "BoardID", "FragmentID"};
6091 
6092  const std::vector<std::string> parameterNames = {
6093  "board_id", //"BoardID",
6094  "fragment_id", //"FragmentID"
6095  };
6096  const std::vector<std::string> parameterValues = {
6097  "0", //"BoardID",
6098  "0" //"FragmentID",
6099  };
6100 
6101  unsigned int parameterRow;
6102  for(unsigned int i = 0; i < parameterNames.size(); ++i)
6103  {
6104  // create artdaq Reader property record
6105  parameterRow = daqParameterTable.tableView_->addRow(
6106  author,
6107  true /*incrementUniqueData*/,
6108  nodeName + parameterUIDs[i]);
6109 
6110  // set app status true
6111  daqParameterTable.tableView_->setValueAsString(
6112  "1",
6113  parameterRow,
6114  daqParameterTable.tableView_->getColStatus());
6115  // set key
6116  daqParameterTable.tableView_->setValueAsString(
6117  parameterNames[i],
6118  parameterRow,
6119  daqParameterTable.tableView_->findCol(
6120  ARTDAQTableBase::colARTDAQDaqParameter_
6121  .colDaqParameterKey_));
6122  // set value
6123  daqParameterTable.tableView_->setValueAsString(
6124  parameterValues[i],
6125  parameterRow,
6126  daqParameterTable.tableView_->findCol(
6127  ARTDAQTableBase::colARTDAQDaqParameter_
6128  .colDaqParameterValue_));
6129  // set groupid
6130  daqParameterTable.tableView_->setValueAsString(
6131  daqParameterGroupID,
6132  parameterRow,
6133  daqParameterTable.tableView_->findCol(
6134  ARTDAQTableBase::colARTDAQDaqParameter_
6135  .colDaqParameterGroupID_));
6136 
6137  } // end Reader default property create loop
6138 
6139  daqParameterTable.tableView_
6140  ->init(); // verify new table (throws runtime_errors)
6141 
6142  } // end Reader default property setup
6143  else if(nodeTypePair.first == processTypes_.BUILDER ||
6144  nodeTypePair.first == processTypes_.LOGGER ||
6145  nodeTypePair.first == processTypes_.DISPATCHER)
6146  {
6147  __COUT__ << "Handling new " << nodeTypePair.first
6148  << " defaults!" << __E__;
6149 
6150  // goes through DAQ table
6151  TableEditStruct& daqTable =
6152  configGroupEdit.getTableEditStruct(
6153  ARTDAQTableBase::ARTDAQ_DAQ_TABLE,
6154  true /*markModified*/);
6155  // create DAQ record
6156  unsigned int daqRecordRow = daqTable.tableView_->addRow(
6157  author,
6158  true /*incrementUniqueData*/,
6159  nodeName + "Daq");
6160  std::string daqRecordUID =
6161  daqTable.tableView_
6162  ->getDataView()[daqRecordRow]
6163  [daqTable.tableView_->getColUID()];
6164 
6165  // create unique link to daq table
6166  typeTable.tableView_->setValueAsString(
6167  ARTDAQTableBase::ARTDAQ_DAQ_TABLE,
6168  row,
6169  typeTable.tableView_->findCol(
6170  ARTDAQTableBase::colARTDAQNotReader_
6171  .colLinkToDaq_));
6172  typeTable.tableView_->setValueAsString(
6173  daqRecordUID,
6174  row,
6175  typeTable.tableView_->findCol(
6176  ARTDAQTableBase::colARTDAQNotReader_
6177  .colLinkToDaqUID_));
6178 
6179  TableEditStruct& daqParameterTable =
6180  configGroupEdit.getTableEditStruct(
6181  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6182  true /*markModified*/);
6183  // create group link to daq parameter table
6184  daqTable.tableView_->setValueAsString(
6185  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6186  daqRecordRow,
6187  daqTable.tableView_->findCol(
6188  ARTDAQTableBase::colARTDAQDaq_
6189  .colLinkToDaqParameters_));
6190  std::string daqParameterGroupID =
6191  daqTable.tableView_->setUniqueColumnValue(
6192  daqRecordRow,
6193  daqTable.tableView_->findCol(
6194  ARTDAQTableBase::colARTDAQDaq_
6195  .colLinkToDaqParametersGroupID_),
6196  nodeName + "DaqParameters");
6197 
6198  // now create parameters at target link
6199  const std::vector<std::string> parameterUIDs = {
6200  "BufferCount", "FragmentsPerEvent"};
6201 
6202  const std::vector<std::string> parameterNames = {
6203  "buffer_count", //"BufferCount",
6204  "expected_fragments_per_event" //"FragmentsPerEvent"
6205  };
6206  const std::vector<std::string> parameterValues = {
6207  "10", //"BufferCount",
6208  "0" //"FragmentsPerEvent",
6209  };
6210 
6211  unsigned int parameterRow;
6212  for(unsigned int i = 0; i < parameterNames.size(); ++i)
6213  {
6214  // create artdaq Reader property record
6215  parameterRow = daqParameterTable.tableView_->addRow(
6216  author,
6217  true /*incrementUniqueData*/,
6218  nodeName + parameterUIDs[i]);
6219 
6220  // set app status true
6221  daqParameterTable.tableView_->setValueAsString(
6222  "1",
6223  parameterRow,
6224  daqParameterTable.tableView_->getColStatus());
6225  // set key
6226  daqParameterTable.tableView_->setValueAsString(
6227  parameterNames[i],
6228  parameterRow,
6229  daqParameterTable.tableView_->findCol(
6230  ARTDAQTableBase::colARTDAQDaqParameter_
6231  .colDaqParameterKey_));
6232  // set value
6233  daqParameterTable.tableView_->setValueAsString(
6234  parameterValues[i],
6235  parameterRow,
6236  daqParameterTable.tableView_->findCol(
6237  ARTDAQTableBase::colARTDAQDaqParameter_
6238  .colDaqParameterValue_));
6239  // set groupid
6240  daqParameterTable.tableView_->setValueAsString(
6241  daqParameterGroupID,
6242  parameterRow,
6243  daqParameterTable.tableView_->findCol(
6244  ARTDAQTableBase::colARTDAQDaqParameter_
6245  .colDaqParameterGroupID_));
6246 
6247  } // end Reader default property create loop
6248 
6249  daqTable.tableView_
6250  ->init(); // verify new table (throws runtime_errors)
6251  daqParameterTable.tableView_
6252  ->init(); // verify new table (throws runtime_errors)
6253 
6254  } // end Builder, Logger, Dispatcher default property setup
6255  }
6256  else // set UID
6257  {
6258  __COUT__
6259  << "Reusing row " << row << " current-UID="
6260  << typeTable.tableView_
6261  ->getDataView()[row]
6262  [typeTable.tableView_->getColUID()]
6263  << " as (temporarily to basename if multinode) new-UID="
6264  << nodeName << __E__;
6265  typeTable.tableView_
6266  ->setValueAsString( //if single record, this renaming is final; if multi record, this renaming to basename is temporary
6267  nodeName,
6268  row,
6269  typeTable.tableView_->getColUID());
6270  }
6271  __COUTV__(row);
6272 
6273  // remove from delete map
6274  if(row < maxRowToDelete)
6275  deleteRecordMap[row] = false;
6276 
6277  __COUTV__(StringMacros::mapToString(
6278  processTypes_.mapToLinkGroupIDColumn_));
6279 
6280  // set GroupID
6281  typeTable.tableView_->setValueAsString(
6282  artdaqSupervisorTable.tableView_
6283  ->getDataView()[artdaqSupervisorRow]
6284  [artdaqSupervisorTable.tableView_->findCol(
6285  processTypes_.mapToLinkGroupIDColumn_
6286  .at(nodeTypePair.first))],
6287  row,
6288  typeTable.tableView_->findCol(
6289  processTypes_.mapToGroupIDColumn_.at(
6290  nodeTypePair.first)));
6291  }
6292  else if(i == 1) // status
6293  {
6294  // enable/disable the target row
6295  typeTable.tableView_->setValueAsString(
6296  nodePair.second[i],
6297  row,
6298  typeTable.tableView_->getColStatus());
6299  }
6300  else if(i == 2) // hostname
6301  {
6302  // set hostname
6303  hostname = nodePair.second[i];
6304  typeTable.tableView_->setValueAsString(
6305  hostname,
6306  row,
6307  typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME));
6308  }
6309  else if(i == 3) // subsystemName
6310  {
6311  // set subsystemName
6312  if(nodePair.second[i] != "" &&
6313  nodePair.second[i] !=
6314  TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
6315  {
6316  // real subsystem?
6317  if(subsystemObjectMap.find(nodePair.second[i]) ==
6318  subsystemObjectMap.end())
6319  {
6320  __SS__ << "Illegal subsystem '" << nodePair.second[i]
6321  << "' mismatch!" << __E__;
6322  __SS_THROW__;
6323  }
6324 
6325  typeTable.tableView_->setValueAsString(
6326  ARTDAQ_SUBSYSTEM_TABLE,
6327  row,
6328  typeTable.tableView_->findCol(
6329  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
6330  typeTable.tableView_->setValueAsString(
6331  nodePair.second[i],
6332  row,
6333  typeTable.tableView_->findCol(
6334  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID));
6335  }
6336  else // no subsystem (i.e. default subsystem)
6337  {
6338  typeTable.tableView_->setValueAsString(
6339  TableViewColumnInfo::DATATYPE_LINK_DEFAULT,
6340  row,
6341  typeTable.tableView_->findCol(
6342  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
6343  }
6344  }
6345  else if(
6346  i == 4 || i == 5 || i == 6 ||
6347  i ==
6348  7) //(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)
6349  {
6350  // fill multi-node and array hostname info to empty
6351  // then handle after all parameters in hand.
6352 
6353  __COUT__ << "Handling printer syntax i=" << i << __E__;
6354 
6355  std::vector<std::string> printerSyntaxArr =
6356  StringMacros::getVectorFromString(nodePair.second[i],
6357  {','} /*delimiter*/);
6358 
6359  if(printerSyntaxArr.size() == 2) // consider if fixed value
6360  {
6361  if(printerSyntaxArr[0] ==
6362  "nnfw") // then node name fixed width
6363  {
6364  sscanf(printerSyntaxArr[1].c_str(),
6365  "%u",
6366  &nodeNameFixedWidth);
6367  __COUTV__(nodeNameFixedWidth);
6368  continue;
6369  }
6370  else if(printerSyntaxArr[0] ==
6371  "hnfw") // then hostname fixed width
6372  {
6373  sscanf(printerSyntaxArr[1].c_str(),
6374  "%u",
6375  &hostnameFixedWidth);
6376  __COUTV__(hostnameFixedWidth);
6377  continue;
6378  }
6379  }
6380 
6381  // unsigned int count = 0;
6382  for(auto& printerSyntaxValue : printerSyntaxArr)
6383  {
6384  __COUTV__(printerSyntaxValue);
6385 
6386  std::vector<std::string> printerSyntaxRange =
6387  StringMacros::getVectorFromString(printerSyntaxValue,
6388  {'-'} /*delimiter*/);
6389  if(printerSyntaxRange.size() == 0 ||
6390  printerSyntaxRange.size() > 2)
6391  {
6392  __SS__ << "Illegal multi-node printer syntax string '"
6393  << printerSyntaxValue << "!'" << __E__;
6394  __SS_THROW__;
6395  }
6396  else if(printerSyntaxRange.size() == 1)
6397  {
6398  // unsigned int index;
6399  __COUTV__(printerSyntaxRange[0]);
6400  // sscanf(printerSyntaxRange[0].c_str(), "%u", &index);
6401  //__COUTV__(index);
6402 
6403  if(i == 4 /*nodeArrayString*/)
6404  nodeIndices.push_back(printerSyntaxRange[0]);
6405  else
6406  hostnameIndices.push_back(printerSyntaxRange[0]);
6407  }
6408  else // printerSyntaxRange.size() == 2
6409  {
6410  unsigned int lo, hi;
6411  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
6412  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
6413  if(hi < lo) // swap
6414  {
6415  lo = hi;
6416  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
6417  }
6418  for(; lo <= hi; ++lo)
6419  {
6420  __COUTVS__(5, lo);
6421  if(i == 4 /*nodeArrayString*/)
6422  nodeIndices.push_back(std::to_string(lo));
6423  else
6424  hostnameIndices.push_back(std::to_string(lo));
6425  }
6426  }
6427  }
6428  }
6429  else
6430  {
6431  __SS__ << "Unexpected parameter[" << i << " '"
6432  << nodePair.second[i] << "' for node " << nodePair.first
6433  << "!" << __E__;
6434  __SS_THROW__;
6435  }
6436  } // end node parameter loop
6437 
6438  __COUTV__(nodeIndices.size());
6439  __COUTV__(hostnameIndices.size());
6440 
6441  if(hostnameIndices.size()) // handle hostname array
6442  {
6443  if(hostnameIndices.size() != nodeIndices.size())
6444  {
6445  __SS__ << "Illegal associated hostname array has count "
6446  << hostnameIndices.size()
6447  << " which is not equal to the node count "
6448  << nodeIndices.size() << "!" << __E__;
6449  __SS_THROW__;
6450  }
6451  }
6452 
6453  if(nodeIndices.size()) // handle multi-node instances
6454  {
6455  unsigned int hostnameCol =
6456  typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME);
6457  // Steps:
6458  // first instance takes current row,
6459  // then copy for remaining instances
6460 
6461  std::vector<std::string> namePieces =
6463  nodePair.first.substr(0, nodePair.first.find(";status=")),
6464  {'*'} /*delimiter*/);
6465  __COUTV__(StringMacros::vectorToString(namePieces));
6466 
6467  if(namePieces.size() < 2)
6468  {
6469  __SS__
6470  << "Illegal multi-node name template - please use * to "
6471  "indicate where the multi-node index should be inserted!"
6472  << __E__;
6473  __SS_THROW__;
6474  }
6475 
6476  std::vector<std::string> hostnamePieces;
6477  if(hostnameIndices.size()) // handle hostname array
6478  {
6479  hostnamePieces = StringMacros::getVectorFromString(
6480  hostname, {'*'} /*delimiter*/);
6481  __COUTV__(StringMacros::vectorToString(hostnamePieces));
6482 
6483  if(hostnamePieces.size() < 2)
6484  {
6485  __SS__
6486  << "Illegal hostname array template - please use * to "
6487  "indicate where the hostname index should be inserted!"
6488  << __E__;
6489  __SS_THROW__;
6490  }
6491  }
6492 
6493  bool isFirst = true;
6494  unsigned int lastArtRow = TableView::INVALID;
6495  for(unsigned int i = 0; i < nodeIndices.size(); ++i)
6496  {
6497  std::string name = namePieces[0];
6498  std::string nodeNameIndex;
6499  for(unsigned int p = 1; p < namePieces.size(); ++p)
6500  {
6501  nodeNameIndex = nodeIndices[i];
6502  if(nodeNameFixedWidth > 1)
6503  {
6504  if(nodeNameIndex.size() > nodeNameFixedWidth)
6505  {
6506  __SS__ << "Illegal node name index '" << nodeNameIndex
6507  << "' - length is longer than fixed width "
6508  "requirement of "
6509  << nodeNameFixedWidth << "!" << __E__;
6510  __SS_THROW__;
6511  }
6512 
6513  // 0 prepend as needed
6514  while(nodeNameIndex.size() < nodeNameFixedWidth)
6515  nodeNameIndex = "0" + nodeNameIndex;
6516  } // end fixed width handling
6517 
6518  name += nodeNameIndex + namePieces[p];
6519  }
6520  __COUTV__(name);
6521 
6522  if(hostnamePieces.size())
6523  {
6524  hostname = hostnamePieces[0];
6525  std::string hostnameIndex;
6526  for(unsigned int p = 1; p < hostnamePieces.size(); ++p)
6527  {
6528  hostnameIndex = hostnameIndices[i];
6529  if(hostnameFixedWidth > 1)
6530  {
6531  if(hostnameIndex.size() > hostnameFixedWidth)
6532  {
6533  __SS__ << "Illegal hostname index '"
6534  << hostnameIndex
6535  << "' - length is longer than fixed width "
6536  "requirement of "
6537  << hostnameFixedWidth << "!" << __E__;
6538  __SS_THROW__;
6539  }
6540 
6541  // 0 prepend as needed
6542  while(hostnameIndex.size() < hostnameFixedWidth)
6543  hostnameIndex = "0" + hostnameIndex;
6544  } // end fixed width handling
6545 
6546  hostname += hostnameIndex + hostnamePieces[p];
6547  }
6548  __COUTV__(hostname);
6549  }
6550  // else use hostname from above
6551 
6552  if(isFirst) // take current row
6553  {
6554  __COUTT__
6555  << author << "... Replacing row UID '"
6556  << typeTable.tableView_
6557  ->getDataView()[row]
6558  [typeTable.tableView_->getColUID()]
6559  << "' with UID '" << name << "'" << __E__;
6560 
6561  // remove from delete map
6562  if(row < maxRowToDelete)
6563  deleteRecordMap[row] = false;
6564  }
6565  else // copy row
6566  {
6567  __COUTT__
6568  << author << "... Copying row UID '"
6569  << typeTable.tableView_
6570  ->getDataView()[row]
6571  [typeTable.tableView_->getColUID()]
6572  << "' to UID '" << name << "'" << __E__;
6573  unsigned int copyRow = typeTable.tableView_->copyRows(
6574  author,
6575  *(typeTable.tableView_),
6576  row,
6577  1 /*srcRowsToCopy*/,
6578  -1 /*destOffsetRow*/,
6579  true /*generateUniqueDataColumns*/);
6580 
6581  // remove from delete map
6582  if(row < maxRowToDelete)
6583  deleteRecordMap[copyRow] = false;
6584  row = copyRow;
6585  }
6586 
6587  typeTable.tableView_->setValueAsString(
6588  name, row, typeTable.tableView_->getColUID());
6589  typeTable.tableView_->setValueAsString(
6590  hostname, row, hostnameCol);
6591  //NOTE: changing UID and copyRows does not change author or date! So change it if not an exact match; so change it now and fill with original archive if exact match
6592  typeTable.tableView_->setValueAsString(
6593  TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT,
6594  row,
6595  commentCol);
6596  typeTable.tableView_->setValueAsString(author, row, authorCol);
6597  typeTable.tableView_->setValue(time(0), row, timestampCol);
6598 
6599  __COUTTV__(typeTable
6600  .tableView_ //comment
6601  ->getDataView()[row][commentCol]);
6602  __COUTTV__(typeTable
6603  .tableView_ //author
6604  ->getDataView()[row][authorCol]);
6605  __COUTTV__(typeTable
6606  .tableView_ //creation time
6607  ->getDataView()[row][timestampCol]);
6608  // Strategy:
6609  // - Take values from best original node match
6610  // - Then overwrite with same values
6611  // - Then overwrite with embedded name values
6612  // - If name matches exactly the original name, then keep Comment, Author, and CreationTime
6613  //i.e., Customize row based on original value map, originalMultinodeSameSiblingValues and originalMultinodeAllSiblingEmbeddedName and originalMultinodeAllSiblingEmbeddedPrinterIndex
6614  {
6615  //find highest score match to original node
6616  __COUT__
6617  << "Looking for best original node match for row=" << row
6618  << " UID='" << name << "'" << __E__;
6619  size_t bestScore = 0;
6620  std::string bestOriginalNodeName;
6621  for(const auto& originalNodePair : originalMultinodeValues)
6622  {
6623  if(originalNodePair.second.find(
6624  ORIG_MAP_ART_PROC_NAME_COL) !=
6625  originalNodePair.second.end())
6626  __COUTTV__(originalNodePair.second.at(
6627  ORIG_MAP_ART_PROC_NAME_COL));
6628  size_t score = 0;
6629  for(size_t c = 0, d = 0;
6630  c < originalNodePair.first.size() && d < name.size();
6631  ++c, ++d)
6632  {
6633  if(name[d] == originalNodePair.first[c])
6634  ++score;
6635  else if(d + 1 < name.size() &&
6636  name[d + 1] == originalNodePair.first[c])
6637  --c; //rewind one for dropped character
6638  else if(c + 1 < originalNodePair.first.size() &&
6639  name[d] == originalNodePair.first[c + 1])
6640  --d; //rewind one for dropped character
6641  }
6642  if(originalNodePair.first.size() == name.size())
6643  ++score;
6644  __COUTVS__(2, score);
6645  if(score > bestScore)
6646  {
6647  bestOriginalNodeName = originalNodePair.first;
6648  bestScore = score;
6649  __COUTVS__(2, bestOriginalNodeName);
6650  __COUTVS__(2, bestScore);
6651  }
6652  } //end scoring loop for best match in originalMultinodeValues
6653 
6654  bool exactMatch = (bestOriginalNodeName == name);
6655  bool needToHandleArtProcessName = false;
6656  std::string artProcessName;
6657 
6658  if(exactMatch ||
6659  originalMultinodeValues.find(bestOriginalNodeName) !=
6660  originalMultinodeValues.end())
6661  {
6662  __COUT__ << "Populating original multinode value from '"
6663  << bestOriginalNodeName << "' into '" << name
6664  << ".'" << __E__;
6665 
6666  for(const auto& valuePair :
6667  originalMultinodeValues.at(bestOriginalNodeName))
6668  {
6669  //(keep new creation time always!) if not exact match then keep new meta info and skip Comment, Author, and CreationTime
6670  if(!exactMatch && (valuePair.first == commentCol ||
6671  valuePair.first == authorCol ||
6672  valuePair.first == timestampCol))
6673  {
6674  __COUTT__
6675  << "Not exact node name match, so keeping "
6676  "default meta info for node: "
6677  << name << "[" << row << "]["
6678  << valuePair.first
6679  << "] /= " << valuePair.second << " keep= "
6680  << typeTable.tableView_
6681  ->getDataView()[row][valuePair.first]
6682  << __E__;
6683  continue;
6684  }
6685 
6686  __COUTT__ << "Customizing node: " << name << "["
6687  << row << "][" << valuePair.first
6688  << "] = " << valuePair.second << __E__;
6689  //handle special columns, otherwise normal columns in type table
6690  if(valuePair.first == ORIG_MAP_ART_PROC_NAME_COL)
6691  {
6692  __COUTT__ << "NEED Special art Process Name "
6693  "column value: "
6694  << valuePair.second << __E__;
6695  needToHandleArtProcessName = true;
6696  artProcessName = valuePair.second;
6697  continue;
6698  artTable->tableView_->setValueAsString(
6699  valuePair.second, row, artProcessNameCol);
6700  }
6701  else
6702  typeTable.tableView_->setValueAsString(
6703  valuePair.second, row, valuePair.first);
6704  }
6705  }
6706  else
6707  __COUT__ << "Did not find '" << name
6708  << "' in original value cache. Looking for "
6709  "bestOriginalNodeName="
6710  << bestOriginalNodeName << __E__;
6711 
6712  __COUTV__(exactMatch);
6713  if(!exactMatch) //not exact match, so apply sibling rules
6714  {
6715  if(originalMultinodeSameSiblingValues.find(
6716  nodePair.first) !=
6717  originalMultinodeSameSiblingValues.end())
6718  {
6719  __COUT__ << "Applying multinode sibling same value "
6720  "rules for row="
6721  << row << " UID='" << name << "'" << __E__;
6722  for(const auto& sameValuePair :
6723  originalMultinodeSameSiblingValues.at(
6724  nodePair.first))
6725  {
6726  if(!sameValuePair.second.first)
6727  continue;
6728  __COUTT__
6729  << "Found originalMultinodeSameSiblingValues["
6730  << nodePair.first << "]["
6731  << sameValuePair.first /* col */ << "] = "
6732  << sameValuePair.second.first << " --> "
6733  << sameValuePair.second.second << __E__;
6734 
6735  //handle special columns, otherwise normal columns in type table
6736  if(sameValuePair.first ==
6737  ORIG_MAP_ART_PROC_NAME_COL)
6738  {
6739  __COUTT__ << "NEED Special art Process Name "
6740  "column value: "
6741  << sameValuePair.second.second
6742  << __E__;
6743  needToHandleArtProcessName = true;
6744  artProcessName = sameValuePair.second.second;
6745  continue;
6746  artTable->tableView_->setValueAsString(
6747  sameValuePair.second.second,
6748  row,
6749  artProcessNameCol);
6750  }
6751  else
6752  typeTable.tableView_->setValueAsString(
6753  sameValuePair.second.second,
6754  row,
6755  sameValuePair.first);
6756  } //end loop to apply multinode same sibling values
6757  }
6758 
6759  //do originalMultinodeAllSiblingEmbeddedPrinterIndex before originalMultinodeAllSiblingEmbeddedName, so that originalMultinodeAllSiblingEmbeddedName has priority
6760  if(originalMultinodeAllSiblingEmbeddedPrinterIndex.find(
6761  nodePair.first) !=
6762  originalMultinodeAllSiblingEmbeddedPrinterIndex.end())
6763  {
6764  __COUT__ << "Applying multinode sibling embbeded "
6765  "printer syntax index rules for row="
6766  << row << " UID='" << name
6767  << "' and printer index='" << nodeNameIndex
6768  << "'" << __E__;
6769  for(const auto& embedValuePair :
6770  originalMultinodeAllSiblingEmbeddedPrinterIndex
6771  .at(nodePair.first))
6772  {
6773  if(!embedValuePair.second.first ||
6774  embedValuePair.second.second.size() < 2)
6775  continue;
6776  __COUTT__
6777  << "Found "
6778  "originalMultinodeAllSiblingEmbeddedPrinte"
6779  "rIndex["
6780  << nodePair.first << "]["
6781  << embedValuePair.first /* col */ << "] = "
6782  << embedValuePair.second.first << " --> "
6784  embedValuePair.second.second)
6785  << __E__;
6786  std::string embedValue =
6788  embedValuePair.second.second,
6789  nodeNameIndex);
6790  __COUTTV__(embedValue);
6791 
6792  //handle special columns, otherwise normal columns in type table
6793  if(embedValuePair.first ==
6794  ORIG_MAP_ART_PROC_NAME_COL)
6795  {
6796  __COUTT__ << "NEED Special art Process Name "
6797  "column value: "
6798  << embedValue << __E__;
6799  needToHandleArtProcessName = true;
6800  artProcessName = embedValue;
6801  continue;
6802  artTable->tableView_->setValueAsString(
6803  embedValue, row, artProcessNameCol);
6804  }
6805  else
6806  typeTable.tableView_->setValueAsString(
6807  embedValue, row, embedValuePair.first);
6808  } //end loop to apply multinode same sibling values
6809  }
6810 
6811  if(originalMultinodeAllSiblingEmbeddedName.find(
6812  nodePair.first) !=
6813  originalMultinodeAllSiblingEmbeddedName.end())
6814  {
6815  __COUT__ << "Applying multinode sibling embbeded "
6816  "name rules for row="
6817  << row << " UID='" << name << "'" << __E__;
6818  for(const auto& embedValuePair :
6819  originalMultinodeAllSiblingEmbeddedName.at(
6820  nodePair.first))
6821  {
6822  if(!embedValuePair.second.first ||
6823  embedValuePair.second.second.size() < 2)
6824  continue;
6825  __COUTT__
6826  << "Found "
6827  "originalMultinodeAllSiblingEmbeddedName["
6828  << nodePair.first << "]["
6829  << embedValuePair.first /* col */ << "] = "
6830  << embedValuePair.second.first << " --> "
6832  embedValuePair.second.second)
6833  << __E__;
6834  std::string embedValue =
6836  embedValuePair.second.second, name);
6837  __COUTTV__(embedValue);
6838 
6839  //handle special columns, otherwise normal columns in type table
6840  if(embedValuePair.first ==
6841  ORIG_MAP_ART_PROC_NAME_COL)
6842  {
6843  __COUTT__ << "NEED Special art Process Name "
6844  "column value: "
6845  << embedValue << __E__;
6846  needToHandleArtProcessName = true;
6847  artProcessName = embedValue;
6848  continue;
6849  artTable->tableView_->setValueAsString(
6850  embedValue, row, artProcessNameCol);
6851  }
6852  else
6853  typeTable.tableView_->setValueAsString(
6854  embedValue, row, embedValuePair.first);
6855  } //end loop to apply multinode same sibling values
6856  }
6857 
6858  __COUTV__(needToHandleArtProcessName);
6859  if(needToHandleArtProcessName)
6860  {
6861  __COUTT__ << "Special art Process Name column value: "
6862  << artProcessName << __E__;
6863  //need to find row or make row for art record
6864  std::string artRecord =
6865  typeTable.tableView_->getDataView()
6866  [row][typeTable.tableView_->findCol(
6867  ARTDAQTableBase::colARTDAQNotReader_
6868  .colLinkToArtUID_)];
6869  __COUTTV__(artRecord);
6870 
6871  const unsigned int artCommentCol =
6872  artTable->tableView_->findColByType(
6873  TableViewColumnInfo::TYPE_COMMENT);
6874  const unsigned int artAuthorCol =
6875  artTable->tableView_->findColByType(
6876  TableViewColumnInfo::TYPE_AUTHOR);
6877  const unsigned int artTimestampCol =
6878  artTable->tableView_->findColByType(
6879  TableViewColumnInfo::TYPE_TIMESTAMP);
6880 
6881  unsigned int artRow = artTable->tableView_->findRow(
6882  artTable->tableView_->getColUID(),
6883  artRecord,
6884  0 /* offsetRow */,
6885  true /* doNotThrow*/);
6886  __COUTTV__(artRow);
6887  if(artRow == TableView::INVALID) //need to make row!
6888  {
6889  __COUTT__ << "Need to make art Process record... "
6890  "artRecord="
6891  << artRecord << __E__;
6892 
6893  //change lastArtRow to best match's art row
6894  {
6895  __COUTTV__(bestOriginalNodeName);
6896  const unsigned int bestMatchRow =
6897  typeTable.tableView_->findRow(
6898  typeTable.tableView_->getColUID(),
6899  bestOriginalNodeName);
6900  __COUTTV__(bestMatchRow);
6901 
6902  std::string bestMatchArtRecord =
6903  typeTable.tableView_->getDataView()
6904  [bestMatchRow]
6905  [typeTable.tableView_->findCol(
6906  ARTDAQTableBase::
6907  colARTDAQNotReader_
6908  .colLinkToArtUID_)];
6909  __COUTTV__(bestMatchArtRecord);
6910 
6911  unsigned int bestMatchArtRow =
6912  artTable->tableView_->findRow(
6913  artTable->tableView_->getColUID(),
6914  bestMatchArtRecord,
6915  0 /* offsetRow */,
6916  true /* doNotThrow*/);
6917  __COUTTV__(bestMatchArtRow);
6918  if(bestMatchArtRow !=
6919  TableView::
6920  INVALID) //found best match's art record
6921  lastArtRow =
6922  bestMatchArtRow; //use best match's art record for copy
6923  __COUTTV__(lastArtRow);
6924  }
6925 
6926  if(lastArtRow != TableView::INVALID)
6927  {
6928  __COUTT__ << "Copying art Process record... "
6929  "from lastArtRow="
6930  << lastArtRow << __E__;
6931  unsigned int copyRow =
6932  artTable->tableView_->copyRows(
6933  author,
6934  *(artTable->tableView_),
6935  lastArtRow,
6936  1 /*srcRowsToCopy*/,
6937  -1 /*destOffsetRow*/,
6938  true /*generateUniqueDataColumns*/);
6939  artTable->tableView_->setValueAsString(
6940  artRecord,
6941  copyRow,
6942  artTable->tableView_->getColUID());
6943  artRow = copyRow;
6944 
6945  //NOTE: changing UID and copyRows does not change author or date! So change it if not an exact match; so change it now and fill with original archive if exact match
6946  artTable->tableView_->setValueAsString(
6948  DATATYPE_COMMENT_DEFAULT,
6949  artRow,
6950  artCommentCol);
6951  artTable->tableView_->setValueAsString(
6952  author, artRow, artAuthorCol);
6953  artTable->tableView_->setValue(
6954  time(0), artRow, artTimestampCol);
6955  }
6956  else
6957  {
6958  __COUTT__ << "Creating art Process record... "
6959  "artRecord="
6960  << artRecord << __E__;
6961 
6962  artRow = artTable->tableView_->addRow(
6963  author,
6964  true /*incrementUniqueData*/,
6965  artRecord);
6966  }
6967  __COUTT__
6968  << "Made art Process record... artRecord="
6969  << artRecord << __E__;
6970  } //end making row
6971 
6972  __COUTT__ << "Modify art Process record based on "
6973  "sibling rules... artRecord="
6974  << artRecord
6975  << " artProcessName=" << artProcessName
6976  << __E__;
6977 
6978  artTable->tableView_->setValueAsString(
6979  artProcessName, artRow, artProcessNameCol);
6980  lastArtRow = artRow;
6981  }
6982  } //end applying sibling value rules
6983  else if(
6984  needToHandleArtProcessName) //get lastArtRow for future multirecord siblings
6985  {
6986  std::string artRecord =
6987  typeTable.tableView_->getDataView()
6988  [row][typeTable.tableView_->findCol(
6989  ARTDAQTableBase::colARTDAQNotReader_
6990  .colLinkToArtUID_)];
6991  __COUTTV__(artRecord);
6992  unsigned int artRow = artTable->tableView_->findRow(
6993  artTable->tableView_->getColUID(),
6994  artRecord,
6995  0 /* offsetRow */,
6996  true /* doNotThrow*/);
6997  __COUTTV__(artRow);
6998  if(artRow !=
6999  TableView::INVALID) //found valid art record row
7000  lastArtRow = artRow;
7001  }
7002 
7003  __COUTTV__(lastArtRow);
7004 
7005  if(TTEST(1))
7006  {
7007  __COUTTV__(row);
7008  if(row < maxRowToDelete)
7009  __COUTTV__(deleteRecordMap[row]);
7010 
7011  __COUTTV__(typeTable
7012  .tableView_ //comment
7013  ->getDataView()[row][commentCol]);
7014  __COUTTV__(typeTable
7015  .tableView_ //author
7016  ->getDataView()[row][authorCol]);
7017  __COUTTV__(typeTable
7018  .tableView_ //creation time
7019  ->getDataView()[row][timestampCol]);
7020  }
7021  } // end copy and customize row handling
7022 
7023  isFirst = false;
7024  } // end multi-node loop
7025  } // end multi-node handling
7026  } // end node record loop
7027 
7028  { // delete record handling
7029  __COUT__ << "Deleting '" << nodeTypePair.first
7030  << "' records not specified..." << __E__;
7031 
7032  // unsigned int row;
7033  std::set<unsigned int> orderedRowSet; // need to delete in reverse order
7034  for(auto& deletePair : deleteRecordMap)
7035  {
7036  if(!deletePair.second)
7037  {
7038  __COUTT__ << "Row keep = " << deletePair.first << __E__;
7039  continue; // only delete if true
7040  }
7041 
7042  __COUTT__ << "Row delete = " << deletePair.first << __E__;
7043  orderedRowSet.emplace(deletePair.first);
7044  }
7045 
7046  // delete elements in reverse order
7047  for(std::set<unsigned int>::reverse_iterator rit = orderedRowSet.rbegin();
7048  rit != orderedRowSet.rend();
7049  rit++)
7050  typeTable.tableView_->deleteRow(*rit);
7051 
7052  } // end delete record handling
7053 
7054  if(TTEST(1) && artTable)
7055  {
7056  std::stringstream ss;
7057  artTable->tableView_->print(ss);
7058  __COUT_MULTI__(1, ss.str());
7059  }
7060 
7061  if(hasArtProcessName && artTable)
7062  artTable->tableView_
7063  ->init(); // verify new art table modifications (throws runtime_errors)
7064 
7065  if(TTEST(1))
7066  {
7067  std::stringstream ss;
7068  typeTable.tableView_->print(ss);
7069  __COUT_MULTI__(1, ss.str());
7070  }
7071 
7072  typeTable.tableView_->init(); // verify new table (throws runtime_errors)
7073 
7074  } // end node type loop
7075 
7076  if(TTEST(1))
7077  {
7078  {
7079  std::stringstream ss;
7080  artdaqSupervisorTable.tableView_->print(ss);
7081  __COUT_MULTI__(1, ss.str());
7082  }
7083  {
7084  std::stringstream ss;
7085  artdaqSubsystemTable.tableView_->print(ss);
7086  __COUT_MULTI__(1, ss.str());
7087  }
7088  }
7089 
7090  artdaqSupervisorTable.tableView_
7091  ->init(); // verify new table (throws runtime_errors)
7092  artdaqSubsystemTable.tableView_
7093  ->init(); // verify new table (throws runtime_errors)
7094  }
7095  catch(...)
7096  {
7097  __COUT__ << "Table errors while creating ARTDAQ nodes. Erasing all newly "
7098  "created table versions."
7099  << __E__;
7100  throw; // re-throw
7101  } // end catch
7102 
7103  __COUT__ << "Edits complete for artdaq nodes and subsystems.. now save and activate "
7104  "groups, and update aliases!"
7105  << __E__;
7106 
7107  TableGroupKey newConfigurationGroupKey;
7108  if(0) //keep for debugging save process
7109  {
7110  __SS__ << "DEBUG blocking save!" << __E__;
7111  __SS_THROW__;
7112  }
7113  {
7114  std::string localAccumulatedWarnings;
7115  configGroupEdit.saveChanges(configGroupEdit.originalGroupName_,
7116  newConfigurationGroupKey,
7117  nullptr /*foundEquivalentGroupKey*/,
7118  true /*activateNewGroup*/,
7119  true /*updateGroupAliases*/,
7120  true /*updateTableAliases*/,
7121  nullptr /*newBackboneKey*/,
7122  nullptr /*foundEquivalentBackboneKey*/,
7123  &localAccumulatedWarnings);
7124  }
7125 
7126 } // end setAndActivateARTDAQSystem()
7127 
7128 //==============================================================================
7129 int ARTDAQTableBase::getSubsytemId(ConfigurationTree subsystemNode)
7130 {
7131  // using row forces a unique ID from 0 to rows-1
7132  // note: default no defined subsystem link to id=1; so add 2
7133 
7134  return subsystemNode.getNodeRow() + 2;
7135 } // end getSubsytemId()
7136 
7137 //==============================================================================
7139 void ARTDAQTableBase::addCommentWhitespace(std::ostream& os, size_t lineLength)
7140 {
7141  for(size_t i = 0; true; i += 20)
7142  {
7143  if(lineLength < FCL_COMMENT_POSITION + i) // pad to FCL_COMMENT_POSITION + i
7144  {
7145  os << std::string(FCL_COMMENT_POSITION + i - lineLength, ' ');
7146  break;
7147  }
7148  }
7149  os << " // ";
7150 } //end addCommentWhitespace()
7151 
7152 //==============================================================================
7153 std::string ARTDAQTableBase::getStructureAsJSON(
7154  const ConfigurationManager* /* configManager */)
7155 {
7156  if(fclMap_.size() == 0) //assume was not generated (not first )
7157  genFlatFHiCL();
7158  std::stringstream oss;
7159 
7160  oss << "{" << __E__;
7161 
7162  if(fclMap_.size() > 1)
7163  {
7164  // Multiple types - keep grouped structure
7165  bool firstType = true;
7166  for(const auto& typePairMap : fclMap_)
7167  {
7168  if(!firstType)
7169  oss << ",";
7170  oss << "\t\"" << getTypeString(typePairMap.first) << "\": {" << __E__;
7171 
7172  bool firstEntry = true;
7173  for(const auto& fclPair : typePairMap.second)
7174  {
7175  if(!firstEntry)
7176  oss << ",";
7177  oss << "\t\t\"" << fclPair.first << "\": \""
7178  << StringMacros::escapeJSONStringEntities(fclPair.second) << "\""
7179  << __E__;
7180  firstEntry = false;
7181  }
7182 
7183  oss << "\t}" << __E__;
7184  firstType = false;
7185  }
7186  }
7187  else
7188  {
7189  // Single type (normal case) - flat structure
7190  bool firstEntry = true;
7191  for(const auto& typePairMap : fclMap_)
7192  {
7193  for(const auto& fclPair : typePairMap.second)
7194  {
7195  if(!firstEntry)
7196  oss << ",";
7197  oss << "\t\"" << fclPair.first << "\": \""
7198  << StringMacros::escapeJSONStringEntities(fclPair.second) << "\""
7199  << __E__;
7200  firstEntry = false;
7201  }
7202  }
7203  }
7204 
7205  oss << "}" << __E__;
7206 
7207  return oss.str();
7208 } //end getStructureAsJSON()
7209 
7210 //==============================================================================
7214  size_t maxFragmentSizeBytes,
7215  size_t routingTimeoutMs,
7216  size_t routingRetryCount,
7217  ProgressBar* progressBar)
7218 {
7219  if(artdaqSupervisorNode.isDisconnected())
7220  {
7221  __SS__ << "ARTDAQ Supervisor node is disconnected while generating boot.txt "
7222  << "content." << __E__;
7223  __SS_THROW__;
7224  }
7225 
7226  const ARTDAQInfo& info = extractARTDAQInfo(artdaqSupervisorNode,
7227  false /*getStatusFalseNodes*/,
7228  false /*doWriteFHiCL*/,
7229  maxFragmentSizeBytes,
7230  routingTimeoutMs,
7231  routingRetryCount,
7232  progressBar);
7233 
7234  int debugLevel =
7235  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colDAQInterfaceDebugLevel_)
7236  .getValue<int>();
7237  std::string setupScript =
7238  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colDAQSetupScript_).getValue();
7239 
7240  return getBootFileContentFromInfo(info, setupScript, debugLevel);
7241 } //end getBootFileContent()
7242 
7243 //==============================================================================
7247  const std::string& setupScript,
7248  int debugLevel)
7249 {
7250  std::stringstream o;
7251 
7252  o << "DAQ setup script: " << setupScript << std::endl;
7253  o << "debug level: " << debugLevel << std::endl;
7254  o << std::endl;
7255 
7256  if(info.subsystems.size() > 1)
7257  {
7258  for(auto& ss : info.subsystems)
7259  {
7260  if(ss.first == 0)
7261  continue;
7262  o << "Subsystem id: " << ss.first << std::endl;
7263  if(ss.second.destination != 0)
7264  {
7265  o << "Subsystem destination: " << ss.second.destination << std::endl;
7266  }
7267  for(auto& sss : ss.second.sources)
7268  {
7269  o << "Subsystem source: " << sss << std::endl;
7270  }
7271  if(ss.second.eventMode)
7272  {
7273  o << "Subsystem fragmentMode: False" << std::endl;
7274  }
7275  o << std::endl;
7276  }
7277  }
7278 
7279  for(auto& builder : info.processes.at(ARTDAQAppType::EventBuilder))
7280  {
7281  o << "EventBuilder host: " << builder.hostname << std::endl;
7282  o << "EventBuilder label: " << builder.label << std::endl;
7283  if(builder.subsystem != 1)
7284  {
7285  o << "EventBuilder subsystem: " << builder.subsystem << std::endl;
7286  }
7287  if(builder.allowed_processors != "")
7288  {
7289  o << "EventBuilder allowed_processors: " << builder.allowed_processors
7290  << std::endl;
7291  }
7292  o << std::endl;
7293  }
7294 
7295  for(auto& logger : info.processes.at(ARTDAQAppType::DataLogger))
7296  {
7297  o << "DataLogger host: " << logger.hostname << std::endl;
7298  o << "DataLogger label: " << logger.label << std::endl;
7299  if(logger.subsystem != 1)
7300  {
7301  o << "DataLogger subsystem: " << logger.subsystem << std::endl;
7302  }
7303  if(logger.allowed_processors != "")
7304  {
7305  o << "DataLogger allowed_processors: " << logger.allowed_processors
7306  << std::endl;
7307  }
7308  o << std::endl;
7309  }
7310 
7311  for(auto& dispatcher : info.processes.at(ARTDAQAppType::Dispatcher))
7312  {
7313  o << "Dispatcher host: " << dispatcher.hostname << std::endl;
7314  o << "Dispatcher label: " << dispatcher.label << std::endl;
7315  if(dispatcher.subsystem != 1)
7316  {
7317  o << "Dispatcher subsystem: " << dispatcher.subsystem << std::endl;
7318  }
7319  if(dispatcher.allowed_processors != "")
7320  {
7321  o << "Dispatcher allowed_processors: " << dispatcher.allowed_processors
7322  << std::endl;
7323  }
7324  o << std::endl;
7325  }
7326 
7327  for(auto& rm : info.processes.at(ARTDAQAppType::RoutingManager))
7328  {
7329  o << "RoutingManager host: " << rm.hostname << std::endl;
7330  o << "RoutingManager label: " << rm.label << std::endl;
7331  if(rm.subsystem != 1)
7332  {
7333  o << "RoutingManager subsystem: " << rm.subsystem << std::endl;
7334  }
7335  if(rm.allowed_processors != "")
7336  {
7337  o << "RoutingManager allowed_processors: " << rm.allowed_processors
7338  << std::endl;
7339  }
7340  o << std::endl;
7341  }
7342 
7343  return o.str();
7344 } //end getBootFileContentFromInfo()
<virtual so future plugins can inherit from multiple table base classes
static void outputDataReceiverFHICL(const ConfigurationTree &receiverNode, ARTDAQAppType appType, size_t maxFragmentSizeBytes=DEFAULT_MAX_FRAGMENT_SIZE, size_t routingTimeoutMs=DEFAULT_ROUTING_TIMEOUT_MS, size_t routingRetryCount=DEFAULT_ROUTING_RETRY_COUNT, std::string *returnFcl=nullptr)
static void insertArtProcessBlock(std::ostream &out, std::string &tabStr, std::string &commentStr, const std::string &parentPath, ConfigurationTree art, ConfigurationTree subsystemLink=ConfigurationTree(), size_t routingTimeoutMs=DEFAULT_ROUTING_TIMEOUT_MS, size_t routingRetryCount=DEFAULT_ROUTING_RETRY_COUNT)
static const std::string ARTDAQ_FCL_PATH
Tree-path rule is, if the last link in the path is a group link with a specified group ID,...
static std::string insertModuleType(std::ostream &out, std::string &tabStr, std::string &commentStr, const std::string &parentPath, ConfigurationTree moduleTypeNode)
static std::string getBootFileContentFromInfo(const ARTDAQInfo &info, const std::string &setupScript, int debugLevel)
static bool isARTDAQEnabled(const ConfigurationManager *cfgMgr)
isARTDAQEnabled
static void insertParameters(std::ostream &out, std::string &tabStr, std::string &commentStr, const std::string &parentPath, ConfigurationTree parameterLink, const std::string &parameterPreamble, bool onlyInsertAtTableParameters=false, bool includeAtTableParameters=false)
static void setAndActivateARTDAQSystem(ConfigurationManagerRW *cfgMgr, const std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, const std::map< std::string, std::string > &subsystemObjectMap)
static struct ots::ARTDAQTableBase::ProcessTypes processTypes_
Note!!!! processTypes_ must be instantiate after the static artdaq table names (to construct map in c...
static void outputOnlineMonitorFHICL(const ConfigurationTree &onlineMonitorNode)
std::string getBootFileContent(ConfigurationTree artdaqSupervisorNode, size_t maxFragmentSizeBytes=DEFAULT_MAX_FRAGMENT_SIZE, size_t routingTimeoutMs=DEFAULT_ROUTING_TIMEOUT_MS, size_t routingRetryCount=DEFAULT_ROUTING_RETRY_COUNT, ProgressBar *progressBar=0)
static const ARTDAQInfo & getARTDAQSystem(ConfigurationManagerRW *cfgMgr, std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, std::map< std::string, std::string > &subsystemObjectMap, std::vector< std::string > &artdaqSupervisoInfo)
static void insertMetricsBlock(std::ostream &out, std::string &tabStr, std::string &commentStr, const std::string &parentPath, ConfigurationTree daqNode)
insertMetricsBlock
const std::string & getUsername(void) const
Getters.
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
const TableBase * getTableByName(const std::string &configurationName) const
void initPrereqsForARTDAQ(void)
loadGroupHistory
const unsigned int & getRow(void) const
getRow
bool isDisconnected(void) const
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
const std::string & getTableName(void) const
getTableName
T getValueWithDefault(const T &defaultValue) const
const std::string & getValueAsString(bool returnLinkTableValue=false) const
const ConfigurationManager * getConfigurationManager(void) const
extracting information from node
void getValue(T &value) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
std::string getParentLinkIndex(void) const
getParentLinkIndex
const std::string & getUIDAsString(void) const
const unsigned int & getNodeRow(void) const
getNodeRow
const std::string & getFieldName(void) const
alias for getValueName
std::string getParentLinkID(void) const
getParentLinkID
const std::string & getParentTableName(void) const
getParentTableName
const std::string & getParentRecordName(void) const
getParentRecordName
const std::string & getParentLinkColumnName(void) const
getParentLinkColumnName
void step()
thread safe
Definition: ProgressBar.cc:74
bool isFirstAppInContext_
for managing things that should only happen once per node (e.g., write files). If used,...
Definition: TableBase.h:134
std::string str() const
alternative alias method
Definition: TableGroupKey.h:23
bool isUID(void) const
isUID
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
< in included .icc source
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
Definition: TableView.cc:1081
void deleteRow(int r)
Definition: TableView.cc:3564
unsigned int getColStatus(void) const
Definition: TableView.cc:1398
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1985
unsigned int copyRows(const std::string &author, const TableView &src, unsigned int srcOffsetRow=0, unsigned int srcRowsToCopy=(unsigned int) -1, unsigned int destOffsetRow=(unsigned int) -1, unsigned char generateUniqueDataColumns=false, const std::string &baseNameAutoUID="")
Definition: TableView.cc:126
const std::string & setUniqueColumnValue(unsigned int row, unsigned int col, std::string baseValueAsString="", bool doMathAppendStrategy=false, std::string childLinkIndex="", std::string groupId="")
Definition: TableView.cc:1101
void init(void)
Definition: TableView.cc:195
unsigned int getColUID(void) const
Definition: TableView.cc:1313
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1962
void setValue(const T &value, unsigned int row, unsigned int col)
< in included .icc source
unsigned int addRow(const std::string &author="", unsigned char incrementUniqueData=false, const std::string &baseNameAutoUID="", unsigned int rowToAdd=(unsigned int) -1, std::string childLinkIndex="", std::string groupId="")
Definition: TableView.cc:3479
const XDAQContext * getTheARTDAQSupervisorContext(void) const
artdaq specific get methods
defines used also by OtsConfigurationWizardSupervisor
ARTDAQ DAQ Parameter Column names.
ARTDAQ Builder/Logger/Dispatcher Column names.
ARTDAQ Reader Column names.
ARTDAQ Subsystem Column names.
ARTDAQ Supervisor Column names.
TableEditStruct & getTableEditStruct(const std::string &tableName, bool markModified=false)
Note: if markModified, and table not found in group, this function will try to add it to group.
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static const std::string & trim(std::string &s)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
setToString ~
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
vectorToString ~
static std::string escapeJSONStringEntities(const std::string &str)
static bool extractCommonChunks(const std::vector< std::string > &haystack, std::vector< std::string > &commonChunksToReturn, std::vector< std::string > &wildcardStrings, unsigned int &fixedWildcardLength)
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static std::string decodeURIComponent(const std::string &data)
static std::string stackTrace(void)