otsdaq  3.07.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  __COUTT__ << "Ignoring missing daqFragmentIDs column associated with "
869  "fragment_ids for 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  // _clone nodes are always independent — never group them into a multinode
3631  if(nodeName.find("_clone") != std::string::npos ||
3632  otherNode.first.find("_clone") != std::string::npos)
3633  continue;
3634 
3635  //__COUTV__(subsystemName);
3636  //__COUTV__(otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID).getValue());
3637 
3638  if(subsystemName ==
3639  otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID)
3640  .getValue())
3641  {
3642  // possible multi-node situation
3643  //__COUT__ << "Checking for multi-node..." << __E__;
3644 
3645  //__COUTV__(thisNode.getNodeRow());
3646  //__COUTV__(otherNode.second.getNodeRow());
3647 
3648  auto otherNodeColumns = otherNode.second.getChildren();
3649 
3650  bool isMultiNode = true;
3651  for(unsigned int i = 0;
3652  i < thisNodeColumns.size() && i < otherNodeColumns.size();
3653  ++i)
3654  {
3655  // skip columns that do not need to be checked for multi-node consideration
3656  if(skipColumns.find(thisNodeColumns[i].first) !=
3657  skipColumns.end() ||
3658  thisNodeColumns[i].second.isLinkNode())
3659  continue;
3660 
3661  // at this point must match for multinode
3662 
3663  //__COUTV__(thisNodeColumns[i].first);
3664  //__COUTV__(otherNodeColumns[i].first);
3665 
3666  //__COUTV__(thisNodeColumns[i].second.getValue());
3667  //__COUTV__(otherNodeColumns[i].second.getValue());
3668 
3669  if(thisNodeColumns[i].second.getValue() !=
3670  otherNodeColumns[i].second.getValue())
3671  {
3672  __COUT__ << "Mismatch, not multi-node member." << __E__;
3673  isMultiNode = false;
3674  break;
3675  }
3676  }
3677 
3678  if(isMultiNode)
3679  {
3680  __COUT__ << "Found '" << nodeName
3681  << "' multi-node member candidate '"
3682  << otherNode.first << "'" << __E__;
3683 
3684  //use StringMacros::encodeURIComponent because dashes will confuse printer syntax later!
3685  if(!multiNodeNames.size()) // add this node first!
3686  {
3687  multiNodeNames.push_back(
3688  StringMacros::encodeURIComponent(nodeName));
3689  hostnameArray.push_back(
3690  StringMacros::encodeURIComponent(hostname));
3691  }
3692  multiNodeNames.push_back(
3693  StringMacros::encodeURIComponent(otherNode.first));
3694  hostnameArray.push_back(StringMacros::encodeURIComponent(
3695  otherNode.second.getNode(ARTDAQ_TYPE_TABLE_HOSTNAME)
3696  .getValue()));
3697 
3698  __COUTV__(hostnameArray.back());
3699  skipSet.emplace(
3700  StringMacros::encodeURIComponent(otherNode.first));
3701  }
3702  }
3703  } // end loop to search for multi-node members
3704 
3705  unsigned int nodeFixedWildcardLength = 0, hostFixedWildcardLength = 0;
3706  std::string multiNodeString = "", hostArrayString = "";
3707 
3708  __COUTV__(nodeName);
3709 
3710  if(multiNodeNames.size() > 1)
3711  {
3712  __COUT__ << "Handling multi-node printer syntax" << __E__;
3713 
3714  __COUTTV__(StringMacros::vectorToString(multiNodeNames));
3715  __COUTTV__(StringMacros::vectorToString(hostnameArray));
3716  __COUTTV__(StringMacros::setToString(skipSet));
3717 
3718  // - if match to layout nodes, then maintain layout template, and trim outliers from skipset
3719  if(nodeLayoutNames.find(typeString) != nodeLayoutNames.end())
3720  {
3721  __COUTTV__(
3722  StringMacros::setToString(nodeLayoutNames.at(typeString)));
3723 
3724  // Strategy to use node layout as guide:
3725  // - do two passes
3726  // - find best node layout name based on narrowest match (i.e. smallest match count)
3727  std::string bestNodeLayoutName = "";
3728  size_t bestNodeLayoutMatchCount =
3729  multiNodeNames.size() + 1; //init to 'infinite'
3730  //first pass
3731  for(const auto& layoutNameFull : nodeLayoutNames.at(typeString))
3732  {
3733  __COUTTV__(layoutNameFull);
3734  size_t statusPos = layoutNameFull.find(";status=");
3735  std::string layoutName = layoutNameFull.substr(0, statusPos);
3736  bool layoutStatus = true;
3737  if(statusPos ==
3738  std::string::
3739  npos) //not specified in layout, so take this status and hope!
3740  layoutStatus = status;
3741  else if("0" ==
3742  layoutNameFull.substr(statusPos +
3743  std::string(";status=").size()))
3744  layoutStatus = false;
3745 
3746  __COUTTV__(layoutStatus);
3747 
3748  if(layoutStatus != status)
3749  {
3750  __COUTT__ << "Status mismatch for template" << __E__;
3751  break;
3752  }
3753 
3754  auto layoutSplit =
3755  StringMacros::getVectorFromString(layoutName, {'*'});
3756  __COUTTV__(StringMacros::vectorToString(layoutSplit));
3757 
3758  bool exactMatch = true;
3759  size_t pos = 0;
3760  for(const auto& layoutSeg : layoutSplit)
3761  if((pos = nodeName.find(layoutSeg, pos)) ==
3762  std::string::npos)
3763  {
3764  __COUTT__ << "Did not find '" << layoutSeg << "' in '"
3765  << nodeName << "'" << __E__;
3766  exactMatch = false;
3767  break;
3768  }
3769 
3770  __COUTTV__(exactMatch);
3771  if(exactMatch)
3772  {
3773  size_t nodeLayoutMatchCount = 1;
3774 
3775  __COUT__ << "Found layout template name match! '"
3776  << layoutName << "' for node '" << nodeName
3777  << ".' Trimming multinode candidates to match..."
3778  << __E__;
3779 
3780  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3781  {
3782  __COUTTV__(multiNodeNames[i]);
3783  std::string multiNodeName =
3785  multiNodeNames[i]);
3786  __COUTTV__(multiNodeName);
3787  bool exactMatch = true;
3788  size_t pos = 0;
3789  for(const auto& layoutSeg : layoutSplit)
3790  if((pos = multiNodeName.find(layoutSeg, pos)) ==
3791  std::string::npos)
3792  {
3793  __COUTT__ << "Did not find '" << layoutSeg
3794  << "' in '" << multiNodeName << "'"
3795  << __E__;
3796  exactMatch = false;
3797  break;
3798  }
3799 
3800  if(exactMatch)
3801  {
3802  ++nodeLayoutMatchCount;
3803  __COUTT__ << "Found '" << layoutName << "' in '"
3804  << multiNodeName << "'" << __E__;
3805  }
3806 
3807  } //end loop to trim multinode candidates
3808 
3809  __COUTTV__(nodeLayoutMatchCount);
3810  if(nodeLayoutMatchCount < bestNodeLayoutMatchCount)
3811  {
3812  bestNodeLayoutName = layoutNameFull;
3813  bestNodeLayoutMatchCount = nodeLayoutMatchCount;
3814  __COUTTV__(bestNodeLayoutName);
3815  __COUTTV__(bestNodeLayoutMatchCount);
3816  }
3817  }
3818  } //end first loop to find best layout node
3819 
3820  __COUTV__(nodeName);
3821  __COUTV__(StringMacros::vectorToString(multiNodeNames));
3822  __COUTV__(StringMacros::vectorToString(hostnameArray));
3823  __COUTV__(StringMacros::setToString(skipSet));
3824 
3825  //second pass, remove from skipSet
3826  if(bestNodeLayoutMatchCount > 0)
3827  {
3828  __COUTV__(bestNodeLayoutName);
3829  std::string layoutNameFull = bestNodeLayoutName;
3830  __COUTTV__(layoutNameFull);
3831  size_t statusPos = layoutNameFull.find(";status=");
3832  std::string layoutName = layoutNameFull.substr(0, statusPos);
3833 
3834  auto layoutSplit =
3835  StringMacros::getVectorFromString(layoutName, {'*'});
3836  __COUTTV__(StringMacros::vectorToString(layoutSplit));
3837 
3838  __COUT__ << "Found layout template name match! '"
3839  << layoutName << "' for node '" << nodeName
3840  << ".' Trimming multinode candidates to match..."
3841  << __E__;
3842 
3843  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3844  {
3845  __COUTTV__(multiNodeNames[i]);
3846  std::string multiNodeName =
3847  StringMacros::decodeURIComponent(multiNodeNames[i]);
3848  __COUTTV__(multiNodeName);
3849  bool exactMatch = true;
3850  size_t pos = 0;
3851  for(const auto& layoutSeg : layoutSplit)
3852  if((pos = multiNodeName.find(layoutSeg, pos)) ==
3853  std::string::npos)
3854  {
3855  __COUTT__ << "Did not find '" << layoutSeg
3856  << "' in '" << multiNodeName << "'"
3857  << __E__;
3858  exactMatch = false;
3859  break;
3860  }
3861 
3862  if(!exactMatch)
3863  {
3864  __COUT__ << "Trimming multinode candidate '"
3865  << multiNodeName << "'" << __E__;
3866  skipSet.erase(multiNodeNames[i]);
3867  multiNodeNames.erase(multiNodeNames.begin() + i);
3868  hostnameArray.erase(hostnameArray.begin() + i);
3869  --i; //rewind for multiNodeNames[i] erase
3870  }
3871  } //end loop to trim multinode candidates
3872  } //end applying layout template name rule
3873  } //end match to layout name templates
3874 
3875  __COUTV__(nodeName);
3876  __COUTV__(StringMacros::vectorToString(multiNodeNames));
3877  __COUTV__(StringMacros::vectorToString(hostnameArray));
3878  __COUTV__(StringMacros::setToString(skipSet));
3879 
3880  std::vector<std::string>
3881  trimmedNodeNames; // track trimmed nodes for collision check
3882  {
3883  // check for alpha-based similarity groupings (ignore numbers and special characters)
3884  unsigned int maxScore = 0;
3885  unsigned int score;
3886  unsigned int minScore = -1;
3887  std::vector<unsigned int> scoreVector;
3888  scoreVector.push_back(-1); // for 0 index (it's perfect)
3889  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
3890  {
3891  score = 0;
3892 
3893  __COUTS__(3) << multiNodeNames[0] << " vs "
3894  << multiNodeNames[i] << __E__;
3895 
3896  // start forward score loop
3897  for(unsigned int j = 0, k = 0; j < multiNodeNames[0].size() &&
3898  k < multiNodeNames[i].size();
3899  ++j, ++k)
3900  {
3901  while(j < multiNodeNames[0].size() &&
3902  !(multiNodeNames[0][j] >= 'a' &&
3903  multiNodeNames[0][j] <= 'z') &&
3904  !(multiNodeNames[0][j] >= 'A' &&
3905  multiNodeNames[0][j] <= 'Z'))
3906  ++j; // skip non-alpha characters
3907  while(k < multiNodeNames[i].size() &&
3908  !(multiNodeNames[i][k] >= 'a' &&
3909  multiNodeNames[i][k] <= 'z') &&
3910  !(multiNodeNames[i][k] >= 'A' &&
3911  multiNodeNames[i][k] <= 'Z'))
3912  ++k; // skip non-alpha characters
3913 
3914  while(k < multiNodeNames[i].size() &&
3915  multiNodeNames[0][j] != multiNodeNames[i][k])
3916  ++k; // skip non-matching alpha characters
3917 
3918  __COUTS__(3)
3919  << j << "-" << k << " of " << multiNodeNames[0].size()
3920  << "-" << multiNodeNames[i].size() << __E__;
3921 
3922  if(j < multiNodeNames[0].size() &&
3923  k < multiNodeNames[i].size())
3924  ++score; // found a matching letter!
3925  } // end forward score loop
3926 
3927  __COUTVS__(3, score);
3928 
3929  // start backward score loop
3930  for(unsigned int j = multiNodeNames[0].size() - 1,
3931  k = multiNodeNames[i].size() - 1;
3932  j < multiNodeNames[0].size() &&
3933  k < multiNodeNames[i].size();
3934  --j, --k)
3935  {
3936  while(j < multiNodeNames[0].size() &&
3937  !(multiNodeNames[0][j] >= 'a' &&
3938  multiNodeNames[0][j] <= 'z') &&
3939  !(multiNodeNames[0][j] >= 'A' &&
3940  multiNodeNames[0][j] <= 'Z'))
3941  --j; // skip non-alpha characters
3942  while(k < multiNodeNames[i].size() &&
3943  !(multiNodeNames[i][k] >= 'a' &&
3944  multiNodeNames[i][k] <= 'z') &&
3945  !(multiNodeNames[i][k] >= 'A' &&
3946  multiNodeNames[i][k] <= 'Z'))
3947  --k; // skip non-alpha characters
3948 
3949  while(k < multiNodeNames[i].size() &&
3950  multiNodeNames[0][j] != multiNodeNames[i][k])
3951  --k; // skip non-matching alpha characters
3952 
3953  __COUTS__(3) << "BACK" << j << "-" << k << " of "
3954  << multiNodeNames[0].size() << "-"
3955  << multiNodeNames[i].size() << __E__;
3956 
3957  if(j < multiNodeNames[0].size() &&
3958  k < multiNodeNames[i].size())
3959  ++score; // found a matching letter!
3960  } // end backward score loop
3961 
3962  __COUTVS__(3, score / 2.0);
3963 
3964  scoreVector.push_back(score);
3965 
3966  if(score > maxScore)
3967  {
3968  maxScore = score;
3969  }
3970 
3971  if(score < minScore)
3972  {
3973  minScore = score;
3974  }
3975 
3976  } // end multi-node member scoring loop
3977 
3978  __COUTVS__(2, minScore);
3979  __COUTVS__(2, maxScore);
3980 
3981  __COUT__ << "Trimming multi-node members with low match score..."
3982  << __E__;
3983 
3984  // go backwards, to not mess up indices as deleted
3985  // do not delete index 0
3986  for(unsigned int i = multiNodeNames.size() - 1;
3987  i > 0 && i < multiNodeNames.size();
3988  --i)
3989  {
3990  //__COUTV__(scoreVector[i]);
3991  //__COUTV__(i);
3992  if(maxScore > multiNodeNames[0].size() &&
3993  scoreVector[i] >= maxScore)
3994  continue;
3995 
3996  // else trim
3997  __COUT__ << "Trimming low score match " << multiNodeNames[i]
3998  << " for node name " << nodeName << __E__;
3999 
4000  trimmedNodeNames.push_back(multiNodeNames[i]);
4001  skipSet.erase(multiNodeNames[i]);
4002  multiNodeNames.erase(multiNodeNames.begin() + i);
4003  hostnameArray.erase(hostnameArray.begin() + i);
4004 
4005  } // end multi-node trim loop
4006 
4007  } // done with multi-node member trim
4008 
4009  // Numeric-only wildcard refinement: when node names have multiple
4010  // separate varying digit groups (e.g. prefix 01-12 AND suffix 0-1
4011  // in names like "01DTC0", "01DTC1", "02DTC0", etc.), fix the digit
4012  // group with the fewest unique values so that the remaining wildcard
4013  // is numeric-only. This heavily favors numeric-only multinode arrays.
4014  if(multiNodeNames.size() > 2)
4015  {
4016  bool sameLengths = true;
4017  size_t nameLen = multiNodeNames[0].size();
4018  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
4019  if(multiNodeNames[i].size() != nameLen)
4020  {
4021  sameLengths = false;
4022  break;
4023  }
4024 
4025  if(sameLengths && nameLen > 0)
4026  {
4027  std::vector<bool> varies(nameLen, false);
4028  for(unsigned int pos = 0; pos < nameLen; ++pos)
4029  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
4030  if(multiNodeNames[i][pos] != multiNodeNames[0][pos])
4031  {
4032  varies[pos] = true;
4033  break;
4034  }
4035 
4036  struct VaryRun
4037  {
4038  unsigned int start, end;
4039  bool allDigits;
4040  };
4041  std::vector<VaryRun> runs;
4042  for(unsigned int pos = 0; pos < nameLen;)
4043  {
4044  if(!varies[pos])
4045  {
4046  ++pos;
4047  continue;
4048  }
4049  unsigned int rStart = pos;
4050  bool runAllDigit = true;
4051  while(pos < nameLen && varies[pos])
4052  {
4053  for(unsigned int i = 0;
4054  i < multiNodeNames.size() && runAllDigit;
4055  ++i)
4056  if(!(multiNodeNames[i][pos] >= '0' &&
4057  multiNodeNames[i][pos] <= '9'))
4058  runAllDigit = false;
4059  ++pos;
4060  }
4061  runs.push_back({rStart, pos, runAllDigit});
4062  }
4063 
4064  // Expand each all-digit run backwards to absorb preceding
4065  // non-varying digit characters that are part of the same
4066  // numeric token. E.g. "BRCalo13" vs "BRCalo14" has only
4067  // position 7 varying, but position 6 ('1') is a digit that
4068  // should be included so the wildcard covers "13"/"14" not
4069  // just "3"/"4".
4070  for(auto& run : runs)
4071  {
4072  if(!run.allDigits)
4073  continue;
4074  while(run.start > 0)
4075  {
4076  bool allDigitAtPrev = true;
4077  for(unsigned int i = 0;
4078  i < multiNodeNames.size() && allDigitAtPrev;
4079  ++i)
4080  if(!(multiNodeNames[i][run.start - 1] >= '0' &&
4081  multiNodeNames[i][run.start - 1] <= '9'))
4082  allDigitAtPrev = false;
4083  if(!allDigitAtPrev)
4084  break;
4085  run.start--;
4086  }
4087  }
4088 
4089  unsigned int numDigitRuns = 0;
4090  for(const auto& run : runs)
4091  if(run.allDigits)
4092  ++numDigitRuns;
4093 
4094  if(numDigitRuns > 1 && numDigitRuns == runs.size())
4095  {
4096  __COUTT__
4097  << "Numeric wildcard refinement: found "
4098  << numDigitRuns
4099  << " separate digit-varying groups in '" << nodeName
4100  << "'. Splitting to favor numeric-only wildcards."
4101  << __E__;
4102 
4103  // For each digit-varying run, compute:
4104  // - uniqueCount: number of distinct digit values
4105  // - hostnameCorrelation: how many distinct hostnames
4106  // correspond to distinct digit values (high = correlated
4107  // with hostname, meaning this group should be the wildcard,
4108  // NOT the one we fix)
4109  unsigned int bestRunToFix = 0;
4110  unsigned int fewestUnique = (unsigned int)-1;
4111  unsigned int lowestHostCorr = (unsigned int)-1;
4112  for(unsigned int r = 0; r < runs.size(); ++r)
4113  {
4114  std::set<std::string> uniqueVals;
4115  for(unsigned int i = 0; i < multiNodeNames.size();
4116  ++i)
4117  uniqueVals.insert(multiNodeNames[i].substr(
4118  runs[r].start, runs[r].end - runs[r].start));
4119 
4120  // Compute hostname correlation by mapping each digit-group
4121  // value to its set of hostnames.
4122  std::map<std::string, std::set<std::string>>
4123  valToHosts;
4124  for(unsigned int i = 0; i < multiNodeNames.size();
4125  ++i)
4126  {
4127  std::string dv = multiNodeNames[i].substr(
4128  runs[r].start, runs[r].end - runs[r].start);
4129  valToHosts[dv].insert(hostnameArray[i]);
4130  }
4131  // hostnameCorrelation = number of digit values that map to
4132  // a unique hostname (i.e. different digit value -> different host)
4133  // If all digit values share the same hostname, correlation = 0.
4134  unsigned int hostCorr = 0;
4135  if(valToHosts.size() > 1)
4136  {
4137  // Check if different digit values map to different hostnames
4138  std::set<std::string> allHostSets;
4139  for(const auto& [dv, hosts] : valToHosts)
4140  {
4141  std::string hostKey;
4142  for(const auto& h : hosts)
4143  hostKey += h + ",";
4144  allHostSets.insert(hostKey);
4145  }
4146  hostCorr = allHostSets.size();
4147  }
4148 
4149  __COUTT__ << "Digit run " << r << " ["
4150  << runs[r].start << "-" << (runs[r].end - 1)
4151  << "]: uniqueVals=" << uniqueVals.size()
4152  << " hostCorr=" << hostCorr << __E__;
4153 
4154  // Prefer to fix the group with fewest unique values,
4155  // and break ties by fixing the one with LOWEST hostname
4156  // correlation (i.e. keep the hostname-correlated group
4157  // as the wildcard).
4158  if(uniqueVals.size() < fewestUnique ||
4159  (uniqueVals.size() == fewestUnique &&
4160  hostCorr < lowestHostCorr))
4161  {
4162  fewestUnique = uniqueVals.size();
4163  lowestHostCorr = hostCorr;
4164  bestRunToFix = r;
4165  }
4166  }
4167 
4168  std::string keepVal = multiNodeNames[0].substr(
4169  runs[bestRunToFix].start,
4170  runs[bestRunToFix].end - runs[bestRunToFix].start);
4171 
4172  __COUTT__ << "Fixing digit group at positions "
4173  << runs[bestRunToFix].start << "-"
4174  << (runs[bestRunToFix].end - 1) << " to value '"
4175  << keepVal
4176  << "' (fewest unique=" << fewestUnique << ")"
4177  << __E__;
4178 
4179  for(unsigned int i = multiNodeNames.size() - 1;
4180  i > 0 && i < multiNodeNames.size();
4181  --i)
4182  {
4183  std::string val = multiNodeNames[i].substr(
4184  runs[bestRunToFix].start,
4185  runs[bestRunToFix].end -
4186  runs[bestRunToFix].start);
4187  if(val != keepVal)
4188  {
4189  __COUTT__ << "Numeric refinement trim: "
4190  << multiNodeNames[i]
4191  << " (digit group '" << val << "' != '"
4192  << keepVal << "')" << __E__;
4193  trimmedNodeNames.push_back(multiNodeNames[i]);
4194  skipSet.erase(multiNodeNames[i]);
4195  multiNodeNames.erase(multiNodeNames.begin() + i);
4196  hostnameArray.erase(hostnameArray.begin() + i);
4197  }
4198  }
4199  __COUT__ << "After numeric refinement: "
4200  << multiNodeNames.size() << " nodes remain for '"
4201  << nodeName << "'." << __E__;
4202  }
4203  }
4204  } // end numeric-only wildcard refinement
4205 
4206  // Collision check: verify that the computed nodeName pattern
4207  // does not also match trimmed nodes or existing map entries.
4208  // If it does, re-score with full character matching and trim
4209  // again until the pattern only matches the current node set.
4210  // If trimming can't resolve the collision, abandon multi-node
4211  // grouping so remaining members get processed individually.
4212  if(multiNodeNames.size() > 1)
4213  {
4214  // Lambda to check if a name matches the commonChunks pattern
4215  // (i.e. all non-empty chunks appear in order, first chunk at position 0).
4216  // When the multinode wildcards are all purely numeric, require
4217  // that the gap portions of the candidate name are also purely
4218  // numeric so that e.g. "DL0CRV" does not falsely collide with
4219  // the "DL*" pattern produced by numeric-only members DL1, DL2, DL3.
4220  auto matchesCommonChunksPattern =
4221  [](const std::string& name,
4222  const std::vector<std::string>& chunks,
4223  const std::vector<std::string>& wildcards) -> bool {
4224  // Check if all multinode wildcards are purely numeric
4225  bool wildcardsAllNumeric = !wildcards.empty();
4226  for(const auto& w : wildcards)
4227  {
4228  if(w.empty() ||
4229  w.find_first_not_of("0123456789") != std::string::npos)
4230  {
4231  wildcardsAllNumeric = false;
4232  break;
4233  }
4234  }
4235 
4236  size_t pos = 0;
4237  for(unsigned int c = 0; c < chunks.size(); ++c)
4238  {
4239  if(chunks[c].empty())
4240  continue;
4241  size_t found;
4242  if(c == 0)
4243  found = (name.size() >= chunks[c].size() &&
4244  name.compare(
4245  0, chunks[c].size(), chunks[c]) == 0)
4246  ? 0
4247  : std::string::npos;
4248  else
4249  {
4250  found = name.find(chunks[c], pos);
4251  // If wildcards are all numeric, verify the gap
4252  // between chunks is also purely numeric
4253  if(wildcardsAllNumeric && found != std::string::npos)
4254  {
4255  std::string gap = name.substr(pos, found - pos);
4256  if(gap.empty() ||
4257  gap.find_first_not_of("0123456789") !=
4258  std::string::npos)
4259  return false;
4260  }
4261  }
4262  if(found == std::string::npos)
4263  return false;
4264  pos = found + chunks[c].size();
4265  }
4266  // Check trailing gap after the last non-empty chunk
4267  if(wildcardsAllNumeric && pos < name.size())
4268  {
4269  std::string trailingGap = name.substr(pos);
4270  if(trailingGap.find_first_not_of("0123456789") !=
4271  std::string::npos)
4272  return false;
4273  }
4274  return true;
4275  };
4276 
4277  bool collisionRetry = true;
4278  while(collisionRetry && multiNodeNames.size() > 1)
4279  {
4280  collisionRetry = false;
4281 
4282  // Trial extraction to get the current commonChunks pattern
4283  std::vector<std::string> trialCommonChunks;
4284  std::vector<std::string> trialWildcards;
4285  unsigned int trialFixedLen = 0;
4286  StringMacros::extractCommonChunks(multiNodeNames,
4287  trialCommonChunks,
4288  trialWildcards,
4289  trialFixedLen);
4290 
4291  __COUT__ << "Collision check: trialCommonChunks = "
4292  << StringMacros::vectorToString(trialCommonChunks)
4293  << __E__;
4294 
4295  // Check 1: trimmed nodes collision
4296  bool collisionFound = false;
4297  for(const auto& trimmedNode : trimmedNodeNames)
4298  {
4299  if(matchesCommonChunksPattern(
4300  trimmedNode, trialCommonChunks, trialWildcards))
4301  {
4302  __COUT__ << "Collision detected: trimmed node '"
4303  << trimmedNode
4304  << "' matches base pattern from commonChunks"
4305  << __E__;
4306  collisionFound = true;
4307  break;
4308  }
4309  }
4310 
4311  // Check 2: existing map entries collision
4312  // Skip entries with a different status since on/off nodes
4313  // are displayed in separate sections and can never collide.
4314  if(!collisionFound)
4315  {
4316  std::string currentStatusStr = status ? "1" : "0";
4317  for(const auto& existingEntry :
4318  nodeTypeToObjectMap.at(typeString))
4319  {
4320  std::string existingBaseName = existingEntry.first;
4321  size_t statusPos = existingBaseName.find(";status=");
4322  if(statusPos != std::string::npos)
4323  {
4324  // Skip entries with different status
4325  if(existingBaseName.substr(
4326  statusPos +
4327  std::string(";status=").size()) !=
4328  currentStatusStr)
4329  continue;
4330  existingBaseName =
4331  existingBaseName.substr(0, statusPos);
4332  }
4333 
4334  if(matchesCommonChunksPattern(existingBaseName,
4335  trialCommonChunks,
4336  trialWildcards))
4337  {
4338  // Extract the gap value from the existing entry
4339  // name and check if it is actually one of the
4340  // current group's wildcard values. If it is not,
4341  // the explicit wildcard list in the multiNodeString
4342  // already disambiguates, so this is not a real
4343  // collision.
4344  std::string existingGap;
4345  if(trialCommonChunks.size() == 1 &&
4346  existingBaseName.size() >
4347  trialCommonChunks[0].size())
4348  existingGap = existingBaseName.substr(
4349  trialCommonChunks[0].size());
4350  else if(trialCommonChunks.size() >= 2)
4351  {
4352  size_t suffixLen = 0;
4353  for(size_t ci = 1;
4354  ci < trialCommonChunks.size();
4355  ++ci)
4356  suffixLen += trialCommonChunks[ci].size();
4357  if(existingBaseName.size() >
4358  trialCommonChunks[0].size() + suffixLen)
4359  existingGap = existingBaseName.substr(
4360  trialCommonChunks[0].size(),
4361  existingBaseName.size() -
4362  trialCommonChunks[0].size() -
4363  suffixLen);
4364  }
4365 
4366  if(!existingGap.empty())
4367  {
4368  bool gapInWildcards = false;
4369  for(const auto& w : trialWildcards)
4370  if(w == existingGap)
4371  {
4372  gapInWildcards = true;
4373  break;
4374  }
4375  if(!gapInWildcards)
4376  {
4377  __COUT__
4378  << "Existing entry '"
4379  << existingEntry.first
4380  << "' matches pattern but gap '"
4381  << existingGap
4382  << "' is not in multinode wildcards"
4383  " - not a collision"
4384  << __E__;
4385  continue;
4386  }
4387  }
4388 
4389  __COUT__
4390  << "Collision detected: existing map entry '"
4391  << existingEntry.first
4392  << "' matches base pattern from commonChunks"
4393  << __E__;
4394  collisionFound = true;
4395  break;
4396  }
4397  }
4398  }
4399 
4400  if(!collisionFound)
4401  break; // no collision, we're done
4402 
4403  // Collision found! Re-score with full character matching
4404  __COUT__ << "Re-scoring remaining multi-node members with "
4405  "full character matching to resolve collision..."
4406  << __E__;
4407 
4408  unsigned int fullMaxScore = 0;
4409  std::vector<unsigned int> fullScoreVector;
4410  fullScoreVector.push_back(-1); // index 0 is perfect (self)
4411 
4412  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
4413  {
4414  unsigned int fscore = 0;
4415  // Simple forward character-by-character matching
4416  for(unsigned int j = 0; j < multiNodeNames[0].size() &&
4417  j < multiNodeNames[i].size();
4418  ++j)
4419  {
4420  if(multiNodeNames[0][j] == multiNodeNames[i][j])
4421  ++fscore;
4422  else
4423  break;
4424  }
4425  fullScoreVector.push_back(fscore);
4426  if(fscore > fullMaxScore)
4427  fullMaxScore = fscore;
4428  }
4429 
4430  __COUT__ << "Full char rescore: maxScore = " << fullMaxScore
4431  << __E__;
4432 
4433  // Trim nodes below max score
4434  bool anyTrimmed = false;
4435  for(unsigned int i = multiNodeNames.size() - 1;
4436  i > 0 && i < multiNodeNames.size();
4437  --i)
4438  {
4439  if(fullScoreVector[i] >= fullMaxScore)
4440  continue;
4441 
4442  __COUT__ << "Collision trim: removing "
4443  << multiNodeNames[i] << " (score "
4444  << fullScoreVector[i] << " < " << fullMaxScore
4445  << ") for node name " << nodeName << __E__;
4446 
4447  trimmedNodeNames.push_back(multiNodeNames[i]);
4448  skipSet.erase(multiNodeNames[i]);
4449  multiNodeNames.erase(multiNodeNames.begin() + i);
4450  hostnameArray.erase(hostnameArray.begin() + i);
4451  anyTrimmed = true;
4452  collisionRetry = true;
4453  }
4454 
4455  // If no trimming was possible but still colliding,
4456  // abandon multi-node grouping - remaining members
4457  // will be processed individually by the main loop
4458  if(!anyTrimmed)
4459  {
4460  __COUT__ << "Cannot narrow multi-node group further to "
4461  "resolve collision. Abandoning multi-node "
4462  "grouping for '"
4463  << nodeName
4464  << "' - remaining members will be "
4465  "processed individually."
4466  << __E__;
4467 
4468  // Remove all members except [0] from skipSet
4469  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
4470  skipSet.erase(multiNodeNames[i]);
4471  multiNodeNames.resize(1);
4472  hostnameArray.resize(1);
4473  break;
4474  }
4475  } // end collision resolution loop
4476 
4477  __COUT__ << "After collision resolution:" << __E__;
4478  __COUTV__(nodeName);
4479  __COUTV__(StringMacros::vectorToString(multiNodeNames));
4480  __COUTV__(StringMacros::vectorToString(hostnameArray));
4481  __COUTV__(StringMacros::setToString(skipSet));
4482  } // end collision check
4483 
4484  __COUTV__(nodeName);
4485  __COUTV__(StringMacros::vectorToString(multiNodeNames));
4486  __COUTV__(StringMacros::vectorToString(hostnameArray));
4487  __COUTV__(StringMacros::setToString(skipSet));
4488 
4489  //set of names fully defined, reorder alphabettically
4490  {
4491  __COUT__ << "Reorganizing multinode '" << nodeName
4492  << "' alphabetically..." << __E__;
4493  std::set<
4494  std::pair<std::string /* node */, std::string /* host */>>
4495  reorderSet;
4496  for(unsigned int i = 0; i < multiNodeNames.size(); ++i)
4497  reorderSet.emplace(
4498  std::make_pair(multiNodeNames[i], hostnameArray[i]));
4499 
4500  __COUTV__(StringMacros::setToString(reorderSet));
4501  //skipset is unchanged, multiNodeNames and hostnameArray are reordered
4502 
4503  multiNodeNames.clear();
4504  hostnameArray.clear();
4505  for(const auto& orderedPair : reorderSet)
4506  {
4507  multiNodeNames.push_back(orderedPair.first);
4508  hostnameArray.push_back(orderedPair.second);
4509  }
4510 
4511  } //end reorder alphabetically
4512  __COUTV__(nodeName);
4513  __COUTV__(StringMacros::vectorToString(multiNodeNames));
4514  __COUTV__(StringMacros::vectorToString(hostnameArray));
4515  __COUTV__(StringMacros::setToString(skipSet));
4516 
4517  auto expandNumericWildcards =
4518  [](std::vector<std::string>& commonChunks,
4519  std::vector<std::string>& wildcards,
4520  const std::string& logPrefix) {
4521  bool allDigitWC = true;
4522  for(const auto& wc : wildcards)
4523  {
4524  if(wc.empty() || wc.find_first_not_of("0123456789") !=
4525  std::string::npos)
4526  {
4527  allDigitWC = false;
4528  break;
4529  }
4530  }
4531  if(allDigitWC && !commonChunks.empty() &&
4532  !commonChunks[0].empty())
4533  {
4534  size_t trailingDigits = 0;
4535  for(int ci = (int)commonChunks[0].size() - 1; ci >= 0;
4536  --ci)
4537  {
4538  if(commonChunks[0][ci] >= '0' &&
4539  commonChunks[0][ci] <= '9')
4540  ++trailingDigits;
4541  else
4542  break;
4543  }
4544  if(trailingDigits > 0 &&
4545  trailingDigits < commonChunks[0].size())
4546  {
4547  std::string prefix = commonChunks[0].substr(
4548  commonChunks[0].size() - trailingDigits);
4549  commonChunks[0] = commonChunks[0].substr(
4550  0, commonChunks[0].size() - trailingDigits);
4551  for(auto& wc : wildcards)
4552  wc = prefix + wc;
4553 
4554  __COUT__
4555  << logPrefix << "moved '" << prefix
4556  << "' from commonChunk prefix into wildcards."
4557  << __E__;
4558  }
4559  }
4560  };
4561 
4562  // from set of nodename wildcards, make printer syntax
4563  if(multiNodeNames.size() > 1)
4564  {
4565  std::vector<std::string> commonChunks;
4566  std::vector<std::string> wildcards;
4567 
4568  //can not change the order of wildcards for node names! or the names will not keep pairing with host
4569 
4570  bool wildcardsNeeded =
4571  StringMacros::extractCommonChunks(multiNodeNames,
4572  commonChunks,
4573  wildcards,
4574  nodeFixedWildcardLength);
4575 
4576  if(!wildcardsNeeded || wildcards.size() != multiNodeNames.size())
4577  {
4578  __SS__
4579  << "Impossible extractCommonChunks result! Please notify "
4580  "admins or try to simplify record naming convention."
4581  << __E__;
4582  __SS_THROW__;
4583  }
4584 
4585  // Expand numeric wildcards: when all wildcards are
4586  // purely numeric and the preceding common chunk ends
4587  // with digits, move those trailing digits from the
4588  // chunk into the wildcard values so the pattern
4589  // boundary falls on a letter/digit transition.
4590  // e.g. commonChunks=["BRCalo1","DTC1"] wildcards=["3","4"]
4591  // => commonChunks=["BRCalo","DTC1"] wildcards=["13","14"]
4592  expandNumericWildcards(
4593  commonChunks, wildcards, "Expanded numeric wildcards: ");
4594  __COUTV__(StringMacros::vectorToString(commonChunks));
4595  __COUTV__(StringMacros::vectorToString(wildcards));
4596 
4597  nodeName = "";
4598  bool first = true;
4599  for(auto& commonChunk : commonChunks)
4600  {
4601  nodeName += (!first ? "*" : "") + commonChunk;
4602  if(first)
4603  first = false;
4604  }
4605  if(commonChunks.size() == 1)
4606  nodeName += '*';
4607 
4608  __COUTV__(nodeName);
4609 
4610  // steps:
4611  // determine if all unsigned ints
4612  // if int, then order and attempt to hyphenate
4613  // if not ints, then comma separated
4614 
4615  bool allIntegers = true;
4616  for(auto& wildcard : wildcards)
4617  if(!allIntegers)
4618  break;
4619  else if(wildcard.size() == 0) // emtpy string is not a number
4620  {
4621  allIntegers = false;
4622  break;
4623  }
4624  else
4625  for(unsigned int i = 0; i < wildcard.size(); ++i)
4626  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
4627  {
4628  allIntegers = false;
4629  break;
4630  }
4631 
4632  __COUTV__(allIntegers);
4633  if(allIntegers)
4634  {
4635  __COUTV__(StringMacros::vectorToString(wildcards));
4636 
4637  // need ints in vector for random access to for hyphenating
4638  std::vector<unsigned int> intWildcards;
4639  for(auto& wildcard : wildcards)
4640  intWildcards.push_back(strtol(wildcard.c_str(), 0, 10));
4641 
4642  __COUTV__(StringMacros::vectorToString(intWildcards));
4643 
4644  unsigned int hyphenLo = -1;
4645  bool isFirst = true;
4646  for(unsigned int i = 0; i < intWildcards.size(); ++i)
4647  {
4648  if(i + 1 < intWildcards.size() &&
4649  intWildcards[i] + 1 == intWildcards[i + 1])
4650  {
4651  if(i < hyphenLo)
4652  hyphenLo = i; // start hyphen
4653  //else continue hyphen
4654  }
4655  else // new comma
4656  {
4657  if(i < hyphenLo)
4658  {
4659  // single number
4660  multiNodeString +=
4661  (isFirst ? "" : ",") +
4662  std::to_string(intWildcards[i]);
4663  }
4664  else
4665  {
4666  // if only 1 number apart, then comma
4667  if(intWildcards[hyphenLo] + 1 == intWildcards[i])
4668  multiNodeString +=
4669  (isFirst ? "" : ",") +
4670  std::to_string(intWildcards[hyphenLo]) +
4671  "," + std::to_string(intWildcards[i]);
4672  else // else hyphen numbers
4673  multiNodeString +=
4674  (isFirst ? "" : ",") +
4675  std::to_string(intWildcards[hyphenLo]) +
4676  "-" + std::to_string(intWildcards[i]);
4677  hyphenLo = -1; // reset for next
4678  }
4679  isFirst = false;
4680  }
4681  }
4682  } // end all integer handling
4683  else // not all integers, so csv
4684  {
4685  multiNodeString = StringMacros::vectorToString(wildcards);
4686  nodeFixedWildcardLength =
4687  0; //wipe out fixed length rule if not all numbers
4688  } // end not-all integer handling
4689 
4690  __COUTV__(multiNodeString);
4691  __COUTV__(nodeFixedWildcardLength);
4692  } // end node name printer syntax handling
4693 
4694  if(hostnameArray.size() > 1)
4695  {
4696  std::vector<std::string> commonChunks;
4697  std::vector<std::string> wildcards;
4698 
4699  //can not change the order of wildcards for hostname! or the names will not keep pairing with host
4700 
4701  bool wildcardsNeeded =
4702  StringMacros::extractCommonChunks(hostnameArray,
4703  commonChunks,
4704  wildcards,
4705  hostFixedWildcardLength);
4706 
4707  __COUTV__(wildcardsNeeded);
4708  __COUTV__(StringMacros::vectorToString(commonChunks));
4709  __COUTV__(StringMacros::vectorToString(wildcards));
4710 
4711  // Expand numeric wildcards for hostname: when all
4712  // wildcards are purely numeric and the preceding
4713  // common chunk ends with digits, move those trailing
4714  // digits from the chunk into the wildcard values so
4715  // the pattern boundary falls on a letter/digit
4716  // transition.
4717  // e.g. commonChunks=["mu2e%2Dcalo%2D1","%2Ddata..."] wildcards=["3","4"]
4718  // => commonChunks=["mu2e%2Dcalo%2D","%2Ddata..."] wildcards=["13","14"]
4719  expandNumericWildcards(
4720  commonChunks,
4721  wildcards,
4722  "Expanded numeric wildcards for hostname: ");
4723  __COUTV__(StringMacros::vectorToString(commonChunks));
4724  __COUTV__(StringMacros::vectorToString(wildcards));
4725 
4726  hostname = "";
4727  bool first = true;
4728  for(auto& commonChunk : commonChunks)
4729  {
4730  hostname += (!first ? "*" : "") + commonChunk;
4731  if(first)
4732  first = false;
4733  }
4734  if(wildcardsNeeded && commonChunks.size() == 1)
4735  hostname += '*';
4736 
4737  __COUTV__(hostname);
4738 
4739  if(wildcardsNeeded)
4740  // else if not wildcards needed, then do not make hostname array string
4741  {
4742  // steps:
4743  // determine if all unsigned ints
4744  // if int, then order and attempt to hyphenate
4745  // if not ints, then comma separated
4746 
4747  bool allIntegers = true;
4748  for(auto& wildcard : wildcards)
4749  for(unsigned int i = 0; i < wildcard.size(); ++i)
4750  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
4751  {
4752  allIntegers = false;
4753  break;
4754  }
4755 
4756  __COUTV__(allIntegers);
4757 
4758  if(allIntegers)
4759  {
4760  __COUTV__(StringMacros::vectorToString(wildcards));
4761 
4762  // need ints in vector for random access to for hyphenating
4763  std::vector<unsigned int> intWildcards;
4764  for(auto& wildcard : wildcards)
4765  intWildcards.push_back(
4766  strtol(wildcard.c_str(), 0, 10));
4767 
4768  __COUTV__(StringMacros::vectorToString(intWildcards));
4769 
4770  unsigned int hyphenLo = -1;
4771  bool isFirst = true;
4772  for(unsigned int i = 0; i < intWildcards.size(); ++i)
4773  {
4774  if(i + 1 < intWildcards.size() &&
4775  intWildcards[i] + 1 == intWildcards[i + 1])
4776  {
4777  if(i < hyphenLo)
4778  hyphenLo = i; // start hyphen
4779  //else continue hyphen
4780  }
4781  else // new comma
4782  {
4783  if(i < hyphenLo)
4784  {
4785  // single number
4786  hostArrayString +=
4787  (isFirst ? "" : ",") +
4788  std::to_string(intWildcards[i]);
4789  }
4790  else
4791  {
4792  // if only 1 number apart, then comma
4793  if(intWildcards[hyphenLo] + 1 ==
4794  intWildcards[i])
4795  hostArrayString +=
4796  (isFirst ? "" : ",") +
4797  std::to_string(
4798  intWildcards[hyphenLo]) +
4799  "," + std::to_string(intWildcards[i]);
4800  else // else hyphen numbers
4801  hostArrayString +=
4802  (isFirst ? "" : ",") +
4803  std::to_string(
4804  intWildcards[hyphenLo]) +
4805  "-" + std::to_string(intWildcards[i]);
4806  hyphenLo = -1; // reset for next
4807  }
4808  isFirst = false;
4809  }
4810  }
4811  } // end all integer handling
4812  else // not all integers, so csv
4813  {
4814  hostArrayString = StringMacros::vectorToString(wildcards);
4815  hostFixedWildcardLength =
4816  0; //wipe out fixed length rule if not all numbers
4817  } // end not-all integer handling
4818  } // end wildcard need handling
4819  __COUTV__(hostArrayString);
4820  __COUTV__(hostFixedWildcardLength);
4821  } // end node name printer syntax handling
4822 
4823  } // end multi node printer syntax handling
4824 
4825  nodeName +=
4826  ";status=" +
4827  std::string(status
4828  ? "1"
4829  : "0"); //include status in name to avoid collissions
4830  auto result = nodeTypeToObjectMap.at(typeString)
4831  .emplace(std::make_pair(
4832  nodeName, std::vector<std::string /*property*/>()));
4833 
4834  if(TTEST(0))
4835  {
4836  __SS__ << "Here is the current nodeTypeToObjectMap:" << __E__;
4837  for(const auto& typePair : nodeTypeToObjectMap)
4838  {
4839  ss << "\tType: " << typePair.first << __E__;
4840  for(const auto& nodePair : typePair.second)
4841  {
4842  ss << "\t\tNode: " << nodePair.first << __E__;
4843  for(const auto& property : nodePair.second)
4844  ss << "\t\t\tProperty: " << property << __E__;
4845  }
4846  }
4847  __COUT__ << ss.str() << __E__;
4848  }
4849 
4850  if(!result.second)
4851  {
4852  __COUT__
4853  << "Collision detected for node '" << nodeName << "' of type '"
4854  << typeString
4855  << "' when inserting into nodeTypeToObjectMap. This likely means "
4856  "that two nodes have the same name and status, and if so, "
4857  "they would be indistinguishable in printer syntax. "
4858  << "Please notify admins or try to simplify record naming "
4859  "convention."
4860  << __E__;
4861 
4862  __SS__ << "Impossible printer syntax handling result! Collision of "
4863  "base names. Please notify "
4864  "admins or try to simplify record naming convention."
4865  << __E__;
4866  __SS_THROW__;
4867  }
4868 
4869  nodeTypeToObjectMap.at(typeString)
4870  .at(nodeName)
4871  .push_back(status ? "1" : "0");
4872 
4873  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(hostname);
4874 
4875  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(subsystemId);
4876  if(multiNodeNames.size() > 1)
4877  {
4878  nodeTypeToObjectMap.at(typeString)
4879  .at(nodeName)
4880  .push_back(multiNodeString);
4881 
4882  nodeTypeToObjectMap.at(typeString)
4883  .at(nodeName)
4884  .push_back(std::to_string(nodeFixedWildcardLength));
4885 
4886  if(hostnameArray.size() > 1)
4887  {
4888  nodeTypeToObjectMap.at(typeString)
4889  .at(nodeName)
4890  .push_back(hostArrayString);
4891 
4892  nodeTypeToObjectMap.at(typeString)
4893  .at(nodeName)
4894  .push_back(std::to_string(hostFixedWildcardLength));
4895  }
4896  } // done adding multinode parameters
4897 
4898  __COUTV__(multiNodeString);
4899  __COUTV__(StringMacros::decodeURIComponent(hostname));
4900  __COUTV__(hostArrayString);
4901  __COUT__ << "Done with extraction of node '" << nodeName << "'" << __E__;
4902  } //end main node extraction loop
4903  } // end processor type handling
4904 
4905  } // end artdaq app loop
4906 
4907  __COUT__ << "Done getting artdaq nodes." << __E__;
4908 
4909  return ARTDAQTableBase::info_;
4910 } // end getARTDAQSystem()
4911 
4912 //==============================================================================
4922  ConfigurationManagerRW* cfgMgr,
4923  const std::map<std::string /*type*/,
4924  std::map<std::string /*record*/,
4925  std::vector<std::string /*property*/>>>& nodeTypeToObjectMap,
4926  const std::map<std::string /*subsystemName*/,
4927  std::string /*destinationSubsystemName*/>& subsystemObjectMap)
4928 {
4929  __COUT__ << "setAndActivateARTDAQSystem()" << __E__;
4930 
4931  const std::string& author = cfgMgr->getUsername();
4932 
4933  // Steps:
4934  // 0. Check for one and only artdaq Supervisor
4935  // 1. create/verify subsystems and destinations
4936  // 2. for each node
4937  // create/verify records
4938 
4939  //------------------------
4940  // 0. Check for one and only artdaq Supervisor
4941 
4942  GroupEditStruct configGroupEdit(ConfigurationManager::GroupType::CONFIGURATION_TYPE,
4943  cfgMgr);
4944 
4945  unsigned int artdaqSupervisorRow = TableView::INVALID;
4946 
4947  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
4948 
4949  const XDAQContextTable::XDAQContext* artdaqContext =
4950  contextTable->getTheARTDAQSupervisorContext();
4951 
4952  bool needArtdaqSupervisorParents = true;
4953  bool needArtdaqSupervisorCreation = false;
4954 
4955  __COUTV__(artdaqContext);
4956  if(artdaqContext) // check for full connection to supervisor
4957  {
4958  try
4959  {
4960  const std::string& activeContextGroupName =
4961  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE);
4962  const TableGroupKey& activeContextGroupKey =
4963  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE);
4964  const std::string& activeConfigGroupName = cfgMgr->getActiveGroupName(
4965  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
4966  const TableGroupKey& activeConfigGroupKey = cfgMgr->getActiveGroupKey(
4967  ConfigurationManager::GroupType::CONFIGURATION_TYPE);
4968 
4969  __COUTV__(activeContextGroupName);
4970  __COUTV__(activeContextGroupKey);
4971  __COUTV__(activeConfigGroupName);
4972  __COUTV__(activeConfigGroupKey);
4973  __COUTV__(cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4974  .getNode(artdaqContext->contextUID_)
4975  .getValueAsString());
4976  __COUTV__(
4977  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4978  .getNode(artdaqContext->contextUID_)
4979  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4980  .getValueAsString());
4981  __COUTV__(
4982  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4983  .getNode(artdaqContext->contextUID_)
4984  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4985  .getNode(artdaqContext->applications_[0].applicationUID_)
4986  .getValueAsString());
4987  __COUTV__(artdaqContext->applications_[0].applicationUID_);
4988  __COUTV__(XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
4989  __COUTV__(
4990  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4991  .getNode(artdaqContext->contextUID_)
4992  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
4993  .getNode(artdaqContext->applications_[0].applicationUID_)
4994  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_)
4995  .getValueAsString());
4996 
4997  ConfigurationTree artdaqSupervisorNode =
4998  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
4999  .getNode(artdaqContext->contextUID_)
5000  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
5001  .getNode(artdaqContext->applications_[0].applicationUID_)
5002  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
5003 
5004  __COUTV__(artdaqSupervisorNode.isDisconnected());
5005 
5006  if(artdaqSupervisorNode.isDisconnected())
5007  needArtdaqSupervisorCreation = true;
5008  else
5009  artdaqSupervisorRow = artdaqSupervisorNode.getRow();
5010 
5011  needArtdaqSupervisorParents = false;
5012  }
5013  catch(...) // parents are a problem if error
5014  {
5015  needArtdaqSupervisorCreation = true;
5016  }
5017  __COUTV__(needArtdaqSupervisorCreation);
5018  }
5019 
5020  if(!artdaqContext || needArtdaqSupervisorCreation)
5021  {
5022  __COUT__ << "No artdaq Supervisor found! Creating..." << __E__;
5023  __COUTV__(needArtdaqSupervisorParents);
5024 
5025  std::string artdaqSupervisorUID;
5026  unsigned int row;
5027 
5028  // create record in ARTDAQ Supervisor table
5029  // connect to an App in a Context
5030 
5031  // now create artdaq Supervisor in configuration group
5032  {
5033  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(
5034  ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
5035 
5036  if(TTEST(0))
5037  {
5038  std::stringstream ss;
5039  artdaqSupervisorTable.tableView_->print(ss);
5040  __COUT_MULTI__(0, ss.str());
5041  }
5042 
5043  // create artdaq Supervisor context record
5044  row = artdaqSupervisorTable.tableView_->addRow(
5045  author, true /*incrementUniqueData*/, "artdaqSupervisor");
5046 
5047  // get UID
5048  artdaqSupervisorUID =
5049  artdaqSupervisorTable.tableView_
5050  ->getDataView()[row][artdaqSupervisorTable.tableView_->getColUID()];
5051  artdaqSupervisorRow = row;
5052 
5053  __COUTV__(artdaqSupervisorRow);
5054  __COUTV__(artdaqSupervisorUID);
5055 
5056  // set DAQInterfaceDebugLevel
5057  artdaqSupervisorTable.tableView_->setValueAsString(
5058  "1",
5059  row,
5060  artdaqSupervisorTable.tableView_->findCol(
5061  colARTDAQSupervisor_.colDAQInterfaceDebugLevel_));
5062  // set DAQSetupScript
5063  artdaqSupervisorTable.tableView_->setValueAsString(
5064  "${MRB_BUILDDIR}/../setup_ots.sh",
5065  row,
5066  artdaqSupervisorTable.tableView_->findCol(
5067  colARTDAQSupervisor_.colDAQSetupScript_));
5068 
5069  // create group link to board readers
5070  artdaqSupervisorTable.tableView_->setValueAsString(
5071  ARTDAQ_READER_TABLE,
5072  row,
5073  artdaqSupervisorTable.tableView_->findCol(
5074  colARTDAQSupervisor_.colLinkToBoardReaders_));
5075  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5076 
5077  row,
5078  artdaqSupervisorTable.tableView_->findCol(
5079  colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
5080  artdaqSupervisorUID +
5081  processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
5082  // create group link to event builders
5083  artdaqSupervisorTable.tableView_->setValueAsString(
5084  ARTDAQ_BUILDER_TABLE,
5085  row,
5086  artdaqSupervisorTable.tableView_->findCol(
5087  colARTDAQSupervisor_.colLinkToEventBuilders_));
5088  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5089  row,
5090  artdaqSupervisorTable.tableView_->findCol(
5091  colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
5092  artdaqSupervisorUID +
5093  processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
5094  // create group link to data loggers
5095  artdaqSupervisorTable.tableView_->setValueAsString(
5096  ARTDAQ_LOGGER_TABLE,
5097  row,
5098  artdaqSupervisorTable.tableView_->findCol(
5099  colARTDAQSupervisor_.colLinkToDataLoggers_));
5100  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5101  row,
5102  artdaqSupervisorTable.tableView_->findCol(
5103  colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
5104  artdaqSupervisorUID +
5105  processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
5106  // create group link to dispatchers
5107  artdaqSupervisorTable.tableView_->setValueAsString(
5108  ARTDAQ_DISPATCHER_TABLE,
5109  row,
5110  artdaqSupervisorTable.tableView_->findCol(
5111  colARTDAQSupervisor_.colLinkToDispatchers_));
5112  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5113  row,
5114  artdaqSupervisorTable.tableView_->findCol(
5115  colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
5116  artdaqSupervisorUID +
5117  processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
5118 
5119  // create group link to routing managers
5120  artdaqSupervisorTable.tableView_->setValueAsString(
5121  ARTDAQ_ROUTER_TABLE,
5122  row,
5123  artdaqSupervisorTable.tableView_->findCol(
5124  colARTDAQSupervisor_.colLinkToRoutingManagers_));
5125  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5126  row,
5127  artdaqSupervisorTable.tableView_->findCol(
5128  colARTDAQSupervisor_.colLinkToRoutingManagersGroupID_),
5129  artdaqSupervisorUID +
5130  processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
5131 
5132  if(TTEST(0))
5133  {
5134  std::stringstream ss;
5135  artdaqSupervisorTable.tableView_->print(ss);
5136  __COUT_MULTI__(0, ss.str());
5137  }
5138  } // end create artdaq Supervisor in configuration group
5139 
5140  // now create artdaq Supervisor parents in context group
5141  {
5142  GroupEditStruct contextGroupEdit(
5143  ConfigurationManager::GroupType::CONTEXT_TYPE, cfgMgr);
5144 
5145  TableEditStruct& contextTable = contextGroupEdit.getTableEditStruct(
5146  ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME, true /*markModified*/);
5147  TableEditStruct& appTable = contextGroupEdit.getTableEditStruct(
5148  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME, true /*markModified*/);
5149  TableEditStruct& appPropertyTable = contextGroupEdit.getTableEditStruct(
5150  ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME,
5151  true /*markModified*/);
5152 
5153  // open try for decorating errors and for clean code scope
5154  std::string appUID;
5155  try
5156  {
5157  std::string contextUID;
5158  std::string contextAppGroupID;
5159 
5160  if(needArtdaqSupervisorParents)
5161  {
5162  // create artdaq Supervisor context record
5163  row = contextTable.tableView_->addRow(
5164  author, true /*incrementUniqueData*/, "artdaqContext");
5165  // set context status true
5166  contextTable.tableView_->setValueAsString(
5167  "1", row, contextTable.tableView_->getColStatus());
5168 
5169  contextUID =
5170  contextTable.tableView_
5171  ->getDataView()[row][contextTable.tableView_->getColUID()];
5172 
5173  __COUTV__(row);
5174  __COUTV__(contextUID);
5175 
5176  // set address/port
5177  contextTable.tableView_->setValueAsString(
5178  "http://${HOSTNAME}",
5179  row,
5180  contextTable.tableView_->findCol(
5181  XDAQContextTable::colContext_.colAddress_));
5182  contextTable.tableView_->setUniqueColumnValue(
5183  row,
5184  contextTable.tableView_->findCol(
5185  XDAQContextTable::colContext_.colPort_),
5186  "${OTS_MAIN_PORT}",
5187  true /*doMathAppendStrategy*/);
5188 
5189  // create group link to artdaq Supervisor app
5190  contextTable.tableView_->setValueAsString(
5191  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME,
5192  row,
5193  contextTable.tableView_->findCol(
5194  XDAQContextTable::colContext_.colLinkToApplicationTable_));
5195  contextAppGroupID = contextTable.tableView_->setUniqueColumnValue(
5196  row,
5197  contextTable.tableView_->findCol(
5198  XDAQContextTable::colContext_.colLinkToApplicationGroupID_),
5199  "artdaqContextApps");
5200 
5201  __COUTV__(contextAppGroupID);
5202 
5203  } // end create context entry
5204 
5205  std::string appPropertiesGroupID;
5206 
5207  // create artdaq Supervisor app
5208  {
5209  unsigned int row;
5210 
5211  if(needArtdaqSupervisorParents)
5212  {
5213  // first disable any existing artdaq supervisor apps
5214  {
5215  unsigned int c = appTable.tableView_->findCol(
5216  XDAQContextTable::colApplication_.colClass_);
5217  for(unsigned int r = 0;
5218  r < appTable.tableView_->getNumberOfRows();
5219  ++r)
5220  if(appTable.tableView_->getDataView()[r][c] ==
5221  ARTDAQ_SUPERVISOR_CLASS)
5222  {
5223  __COUT_WARN__
5224  << "Found partially existing artdaq Supervisor "
5225  "application '"
5226  << appTable.tableView_->getDataView()
5227  [r][appTable.tableView_->getColUID()]
5228  << "'... Disabling it." << __E__;
5229  appTable.tableView_->setValueAsString(
5230  "0", r, appTable.tableView_->getColStatus());
5231  }
5232  }
5233 
5234  // create artdaq Supervisor context record
5235  row = appTable.tableView_->addRow(
5236  author, true /*incrementUniqueData*/, "artdaqSupervisor");
5237  // set app status true
5238  appTable.tableView_->setValueAsString(
5239  "1", row, appTable.tableView_->getColStatus());
5240 
5241  appUID =
5242  appTable.tableView_
5243  ->getDataView()[row][appTable.tableView_->getColUID()];
5244 
5245  __COUTV__(row);
5246  __COUTV__(appUID);
5247 
5248  // set class
5249  appTable.tableView_->setValueAsString(
5250  ARTDAQ_SUPERVISOR_CLASS,
5251  row,
5252  appTable.tableView_->findCol(
5253  XDAQContextTable::colApplication_.colClass_));
5254  // set module
5255  appTable.tableView_->setValueAsString(
5256  "${OTSDAQ_LIB}/libARTDAQSupervisor.so",
5257  row,
5258  appTable.tableView_->findCol(
5259  XDAQContextTable::colApplication_.colModule_));
5260  // set groupid
5261  appTable.tableView_->setValueAsString(
5262  contextAppGroupID,
5263  row,
5264  appTable.tableView_->findCol(XDAQContextTable::colApplication_
5265  .colApplicationGroupID_));
5266 
5267  // create group link to artdaq Supervisor app properties
5268  appTable.tableView_->setValueAsString(
5269  ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME,
5270  row,
5271  appTable.tableView_->findCol(XDAQContextTable::colApplication_
5272  .colLinkToPropertyTable_));
5273  appPropertiesGroupID = appTable.tableView_->setUniqueColumnValue(
5274  row,
5275  appTable.tableView_->findCol(XDAQContextTable::colApplication_
5276  .colLinkToPropertyGroupID_),
5277  appUID + "Properties");
5278 
5279  __COUTV__(appPropertiesGroupID);
5280  }
5281  else
5282  {
5283  __COUT__ << "Getting row of existing parent supervisor." << __E__;
5284 
5285  // get row of current artdaq supervisor app
5286  row =
5287  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
5288  .getNode(artdaqContext->contextUID_)
5289  .getNode(XDAQContextTable::colContext_
5290  .colLinkToApplicationTable_)
5291  .getNode(artdaqContext->applications_[0].applicationUID_)
5292  .getRow();
5293  __COUTV__(row);
5294  }
5295 
5296  // create group link to artdaq Supervisor app properties
5297  // create link whether or not parents were created
5298  // because, if here, then artdaq supervisor record was created.
5299  appTable.tableView_->setValueAsString(
5300  ARTDAQ_SUPERVISOR_TABLE,
5301  row,
5302  appTable.tableView_->findCol(
5303  XDAQContextTable::colApplication_.colLinkToSupervisorTable_));
5304  appTable.tableView_->setValueAsString(
5305  artdaqSupervisorUID,
5306  row,
5307  appTable.tableView_->findCol(
5308  XDAQContextTable::colApplication_.colLinkToSupervisorUID_));
5309 
5310  } // end create app entry
5311 
5312  // create artdaq Supervisor properties
5313  if(needArtdaqSupervisorParents)
5314  {
5315  unsigned int row;
5316 
5317  const std::vector<std::string> propertyUIDs = {"Partition0",
5318  "ProductsDir",
5319  "FragmentSize",
5320  "BoardReaderTimeout",
5321  "EventBuilderTimeout",
5322  "DataLoggerTimeout",
5323  "DispatcherTimeout"};
5324  const std::vector<std::string> propertyNames = {
5325  "partition", //"Partition0",
5326  "productsdir_for_bash_scripts", //"ProductsDir",
5327  "max_fragment_size_bytes", //"FragmentSize",
5328  "boardreader_timeout", //"BoardReaderTimeout",
5329  "eventbuilder_timeout", //"EventBuilderTimeout",
5330  "datalogger_timeout", //"DataLoggerTimeout",
5331  "dispatcher_timeout" //"DispatcherTimeout"
5332  };
5333  const std::vector<std::string> propertyValues = {
5334  "0", //"Partition0",
5335  "${OTS_PRODUCTS}", //"ProductsDir",
5336  "1284180560", //"FragmentSize",
5337  "600", //"BoardReaderTimeout",
5338  "600", //"EventBuilderTimeout",
5339  "600", //"DataLoggerTimeout",
5340  "600" //"DispatcherTimeout"
5341  };
5342 
5343  for(unsigned int i = 0; i < propertyNames.size(); ++i)
5344  {
5345  // create artdaq Supervisor property record
5346  row = appPropertyTable.tableView_->addRow(
5347  author,
5348  true /*incrementUniqueData*/,
5349  appUID + propertyUIDs[i]);
5350  // set app status true
5351  appPropertyTable.tableView_->setValueAsString(
5352  "1", row, appPropertyTable.tableView_->getColStatus());
5353 
5354  // set type
5355  appPropertyTable.tableView_->setValueAsString(
5356  "ots::SupervisorProperty",
5357  row,
5358  appPropertyTable.tableView_->findCol(
5359  XDAQContextTable::colAppProperty_.colPropertyType_));
5360  // set name
5361  appPropertyTable.tableView_->setValueAsString(
5362  propertyNames[i],
5363  row,
5364  appPropertyTable.tableView_->findCol(
5365  XDAQContextTable::colAppProperty_.colPropertyName_));
5366  // set value
5367  appPropertyTable.tableView_->setValueAsString(
5368  propertyValues[i],
5369  row,
5370  appPropertyTable.tableView_->findCol(
5371  XDAQContextTable::colAppProperty_.colPropertyValue_));
5372  // set groupid
5373  appPropertyTable.tableView_->setValueAsString(
5374  appPropertiesGroupID,
5375  row,
5376  appPropertyTable.tableView_->findCol(
5377  XDAQContextTable::colAppProperty_.colPropertyGroupID_));
5378  } // end property create loop
5379  } // end create app property entries
5380 
5381  {
5382  std::stringstream ss;
5383  contextTable.tableView_->print(ss);
5384  __COUT_MULTI__(0, ss.str());
5385  }
5386  {
5387  std::stringstream ss;
5388  appTable.tableView_->print(ss);
5389  __COUT_MULTI__(0, ss.str());
5390  }
5391  {
5392  std::stringstream ss;
5393  appPropertyTable.tableView_->print(ss);
5394  __COUT_MULTI__(0, ss.str());
5395  }
5396 
5397  contextTable.tableView_
5398  ->init(); // verify new table (throws runtime_errors)
5399  appTable.tableView_->init(); // verify new table (throws runtime_errors)
5400  appPropertyTable.tableView_
5401  ->init(); // verify new table (throws runtime_errors)
5402  }
5403  catch(...)
5404  {
5405  __COUT__
5406  << "Table errors while creating ARTDAQ Supervisor. Erasing all newly "
5407  "created table versions."
5408  << __E__;
5409  throw; // re-throw
5410  } // end catch
5411 
5412  __COUT_INFO__ << "Edits complete for new artdaq Supervisor! Created '"
5413  << appUID << "'" << __E__;
5414 
5415  if(0) //keep for debugging save process
5416  {
5417  __SS__ << "DEBUG blocking artdaq supervisor save!" << __E__;
5418  __SS_THROW__;
5419  }
5420  TableGroupKey newContextGroupKey;
5421  contextGroupEdit.saveChanges(contextGroupEdit.originalGroupName_,
5422  newContextGroupKey,
5423  nullptr /*foundEquivalentGroupKey*/,
5424  true /*activateNewGroup*/,
5425  true /*updateGroupAliases*/,
5426  true /*updateTableAliases*/);
5427 
5428  } // end create artdaq Supervisor in context group
5429 
5430  } // end artdaq Supervisor verification
5431  else
5432  {
5433  artdaqSupervisorRow =
5434  cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
5435  .getNode(artdaqContext->contextUID_)
5436  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
5437  .getNode(artdaqContext->applications_[0].applicationUID_)
5438  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_)
5439  .getRow();
5440  }
5441 
5442  __COUT__ << "------------------------- artdaq nodes to save:" << __E__;
5443  for(auto& subsystemPair : subsystemObjectMap)
5444  {
5445  __COUTV__(subsystemPair.first);
5446 
5447  } // end subsystem loop
5448 
5449  for(auto& nodeTypePair : nodeTypeToObjectMap)
5450  {
5451  __COUTV__(nodeTypePair.first);
5452 
5453  for(auto& nodePair : nodeTypePair.second)
5454  {
5455  __COUTV__(nodePair.first);
5456  }
5457 
5458  } // end node type loop
5459  __COUT__ << "------------------------- end artdaq nodes to save." << __E__;
5460 
5461  //==================================
5462  // at this point artdaqSupervisor is verified and we have row
5463  __COUTV__(artdaqSupervisorRow);
5464  if(artdaqSupervisorRow >= TableView::INVALID)
5465  {
5466  __SS__ << "Invalid artdaq Supervisor row " << artdaqSupervisorRow << " found!"
5467  << __E__;
5468  __SS_THROW__;
5469  }
5470 
5471  // Remaining steps:
5472  // Step 1. create/verify subsystems and destinations
5473  // Step 2. for each node, create/verify records
5474 
5475  // open try for decorating configuration group errors and for clean code scope
5476  try
5477  {
5478  unsigned int row;
5479 
5480  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(
5481  ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
5482 
5483  // for any NO_LINK links in artdaqSupervisor record, fix them
5484  {
5485  std::string artdaqSupervisorUID =
5486  artdaqSupervisorTable.tableView_
5487  ->getDataView()[artdaqSupervisorRow]
5488  [artdaqSupervisorTable.tableView_->getColUID()];
5489 
5490  // create group link to board readers
5491  if(artdaqSupervisorTable.tableView_
5492  ->getDataView()[artdaqSupervisorRow]
5493  [artdaqSupervisorTable.tableView_->findCol(
5494  colARTDAQSupervisor_.colLinkToBoardReaders_)] ==
5495  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5496  {
5497  __COUT__ << "Fixing missing link to Readers" << __E__;
5498  artdaqSupervisorTable.tableView_->setValueAsString(
5499  ARTDAQ_READER_TABLE,
5500  artdaqSupervisorRow,
5501  artdaqSupervisorTable.tableView_->findCol(
5502  colARTDAQSupervisor_.colLinkToBoardReaders_));
5503  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5504  artdaqSupervisorRow,
5505  artdaqSupervisorTable.tableView_->findCol(
5506  colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
5507  artdaqSupervisorUID +
5508  processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
5509  }
5510 
5511  // create group link to event builders
5512  if(artdaqSupervisorTable.tableView_
5513  ->getDataView()[artdaqSupervisorRow]
5514  [artdaqSupervisorTable.tableView_->findCol(
5515  colARTDAQSupervisor_.colLinkToEventBuilders_)] ==
5516  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5517  {
5518  __COUT__ << "Fixing missing link to Builders" << __E__;
5519  artdaqSupervisorTable.tableView_->setValueAsString(
5520  ARTDAQ_BUILDER_TABLE,
5521  artdaqSupervisorRow,
5522  artdaqSupervisorTable.tableView_->findCol(
5523  colARTDAQSupervisor_.colLinkToEventBuilders_));
5524  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5525  artdaqSupervisorRow,
5526  artdaqSupervisorTable.tableView_->findCol(
5527  colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
5528  artdaqSupervisorUID +
5529  processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
5530  }
5531 
5532  // create group link to data loggers
5533  if(artdaqSupervisorTable.tableView_
5534  ->getDataView()[artdaqSupervisorRow]
5535  [artdaqSupervisorTable.tableView_->findCol(
5536  colARTDAQSupervisor_.colLinkToDataLoggers_)] ==
5537  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5538  {
5539  __COUT__ << "Fixing missing link to Loggers" << __E__;
5540  artdaqSupervisorTable.tableView_->setValueAsString(
5541  ARTDAQ_LOGGER_TABLE,
5542  artdaqSupervisorRow,
5543  artdaqSupervisorTable.tableView_->findCol(
5544  colARTDAQSupervisor_.colLinkToDataLoggers_));
5545  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5546  artdaqSupervisorRow,
5547  artdaqSupervisorTable.tableView_->findCol(
5548  colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
5549  artdaqSupervisorUID +
5550  processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
5551  }
5552 
5553  // create group link to dispatchers
5554  if(artdaqSupervisorTable.tableView_
5555  ->getDataView()[artdaqSupervisorRow]
5556  [artdaqSupervisorTable.tableView_->findCol(
5557  colARTDAQSupervisor_.colLinkToDispatchers_)] ==
5558  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5559  {
5560  __COUT__ << "Fixing missing link to Dispatchers" << __E__;
5561  artdaqSupervisorTable.tableView_->setValueAsString(
5562  ARTDAQ_DISPATCHER_TABLE,
5563  artdaqSupervisorRow,
5564  artdaqSupervisorTable.tableView_->findCol(
5565  colARTDAQSupervisor_.colLinkToDispatchers_));
5566  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5567  artdaqSupervisorRow,
5568  artdaqSupervisorTable.tableView_->findCol(
5569  colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
5570  artdaqSupervisorUID +
5571  processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
5572  }
5573 
5574  // create group link to routing managers
5575  if(artdaqSupervisorTable.tableView_
5576  ->getDataView()[artdaqSupervisorRow]
5577  [artdaqSupervisorTable.tableView_->findCol(
5578  colARTDAQSupervisor_.colLinkToRoutingManagers_)] ==
5579  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5580  {
5581  __COUT__ << "Fixing missing link to Routers" << __E__;
5582  artdaqSupervisorTable.tableView_->setValueAsString(
5583  ARTDAQ_ROUTER_TABLE,
5584  artdaqSupervisorRow,
5585  artdaqSupervisorTable.tableView_->findCol(
5586  colARTDAQSupervisor_.colLinkToRoutingManagers_));
5587  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
5588  artdaqSupervisorRow,
5589  artdaqSupervisorTable.tableView_->findCol(
5590  colARTDAQSupervisor_.colLinkToRoutingManagersGroupID_),
5591  artdaqSupervisorUID +
5592  processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
5593  }
5594 
5595  {
5596  std::stringstream ss;
5597  artdaqSupervisorTable.tableView_->print(ss);
5598  __COUT_MULTI__(0, ss.str());
5599  }
5600  } // end fixing links
5601 
5602  // Step 1. create/verify subsystems and destinations
5603  TableEditStruct& artdaqSubsystemTable = configGroupEdit.getTableEditStruct(
5604  ARTDAQ_SUBSYSTEM_TABLE, true /*markModified*/);
5605 
5606  // clear all records
5607  artdaqSubsystemTable.tableView_->deleteAllRows();
5608 
5609  for(auto& subsystemPair : subsystemObjectMap)
5610  {
5611  __COUTV__(subsystemPair.first);
5612  __COUTV__(subsystemPair.second);
5613 
5614  // create artdaq Subsystem record
5615  row = artdaqSubsystemTable.tableView_->addRow(
5616  author, true /*incrementUniqueData*/, subsystemPair.first);
5617 
5618  if(subsystemPair.second != "" &&
5619  subsystemPair.second != TableViewColumnInfo::DATATYPE_STRING_DEFAULT &&
5620  subsystemPair.second != TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT &&
5621  subsystemPair.second != NULL_SUBSYSTEM_DESTINATION_LABEL)
5622  {
5623  // set subsystem link
5624  artdaqSubsystemTable.tableView_->setValueAsString(
5625  ARTDAQ_SUBSYSTEM_TABLE,
5626  row,
5627  artdaqSubsystemTable.tableView_->findCol(
5628  colARTDAQSubsystem_.colLinkToDestination_));
5629  artdaqSubsystemTable.tableView_->setValueAsString(
5630  subsystemPair.second,
5631  row,
5632  artdaqSubsystemTable.tableView_->findCol(
5633  colARTDAQSubsystem_.colLinkToDestinationUID_));
5634  }
5635  // else leave disconnected link
5636 
5637  } // end subsystem loop
5638 
5639  // Step 2. for each node, create/verify records
5640  for(auto& nodeTypePair : nodeTypeToObjectMap)
5641  {
5642  __COUTV__(nodeTypePair.first);
5643 
5644  //__COUTV__(StringMacros::mapToString(processTypes_.mapToTable_));
5645 
5646  auto it = processTypes_.mapToTable_.find(nodeTypePair.first);
5647  if(it == processTypes_.mapToTable_.end())
5648  {
5649  __SS__ << "Invalid artdaq node type '" << nodeTypePair.first
5650  << "' attempted!" << __E__;
5651  __SS_THROW__;
5652  }
5653  __COUTV__(it->second);
5654 
5655  // test the table before getting for real
5656  try
5657  {
5658  /* TableEditStruct& tmpTypeTable = */ configGroupEdit.getTableEditStruct(
5659  it->second, true /*markModified*/);
5660  }
5661  catch(...)
5662  {
5663  if(nodeTypePair.second.size())
5664  throw; // do not ignore if user was trying to save records
5665 
5666  __COUT__ << "Ignoring missing table '" << it->second
5667  << "' since there were no user records attempted of type '"
5668  << nodeTypePair.first << ".'" << __E__;
5669  continue;
5670  }
5671  TableEditStruct& typeTable =
5672  configGroupEdit.getTableEditStruct(it->second, true /*markModified*/);
5673 
5674  TableEditStruct* artTable = nullptr;
5675  bool hasArtProcessName = false;
5676  unsigned int artProcessNameCol = -1;
5677  if(nodeTypePair.first != ARTDAQTableBase::processTypes_.READER &&
5678  nodeTypePair.first != ARTDAQTableBase::processTypes_.ROUTER)
5679  {
5680  __COUT__ << "Identified non-Reader, no-Router type '"
5681  << nodeTypePair.first
5682  << "' that has an art link and thus Process Name, so creating "
5683  "table edit structure to ART table."
5684  << __E__;
5685  artTable = &configGroupEdit.getTableEditStruct(
5686  ARTDAQTableBase::ARTDAQ_ART_TABLE, true /*markModified*/);
5687  if(TTEST(1))
5688  {
5689  std::stringstream ss;
5690  artTable->tableView_->print(ss);
5691  __COUT_MULTI__(1, ss.str());
5692  }
5693  artProcessNameCol = artTable->tableView_->findCol(
5694  ARTDAQTableBase::colARTDAQArt_.colProcessName_);
5695  __COUTTV__(artProcessNameCol);
5696 
5697  hasArtProcessName = true;
5698  }
5699  __COUTV__(hasArtProcessName);
5700 
5701  const unsigned int commentCol =
5702  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_COMMENT);
5703  const unsigned int authorCol =
5704  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_AUTHOR);
5705  const unsigned int timestampCol =
5706  typeTable.tableView_->findColByType(TableViewColumnInfo::TYPE_TIMESTAMP);
5707 
5708  // keep track of records to delete, initialize to all in current table
5709  std::map<unsigned int /*type record row*/, bool /*doDelete*/> deleteRecordMap;
5710  unsigned int maxRowToDelete = typeTable.tableView_->getNumberOfRows();
5711  for(unsigned int r = 0; r < typeTable.tableView_->getNumberOfRows(); ++r)
5712  deleteRecordMap.emplace(std::make_pair(
5713  r, // typeTable.tableView_->getDataView()[i][typeTable.tableView_->getColUID()],
5714  true)); // init to delete
5715  __COUTTV__(maxRowToDelete);
5716 
5717  // keep a map of original multinode values, to maintain node specific links
5718  // (emplace when original node is deleted)
5719  // Note special (hierarchical) columns are defined as follows:
5720  // [-1] := ARTDAQTableBase::colARTDAQNotReader_.colLinkToArt_ / ARTDAQTableBase::colARTDAQArt_.colProcessName_
5721  const unsigned int ORIG_MAP_ART_PROC_NAME_COL = -1;
5722  std::map<std::string /*originalMultiNode name*/,
5723  std::map<unsigned int /*col*/, std::string /*value*/>>
5724  originalMultinodeValues;
5725  std::map<std::string /*multinode key*/,
5726  std::map<unsigned int /*col*/,
5727  std::pair<bool /* all siblings have same value */,
5728  std::string /* sameValue */>>>
5729  originalMultinodeSameSiblingValues;
5730  std::map<
5731  std::string /*multinode key*/,
5732  std::map<unsigned int /*col*/,
5733  std::pair<bool /* all siblings have embedded name */,
5734  std::vector<std::string /* splitForEmbeddedValue */>>>>
5735  originalMultinodeAllSiblingEmbeddedName;
5736  std::map<
5737  std::string /*multinode key*/,
5738  std::map<unsigned int /*col*/,
5739  std::pair<bool /* all siblings have embedded printer index */,
5740  std::vector<std::string /* splitForEmbeddedIndex */>>>>
5741  originalMultinodeAllSiblingEmbeddedPrinterIndex;
5742 
5743  // node instance loop
5744  for(auto& nodePair : nodeTypePair.second)
5745  {
5746  __COUTV__(nodePair.first); //new name
5747 
5748  // default multi-node and array hostname info to empty
5749  std::vector<std::string> nodeIndices, hostnameIndices;
5750  unsigned int hostnameFixedWidth = 0, nodeNameFixedWidth = 0;
5751  std::string hostname;
5752 
5753  // if original record is found, then commandeer that record
5754  // else create a new record
5755  // Node properties: {originalName,hostname,subsystemName,(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)}
5756 
5757  // node parameter loop
5758  for(unsigned int i = 0; i < nodePair.second.size(); ++i)
5759  {
5760  __COUTV__(nodePair.second[i]); //original name
5761 
5762  if(i == 0) // original UID
5763  {
5764  std::string nodeName;
5765  // Steps:
5766  // if original was multi-node,
5767  // then delete all but one
5768  // else
5769  // take over the row, or create new
5770  if(nodePair.second[i][0] == ':')
5771  {
5772  __COUT__ << "Handling original multi-node." << __E__;
5773 
5774  // format:
5775  // :<nodeNameFixedWidth>:<nodeVectorIndexString>:<nodeNameTemplate>
5776 
5777  std::string lastOriginalName;
5778  std::vector<std::string> originalParameterArr =
5780  &(nodePair.second[i].c_str()[1]),
5781  {':'} /*delimiter*/);
5782 
5783  if(originalParameterArr.size() != 3)
5784  {
5785  __SS__ << "Illegal original name parameter string '"
5786  << nodePair.second[i] << "!'" << __E__;
5787  __SS_THROW__;
5788  }
5789  __COUTTV__(
5790  StringMacros::vectorToString(originalParameterArr));
5791 
5792  unsigned int fixedWidth;
5793  sscanf(originalParameterArr[0].c_str(), "%u", &fixedWidth);
5794  __COUTV__(fixedWidth);
5795 
5796  std::vector<std::string> printerSyntaxArr =
5797  StringMacros::getVectorFromString(originalParameterArr[1],
5798  {','} /*delimiter*/);
5799 
5800  // unsigned int count = 0;
5801  std::vector<std::string> originalNodeIndices;
5802  for(auto& printerSyntaxValue : printerSyntaxArr)
5803  {
5804  __COUTV__(printerSyntaxValue);
5805 
5806  std::vector<std::string> printerSyntaxRange =
5808  printerSyntaxValue, {'-'} /*delimiter*/);
5809 
5810  if(printerSyntaxRange.size() == 0 ||
5811  printerSyntaxRange.size() > 2)
5812  {
5813  __SS__ << "Illegal multi-node printer syntax string '"
5814  << printerSyntaxValue << "!'" << __E__;
5815  __SS_THROW__;
5816  }
5817  else if(printerSyntaxRange.size() == 1)
5818  {
5819  __COUTV__(printerSyntaxRange[0]);
5820  originalNodeIndices.push_back(printerSyntaxRange[0]);
5821  }
5822  else // printerSyntaxRange.size() == 2
5823  {
5824  unsigned int lo, hi;
5825  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
5826  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
5827  if(hi < lo) // swap
5828  {
5829  lo = hi;
5830  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
5831  }
5832  for(; lo <= hi; ++lo)
5833  {
5834  __COUTTV__(lo);
5835  originalNodeIndices.push_back(std::to_string(lo));
5836  }
5837  }
5838  } // end printer syntax loop
5839 
5840  __COUTTV__(originalParameterArr[2]);
5841  //remove ;status=
5842  originalParameterArr[2] = originalParameterArr[2].substr(
5843  0, originalParameterArr[2].find(";status="));
5844  __COUTV__(originalParameterArr[2]);
5845  std::vector<std::string> originalNamePieces =
5846  StringMacros::getVectorFromString(originalParameterArr[2],
5847  {'*'} /*delimiter*/);
5848  __COUTV__(StringMacros::vectorToString(originalNamePieces));
5849 
5850  if(originalNamePieces.size() < 2)
5851  {
5852  __SS__ << "Illegal original multi-node name template - "
5853  "please use * to indicate where the multi-node "
5854  "index should be inserted!"
5855  << __E__;
5856  __SS_THROW__;
5857  }
5858 
5859  if(TTEST(1))
5860  {
5861  std::stringstream ss;
5862  typeTable.tableView_->print(ss);
5863  __COUT_MULTI__(1, ss.str());
5864  }
5865 
5866  //create matching bools to decide copy stategy
5867  __COUT__
5868  << "originalMultinodeSameSiblingValues init col map for "
5869  << nodePair.first << __E__;
5870  originalMultinodeSameSiblingValues.emplace(std::make_pair(
5871  nodePair.first,
5872  std::map<
5873  unsigned int /*col*/,
5874  std::pair<bool /* all siblings have same value */,
5875  std::string /* sameValue */>>()));
5876  __COUT__ << "originalMultinodeAllSiblingEmbeddedName init "
5877  "col map for "
5878  << nodePair.first << __E__;
5879  originalMultinodeAllSiblingEmbeddedName.emplace(std::make_pair(
5880  nodePair.first,
5881  std::map<
5882  unsigned int /*col*/,
5883  std::pair<
5884  bool /* all siblings have embedded name */,
5885  std::vector<
5886  std::
5887  string /* splitForEmbeddedValue */>>>()));
5888  __COUT__ << "originalMultinodeAllSiblingEmbeddedPrinterIndex "
5889  "init col map for "
5890  << nodePair.first << __E__;
5891  originalMultinodeAllSiblingEmbeddedPrinterIndex.emplace(
5892  std::make_pair(
5893  nodePair.first,
5894  std::map<
5895  unsigned int /*col*/,
5896  std::pair<
5897  bool /* all siblings have embedded printed index */
5898  ,
5899  std::vector<
5900  std::
5901  string /* splitForEmbeddedIndex */>>>()));
5902 
5903  // bool isFirst = true;
5904  unsigned int originalRow = TableView::INVALID,
5905  lastOriginalRow = TableView::INVALID,
5906  lastArtProcessRow = TableView::INVALID;
5907  for(unsigned int i = 0; i < originalNodeIndices.size(); ++i)
5908  {
5909  std::string originalName = originalNamePieces[0];
5910  std::string nodeNameIndex;
5911  for(unsigned int p = 1; p < originalNamePieces.size();
5912  ++p)
5913  {
5914  nodeNameIndex = originalNodeIndices[i];
5915  if(fixedWidth > 1)
5916  {
5917  if(nodeNameIndex.size() > fixedWidth)
5918  {
5919  __SS__ << "Illegal original node name index '"
5920  << nodeNameIndex
5921  << "' - length is longer than fixed "
5922  "width requirement of "
5923  << fixedWidth << "!" << __E__;
5924  __SS_THROW__;
5925  }
5926 
5927  // 0 prepend as needed
5928  while(nodeNameIndex.size() < fixedWidth)
5929  nodeNameIndex = "0" + nodeNameIndex;
5930  } // end fixed width handling
5931 
5932  originalName += nodeNameIndex + originalNamePieces[p];
5933  }
5934  __COUTTV__(originalName);
5935  originalRow = typeTable.tableView_->findRow(
5936  typeTable.tableView_->getColUID(),
5937  originalName,
5938  0 /*offsetRow*/,
5939  true /*doNotThrow*/);
5940  __COUTTV__(originalRow);
5941 
5942  // if have a new 'seed' valid row, then delete last valid row
5943  // before deleting, record all customizing values to draw from when creating new multinode records
5944  auto result = originalMultinodeValues.emplace(
5945  std::make_pair(originalName,
5946  std::map<unsigned int /*col*/,
5947  std::string /*value*/>()));
5948  if(!result.second)
5949  __COUT__
5950  << "originalName '" << originalName
5951  << "' already in original multinode value cache."
5952  << __E__;
5953  else //keep original cache values
5954  {
5955  __COUT__ << "Saving multinode value " << originalName
5956  << "[" << originalRow
5957  << "][*] with row count = "
5958  << typeTable.tableView_->getNumberOfRows()
5959  << __E__;
5960 
5961  // save all link values
5962  for(unsigned int col = 0;
5963  col < typeTable.tableView_->getNumberOfColumns();
5964  ++col)
5965  {
5966  if(typeTable.tableView_->getColumnInfo(col)
5967  .getName() ==
5968  ARTDAQTableBase::
5969  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK ||
5970  typeTable.tableView_->getColumnInfo(col)
5971  .getName() ==
5972  ARTDAQTableBase::
5973  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID ||
5974  typeTable.tableView_->getColumnInfo(col)
5975  .getName() ==
5976  ARTDAQTableBase::
5977  ARTDAQ_TYPE_TABLE_HOSTNAME ||
5978  typeTable.tableView_->getColumnInfo(col)
5979  .isUID() ||
5980  col == typeTable.tableView_->getColStatus() ||
5981  typeTable.tableView_->getColumnInfo(col)
5982  .isGroupID() ||
5983  col == timestampCol ||
5984  col ==
5985  authorCol) //always go with now author/timestamp on touched records (too easy to misidentify change vs nochange)
5986  continue; // skip subsystem link, etc that is modified by fields maintained in the GUI
5987  else
5988  {
5989  __COUTT__
5990  << "Caching node value: " << originalName
5991  << "[" << originalRow << "][" << col
5992  << "/"
5993  << typeTable.tableView_
5994  ->getColumnInfo(col)
5995  .getName()
5996  << "] = "
5997  << typeTable.tableView_
5998  ->getDataView()[originalRow][col]
5999  << __E__;
6000  originalMultinodeValues.at(originalName)
6001  .emplace(std::make_pair(
6002  col,
6003  typeTable.tableView_
6004  ->getDataView()[originalRow]
6005  [col]));
6006 
6007  //the first time, set to true and then prove wrong
6008 
6009  for(const auto& pair :
6010  originalMultinodeSameSiblingValues)
6011  __COUTT__ << "originalMultinodeSameSiblin"
6012  "gValues["
6013  << pair.first << "]" << __E__;
6014  auto result2 =
6015  originalMultinodeSameSiblingValues
6016  .at(nodePair.first)
6017  .emplace(std::make_pair(
6018  col,
6019  //same value
6020  std::make_pair(
6021  true,
6022  typeTable.tableView_
6023  ->getDataView()
6024  [originalRow][col])));
6025 
6026  for(const auto& pair :
6027  originalMultinodeAllSiblingEmbeddedName)
6028  __COUTT__ << "originalMultinodeAllSibling"
6029  "EmbeddedName["
6030  << pair.first << "]" << __E__;
6031  originalMultinodeAllSiblingEmbeddedName
6032  .at(nodePair.first)
6033  .emplace(std::make_pair(
6034  col,
6035  std::make_pair( //bool
6036  typeTable.tableView_
6037  ->getDataView()
6038  [originalRow][col]
6039  .find(originalName) !=
6040  std::string::npos,
6041  //split string
6042  std::vector<std::string>())));
6043 
6044  for(const auto& pair :
6045  originalMultinodeAllSiblingEmbeddedPrinterIndex)
6046  __COUTT__ << "originalMultinodeAllSibling"
6047  "EmbeddedPrinterIndex["
6048  << pair.first << "]" << __E__;
6049  originalMultinodeAllSiblingEmbeddedPrinterIndex
6050  .at(nodePair.first)
6051  .emplace(std::make_pair(
6052  col,
6053  std::make_pair( //bool
6054  typeTable.tableView_
6055  ->getDataView()
6056  [originalRow][col]
6057  .find(nodeNameIndex) !=
6058  std::string::npos,
6059  //split string
6060  std::vector<std::string>())));
6061 
6062  if(result2
6063  .second) //emplace always should work first time
6064  {
6065  __COUTTV__(
6066  originalMultinodeSameSiblingValues
6067  .at(nodePair.first)
6068  .at(col)
6069  .second);
6070 
6071  __COUTTV__(
6072  originalMultinodeAllSiblingEmbeddedName
6073  .at(nodePair.first)
6074  .at(col)
6075  .first);
6076  if(originalMultinodeAllSiblingEmbeddedName
6077  .at(nodePair.first)
6078  .at(col)
6079  .first)
6080  {
6081  __COUTT__
6082  << "Determine string splits for "
6083  "embedded name"
6084  << __E__;
6085  const std::string& val =
6086  typeTable.tableView_
6087  ->getDataView()[originalRow]
6088  [col];
6089  size_t pos = val.find(originalName);
6090  originalMultinodeAllSiblingEmbeddedName
6091  .at(nodePair.first)
6092  .at(col)
6093  .second.push_back(
6094  val.substr(0, pos));
6095  originalMultinodeAllSiblingEmbeddedName
6096  .at(nodePair.first)
6097  .at(col)
6098  .second.push_back(val.substr(
6099  pos + originalName.size()));
6100  __COUTTV__(StringMacros::vectorToString(
6101  originalMultinodeAllSiblingEmbeddedName
6102  .at(nodePair.first)
6103  .at(col)
6104  .second));
6105  }
6106  __COUTTV__(
6107  originalMultinodeAllSiblingEmbeddedPrinterIndex
6108  .at(nodePair.first)
6109  .at(col)
6110  .first);
6111  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
6112  .at(nodePair.first)
6113  .at(col)
6114  .first)
6115  {
6116  __COUTT__ << "Determine string "
6117  "splits for embedded "
6118  "printer syntax index: "
6119  << nodeNameIndex << __E__;
6120  const std::string& val =
6121  typeTable.tableView_
6122  ->getDataView()[originalRow]
6123  [col];
6124  size_t pos = val.find(nodeNameIndex);
6125  originalMultinodeAllSiblingEmbeddedPrinterIndex
6126  .at(nodePair.first)
6127  .at(col)
6128  .second.push_back(
6129  val.substr(0, pos));
6130  originalMultinodeAllSiblingEmbeddedPrinterIndex
6131  .at(nodePair.first)
6132  .at(col)
6133  .second.push_back(val.substr(
6134  pos + nodeNameIndex.size()));
6135  __COUTTV__(StringMacros::vectorToString(
6136  originalMultinodeAllSiblingEmbeddedPrinterIndex
6137  .at(nodePair.first)
6138  .at(col)
6139  .second));
6140  }
6141  }
6142  else //not first time, so prove wrong
6143  {
6144  if(originalMultinodeSameSiblingValues
6145  .at(nodePair.first)
6146  .at(col)
6147  .first)
6148  {
6149  __COUTT__ << "Checking sibling same "
6150  "values... for "
6151  << nodePair.first << __E__;
6152  if(typeTable.tableView_
6153  ->getDataView()[originalRow]
6154  [col] !=
6155  typeTable.tableView_->getDataView()
6156  [lastOriginalRow][col])
6157  {
6158  __COUT__
6159  << "Found different sibling "
6160  "values at col="
6161  << col << " for "
6162  << nodePair.first << __E__;
6163  originalMultinodeSameSiblingValues
6164  .at(nodePair.first)
6165  .at(col)
6166  .first = false;
6167  }
6168  }
6169  if(originalMultinodeAllSiblingEmbeddedName
6170  .at(nodePair.first)
6171  .at(col)
6172  .first)
6173  {
6174  __COUTT__ << "Checking sibling "
6175  "embedded name... for "
6176  << nodePair.first << ":"
6177  << originalName << __E__;
6178  if(typeTable.tableView_
6179  ->getDataView()[originalRow]
6180  [col]
6181  .find(originalName) ==
6182  std::string::npos)
6183  {
6184  __COUT__ << "Found no embedded "
6185  "name at col="
6186  << col << " looking for "
6187  << originalName << __E__;
6188  originalMultinodeAllSiblingEmbeddedName
6189  .at(nodePair.first)
6190  .at(col)
6191  .first = false;
6192  }
6193  }
6194  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
6195  .at(nodePair.first)
6196  .at(col)
6197  .first)
6198  {
6199  __COUTT__
6200  << "Checking sibling embedded "
6201  "printer syntax index... for "
6202  << nodePair.first << ":"
6203  << nodeNameIndex << __E__;
6204  if(typeTable.tableView_
6205  ->getDataView()[originalRow]
6206  [col]
6207  .find(nodeNameIndex) ==
6208  std::string::npos)
6209  {
6210  __COUT__ << "Found no embedded "
6211  "printer syntax "
6212  "index at col="
6213  << col << " looking for "
6214  << nodeNameIndex
6215  << __E__;
6216  originalMultinodeAllSiblingEmbeddedPrinterIndex
6217  .at(nodePair.first)
6218  .at(col)
6219  .first = false;
6220  }
6221  }
6222  }
6223 
6224  __COUTT__
6225  << "originalMultinodeSameSiblingValues["
6226  << nodePair.first << "][" << col << "] = "
6227  << originalMultinodeSameSiblingValues
6228  .at(nodePair.first)
6229  .at(col)
6230  .first
6231  << __E__;
6232  __COUTT__
6233  << "originalMultinodeAllSiblingEmbeddedNa"
6234  "me["
6235  << nodePair.first << "][" << col << "] = "
6236  << originalMultinodeAllSiblingEmbeddedName
6237  .at(nodePair.first)
6238  .at(col)
6239  .first
6240  << __E__;
6241  __COUTT__
6242  << "originalMultinodeAllSiblingEmbeddedPr"
6243  "interIndex["
6244  << nodePair.first << "][" << col << "] = "
6245  << originalMultinodeAllSiblingEmbeddedPrinterIndex
6246  .at(nodePair.first)
6247  .at(col)
6248  .first
6249  << __E__;
6250 
6251  if(hasArtProcessName && artTable &&
6252  typeTable.tableView_->getColumnInfo(col)
6253  .getName() ==
6254  ARTDAQTableBase::colARTDAQNotReader_
6255  .colLinkToArtUID_)
6256  {
6257  //note at this point, col = Link to art record
6258  __COUT__
6259  << "Checking ART Process Name... for "
6260  "originalName='"
6261  << originalName << "' / "
6262  << typeTable.tableView_
6263  ->getDataView()[originalRow]
6264  [col]
6265  << __E__;
6266  unsigned int artRow =
6267  artTable->tableView_->findRow(
6268  artTable->tableView_->getColUID(),
6269  /* art UID record name */
6270  typeTable.tableView_
6271  ->getDataView()[originalRow]
6272  [col]);
6273  __COUTTV__(artRow);
6274 
6275  __COUTT__
6276  << "Found ART Process Name = "
6277  << artTable->tableView_->getDataView()
6278  [artRow][artProcessNameCol]
6279  << __E__;
6280 
6281  //original value tracking/emplace handling copied from above L4284
6282  originalMultinodeValues.at(originalName)
6283  .emplace(std::make_pair(
6284  ORIG_MAP_ART_PROC_NAME_COL,
6285  artTable->tableView_
6286  ->getDataView()
6287  [artRow]
6288  [artProcessNameCol]));
6289  __COUTTV__(
6290  originalMultinodeValues
6291  .at(originalName)
6292  .at(ORIG_MAP_ART_PROC_NAME_COL));
6293 
6294  //the first time, set to true and then prove wrong
6295  originalMultinodeSameSiblingValues
6296  .at(nodePair.first)
6297  .emplace(std::make_pair(
6298  ORIG_MAP_ART_PROC_NAME_COL,
6299  //same value
6300  std::make_pair(
6301  true,
6302  artTable->tableView_
6303  ->getDataView()
6304  [artRow]
6305  [artProcessNameCol])));
6306  originalMultinodeAllSiblingEmbeddedName
6307  .at(nodePair.first)
6308  .emplace(std::make_pair(
6309  ORIG_MAP_ART_PROC_NAME_COL,
6310  std::make_pair( //bool
6311  artTable->tableView_
6312  ->getDataView()
6313  [artRow]
6314  [artProcessNameCol]
6315  .find(originalName) !=
6316  std::string::npos,
6317  //split string
6318  std::vector<std::string>())));
6319  originalMultinodeAllSiblingEmbeddedPrinterIndex
6320  .at(nodePair.first)
6321  .emplace(std::make_pair(
6322  ORIG_MAP_ART_PROC_NAME_COL,
6323  std::make_pair( //bool
6324  artTable->tableView_
6325  ->getDataView()
6326  [artRow]
6327  [artProcessNameCol]
6328  .find(
6329  nodeNameIndex) !=
6330  std::string::npos,
6331  //split string
6332  std::vector<std::string>())));
6333 
6334  if(result2
6335  .second) //emplace always should work first time
6336  {
6337  __COUTTV__(
6338  originalMultinodeSameSiblingValues
6339  .at(nodePair.first)
6340  .at(ORIG_MAP_ART_PROC_NAME_COL)
6341  .second);
6342 
6343  __COUTTV__(
6344  originalMultinodeAllSiblingEmbeddedName
6345  .at(nodePair.first)
6346  .at(ORIG_MAP_ART_PROC_NAME_COL)
6347  .first);
6348  if(originalMultinodeAllSiblingEmbeddedName
6349  .at(nodePair.first)
6350  .at(ORIG_MAP_ART_PROC_NAME_COL)
6351  .first)
6352  {
6353  __COUTT__
6354  << "Determine string splits "
6355  "for embedded name"
6356  << __E__;
6357  const std::string& val =
6358  artTable->tableView_
6359  ->getDataView()
6360  [artRow]
6361  [artProcessNameCol];
6362  size_t pos =
6363  val.find(originalName);
6364  originalMultinodeAllSiblingEmbeddedName
6365  .at(nodePair.first)
6366  .at(ORIG_MAP_ART_PROC_NAME_COL)
6367  .second.push_back(
6368  val.substr(0, pos));
6369  originalMultinodeAllSiblingEmbeddedName
6370  .at(nodePair.first)
6371  .at(ORIG_MAP_ART_PROC_NAME_COL)
6372  .second.push_back(val.substr(
6373  pos +
6374  originalName.size()));
6375  __COUTTV__(StringMacros::vectorToString(
6376  originalMultinodeAllSiblingEmbeddedName
6377  .at(nodePair.first)
6378  .at(ORIG_MAP_ART_PROC_NAME_COL)
6379  .second));
6380  }
6381  __COUTTV__(
6382  originalMultinodeAllSiblingEmbeddedPrinterIndex
6383  .at(nodePair.first)
6384  .at(ORIG_MAP_ART_PROC_NAME_COL)
6385  .first);
6386  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
6387  .at(nodePair.first)
6388  .at(ORIG_MAP_ART_PROC_NAME_COL)
6389  .first)
6390  {
6391  __COUTT__
6392  << "Determine string splits "
6393  "for embedded printer "
6394  "syntax index: "
6395  << nodeNameIndex << __E__;
6396  const std::string& val =
6397  artTable->tableView_
6398  ->getDataView()
6399  [artRow]
6400  [artProcessNameCol];
6401  size_t pos =
6402  val.find(nodeNameIndex);
6403  originalMultinodeAllSiblingEmbeddedPrinterIndex
6404  .at(nodePair.first)
6405  .at(ORIG_MAP_ART_PROC_NAME_COL)
6406  .second.push_back(
6407  val.substr(0, pos));
6408  originalMultinodeAllSiblingEmbeddedPrinterIndex
6409  .at(nodePair.first)
6410  .at(ORIG_MAP_ART_PROC_NAME_COL)
6411  .second.push_back(val.substr(
6412  pos +
6413  nodeNameIndex.size()));
6414  __COUTTV__(StringMacros::vectorToString(
6415  originalMultinodeAllSiblingEmbeddedPrinterIndex
6416  .at(nodePair.first)
6417  .at(ORIG_MAP_ART_PROC_NAME_COL)
6418  .second));
6419  }
6420  }
6421  else //not first time, so prove wrong
6422  {
6423  if(originalMultinodeSameSiblingValues
6424  .at(nodePair.first)
6425  .at(ORIG_MAP_ART_PROC_NAME_COL)
6426  .first)
6427  {
6428  __COUTT__ << "Checking sibling "
6429  "same values... for "
6430  << nodePair.first
6431  << __E__;
6432  if(artTable->tableView_
6433  ->getDataView()
6434  [artRow]
6435  [artProcessNameCol] !=
6436  artTable->tableView_
6437  ->getDataView()
6438  [lastArtProcessRow]
6439  [artProcessNameCol])
6440  {
6441  __COUT__
6442  << "Found different "
6443  "sibling values "
6444  "at artProcessNameCol="
6445  << artProcessNameCol
6446  << " for "
6447  << nodePair.first
6448  << __E__;
6449  originalMultinodeSameSiblingValues
6450  .at(nodePair.first)
6451  .at(ORIG_MAP_ART_PROC_NAME_COL)
6452  .first = false;
6453  }
6454  }
6455  if(originalMultinodeAllSiblingEmbeddedName
6456  .at(nodePair.first)
6457  .at(ORIG_MAP_ART_PROC_NAME_COL)
6458  .first)
6459  {
6460  __COUTT__
6461  << "Checking sibling "
6462  "embedded name... for "
6463  << nodePair.first << ":"
6464  << originalName << __E__;
6465  if(artTable->tableView_
6466  ->getDataView()
6467  [artRow]
6468  [artProcessNameCol]
6469  .find(originalName) ==
6470  std::string::npos)
6471  {
6472  __COUT__
6473  << "Found no embedded "
6474  "name at "
6475  "artProcessNameCol="
6476  << artProcessNameCol
6477  << " looking for "
6478  << originalName << __E__;
6479  originalMultinodeAllSiblingEmbeddedName
6480  .at(nodePair.first)
6481  .at(ORIG_MAP_ART_PROC_NAME_COL)
6482  .first = false;
6483  }
6484  }
6485  if(originalMultinodeAllSiblingEmbeddedPrinterIndex
6486  .at(nodePair.first)
6487  .at(ORIG_MAP_ART_PROC_NAME_COL)
6488  .first)
6489  {
6490  __COUTT__
6491  << "Checking sibling "
6492  "embedded printer syntax "
6493  "index... for "
6494  << nodePair.first << ":"
6495  << nodeNameIndex << __E__;
6496  if(artTable->tableView_
6497  ->getDataView()
6498  [artRow]
6499  [artProcessNameCol]
6500  .find(nodeNameIndex) ==
6501  std::string::npos)
6502  {
6503  __COUT__
6504  << "Found no embedded "
6505  "printer syntax index "
6506  "at artProcessNameCol="
6507  << artProcessNameCol
6508  << " looking for "
6509  << nodeNameIndex << __E__;
6510  originalMultinodeAllSiblingEmbeddedPrinterIndex
6511  .at(nodePair.first)
6512  .at(ORIG_MAP_ART_PROC_NAME_COL)
6513  .first = false;
6514  }
6515  }
6516  }
6517 
6518  __COUTT__
6519  << "originalMultinodeSameSiblingValue"
6520  "s["
6521  << nodePair.first << "]["
6522  << ORIG_MAP_ART_PROC_NAME_COL
6523  << "] = "
6524  << originalMultinodeSameSiblingValues
6525  .at(nodePair.first)
6526  .at(ORIG_MAP_ART_PROC_NAME_COL)
6527  .first
6528  << __E__;
6529  __COUTT__
6530  << "originalMultinodeAllSiblingEmbedd"
6531  "edName["
6532  << nodePair.first << "]["
6533  << ORIG_MAP_ART_PROC_NAME_COL
6534  << "] = "
6535  << originalMultinodeAllSiblingEmbeddedName
6536  .at(nodePair.first)
6537  .at(ORIG_MAP_ART_PROC_NAME_COL)
6538  .first
6539  << __E__;
6540  __COUTT__
6541  << "originalMultinodeAllSiblingEmbedd"
6542  "edPrinterIndex["
6543  << nodePair.first << "]["
6544  << ORIG_MAP_ART_PROC_NAME_COL
6545  << "] = "
6546  << originalMultinodeAllSiblingEmbeddedPrinterIndex
6547  .at(nodePair.first)
6548  .at(ORIG_MAP_ART_PROC_NAME_COL)
6549  .first
6550  << __E__;
6551 
6552  __COUT__
6553  << "Checking ART Process Name "
6554  "complete for originalName='"
6555  << originalName << "' / "
6556  << typeTable.tableView_
6557  ->getDataView()[originalRow]
6558  [col]
6559  << __E__;
6560  lastArtProcessRow =
6561  artRow; //save for next comparison
6562  } //end ART Process Name cache handling
6563 
6564  } //end col caching handling
6565  } //end col loop
6566  } //end cache handling
6567 
6568  if(originalRow !=
6569  TableView::
6570  INVALID) // save last original valid row for future cache/deletion
6571  lastOriginalRow = originalRow;
6572 
6573  __COUTTV__(lastOriginalRow);
6574  lastOriginalName = originalName;
6575  } // end loop through multi-node instances
6576 
6577  for(const auto& pair :
6578  originalMultinodeSameSiblingValues.at(nodePair.first))
6579  __COUTT__ << "originalMultinodeSameSiblingValues["
6580  << nodePair.first << "][" << pair.first
6581  << "] = " << pair.second.first << __E__;
6582  for(const auto& pair :
6583  originalMultinodeAllSiblingEmbeddedName.at(
6584  nodePair.first))
6585  __COUTT__ << "originalMultinodeAllSiblingEmbeddedName["
6586  << nodePair.first << "][" << pair.first
6587  << "] = " << pair.second.first << __E__;
6588  for(const auto& pair :
6589  originalMultinodeAllSiblingEmbeddedPrinterIndex.at(
6590  nodePair.first))
6591  __COUTT__
6592  << "originalMultinodeAllSiblingEmbeddedPrinterIndex["
6593  << nodePair.first << "][" << pair.first
6594  << "] = " << pair.second.first << __E__;
6595 
6596  __COUTTV__(lastOriginalRow);
6597  row = lastOriginalRow; // take last valid row to proceed
6598  __COUTV__(row);
6599  } // end handling of original multinode
6600  else
6601  {
6602  std::string originalName = nodePair.second[i].substr(
6603  0, nodePair.second[i].find(";status="));
6604  __COUTV__(originalName);
6605 
6606  // attempt to find original 'single' node name
6607  row = typeTable.tableView_->findRow(
6608  typeTable.tableView_->getColUID(),
6609  originalName,
6610  0 /*offsetRow*/,
6611  true /*doNotThrow*/);
6612  __COUTV__(row);
6613  }
6614 
6615  //if no original nodes, there may be *'s in node name, so remove them
6616  {
6617  nodeName = nodePair.first; // take new node name
6618  __COUTV__(nodeName);
6619  //remove ;status=
6620  nodeName = nodeName.substr(0, nodeName.find(";status="));
6621 
6622  //remove stars for seed nodename
6623  std::string tmpNodeName = nodeName;
6624  nodeName = ""; //clear
6625  for(size_t c = 0; c < tmpNodeName.size(); ++c)
6626  if(tmpNodeName[c] != '*')
6627  nodeName += tmpNodeName[c];
6628  } //end removing *'s from node name
6629 
6630  __COUTV__(nodeName);
6631  if(row == TableView::INVALID)
6632  {
6633  // No original record, so create artdaq type instance record
6634  row = typeTable.tableView_->addRow(
6635  author, true /*incrementUniqueData*/, nodeName);
6636 
6637  // fill defaults properties/parameters here!
6638  if(nodeTypePair.first == processTypes_.READER)
6639  {
6640  __COUT__ << "Handling new " << nodeTypePair.first
6641  << " defaults!" << __E__;
6642  TableEditStruct& daqParameterTable =
6643  configGroupEdit.getTableEditStruct(
6644  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6645  true /*markModified*/);
6646 
6647  // create group link to daq parameter table
6648  typeTable.tableView_->setValueAsString(
6649  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6650  row,
6651  typeTable.tableView_->findCol(
6652  ARTDAQTableBase::colARTDAQReader_
6653  .colLinkToDaqParameters_));
6654  std::string daqParameterGroupID =
6655  typeTable.tableView_->setUniqueColumnValue(
6656  row,
6657  typeTable.tableView_->findCol(
6658  ARTDAQTableBase::colARTDAQReader_
6659  .colLinkToDaqParametersGroupID_),
6660  nodeName + "DaqParameters");
6661 
6662  {
6663  std::stringstream ss;
6664  typeTable.tableView_->print(ss);
6665  __COUT_MULTI__(1, ss.str());
6666  }
6667 
6668  // now create parameters at target link
6669  const std::vector<std::string> parameterUIDs = {
6670  "BoardID", "FragmentID"};
6671 
6672  const std::vector<std::string> parameterNames = {
6673  "board_id", //"BoardID",
6674  "fragment_id", //"FragmentID"
6675  };
6676  const std::vector<std::string> parameterValues = {
6677  "0", //"BoardID",
6678  "0" //"FragmentID",
6679  };
6680 
6681  unsigned int parameterRow;
6682  for(unsigned int i = 0; i < parameterNames.size(); ++i)
6683  {
6684  // create artdaq Reader property record
6685  parameterRow = daqParameterTable.tableView_->addRow(
6686  author,
6687  true /*incrementUniqueData*/,
6688  nodeName + parameterUIDs[i]);
6689 
6690  // set app status true
6691  daqParameterTable.tableView_->setValueAsString(
6692  "1",
6693  parameterRow,
6694  daqParameterTable.tableView_->getColStatus());
6695  // set key
6696  daqParameterTable.tableView_->setValueAsString(
6697  parameterNames[i],
6698  parameterRow,
6699  daqParameterTable.tableView_->findCol(
6700  ARTDAQTableBase::colARTDAQDaqParameter_
6701  .colDaqParameterKey_));
6702  // set value
6703  daqParameterTable.tableView_->setValueAsString(
6704  parameterValues[i],
6705  parameterRow,
6706  daqParameterTable.tableView_->findCol(
6707  ARTDAQTableBase::colARTDAQDaqParameter_
6708  .colDaqParameterValue_));
6709  // set groupid
6710  daqParameterTable.tableView_->setValueAsString(
6711  daqParameterGroupID,
6712  parameterRow,
6713  daqParameterTable.tableView_->findCol(
6714  ARTDAQTableBase::colARTDAQDaqParameter_
6715  .colDaqParameterGroupID_));
6716 
6717  } // end Reader default property create loop
6718 
6719  daqParameterTable.tableView_
6720  ->init(); // verify new table (throws runtime_errors)
6721 
6722  } // end Reader default property setup
6723  else if(nodeTypePair.first == processTypes_.BUILDER ||
6724  nodeTypePair.first == processTypes_.LOGGER ||
6725  nodeTypePair.first == processTypes_.DISPATCHER)
6726  {
6727  __COUT__ << "Handling new " << nodeTypePair.first
6728  << " defaults!" << __E__;
6729 
6730  // goes through DAQ table
6731  TableEditStruct& daqTable =
6732  configGroupEdit.getTableEditStruct(
6733  ARTDAQTableBase::ARTDAQ_DAQ_TABLE,
6734  true /*markModified*/);
6735  // create DAQ record
6736  unsigned int daqRecordRow = daqTable.tableView_->addRow(
6737  author,
6738  true /*incrementUniqueData*/,
6739  nodeName + "Daq");
6740  std::string daqRecordUID =
6741  daqTable.tableView_
6742  ->getDataView()[daqRecordRow]
6743  [daqTable.tableView_->getColUID()];
6744 
6745  // create unique link to daq table
6746  typeTable.tableView_->setValueAsString(
6747  ARTDAQTableBase::ARTDAQ_DAQ_TABLE,
6748  row,
6749  typeTable.tableView_->findCol(
6750  ARTDAQTableBase::colARTDAQNotReader_
6751  .colLinkToDaq_));
6752  typeTable.tableView_->setValueAsString(
6753  daqRecordUID,
6754  row,
6755  typeTable.tableView_->findCol(
6756  ARTDAQTableBase::colARTDAQNotReader_
6757  .colLinkToDaqUID_));
6758 
6759  TableEditStruct& daqParameterTable =
6760  configGroupEdit.getTableEditStruct(
6761  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6762  true /*markModified*/);
6763  // create group link to daq parameter table
6764  daqTable.tableView_->setValueAsString(
6765  ARTDAQTableBase::ARTDAQ_DAQ_PARAMETER_TABLE,
6766  daqRecordRow,
6767  daqTable.tableView_->findCol(
6768  ARTDAQTableBase::colARTDAQDaq_
6769  .colLinkToDaqParameters_));
6770  std::string daqParameterGroupID =
6771  daqTable.tableView_->setUniqueColumnValue(
6772  daqRecordRow,
6773  daqTable.tableView_->findCol(
6774  ARTDAQTableBase::colARTDAQDaq_
6775  .colLinkToDaqParametersGroupID_),
6776  nodeName + "DaqParameters");
6777 
6778  // now create parameters at target link
6779  const std::vector<std::string> parameterUIDs = {
6780  "BufferCount", "FragmentsPerEvent"};
6781 
6782  const std::vector<std::string> parameterNames = {
6783  "buffer_count", //"BufferCount",
6784  "expected_fragments_per_event" //"FragmentsPerEvent"
6785  };
6786  const std::vector<std::string> parameterValues = {
6787  "10", //"BufferCount",
6788  "0" //"FragmentsPerEvent",
6789  };
6790 
6791  unsigned int parameterRow;
6792  for(unsigned int i = 0; i < parameterNames.size(); ++i)
6793  {
6794  // create artdaq Reader property record
6795  parameterRow = daqParameterTable.tableView_->addRow(
6796  author,
6797  true /*incrementUniqueData*/,
6798  nodeName + parameterUIDs[i]);
6799 
6800  // set app status true
6801  daqParameterTable.tableView_->setValueAsString(
6802  "1",
6803  parameterRow,
6804  daqParameterTable.tableView_->getColStatus());
6805  // set key
6806  daqParameterTable.tableView_->setValueAsString(
6807  parameterNames[i],
6808  parameterRow,
6809  daqParameterTable.tableView_->findCol(
6810  ARTDAQTableBase::colARTDAQDaqParameter_
6811  .colDaqParameterKey_));
6812  // set value
6813  daqParameterTable.tableView_->setValueAsString(
6814  parameterValues[i],
6815  parameterRow,
6816  daqParameterTable.tableView_->findCol(
6817  ARTDAQTableBase::colARTDAQDaqParameter_
6818  .colDaqParameterValue_));
6819  // set groupid
6820  daqParameterTable.tableView_->setValueAsString(
6821  daqParameterGroupID,
6822  parameterRow,
6823  daqParameterTable.tableView_->findCol(
6824  ARTDAQTableBase::colARTDAQDaqParameter_
6825  .colDaqParameterGroupID_));
6826 
6827  } // end Reader default property create loop
6828 
6829  daqTable.tableView_
6830  ->init(); // verify new table (throws runtime_errors)
6831  daqParameterTable.tableView_
6832  ->init(); // verify new table (throws runtime_errors)
6833 
6834  } // end Builder, Logger, Dispatcher default property setup
6835  }
6836  else // set UID
6837  {
6838  __COUT__
6839  << "Reusing row " << row << " current-UID="
6840  << typeTable.tableView_
6841  ->getDataView()[row]
6842  [typeTable.tableView_->getColUID()]
6843  << " as (temporarily to basename if multinode) new-UID="
6844  << nodeName << __E__;
6845  typeTable.tableView_
6846  ->setValueAsString( //if single record, this renaming is final; if multi record, this renaming to basename is temporary
6847  nodeName,
6848  row,
6849  typeTable.tableView_->getColUID());
6850  }
6851  __COUTV__(row);
6852 
6853  // remove from delete map
6854  if(row < maxRowToDelete)
6855  deleteRecordMap[row] = false;
6856 
6857  __COUTV__(StringMacros::mapToString(
6858  processTypes_.mapToLinkGroupIDColumn_));
6859 
6860  // set GroupID
6861  typeTable.tableView_->setValueAsString(
6862  artdaqSupervisorTable.tableView_
6863  ->getDataView()[artdaqSupervisorRow]
6864  [artdaqSupervisorTable.tableView_->findCol(
6865  processTypes_.mapToLinkGroupIDColumn_
6866  .at(nodeTypePair.first))],
6867  row,
6868  typeTable.tableView_->findCol(
6869  processTypes_.mapToGroupIDColumn_.at(
6870  nodeTypePair.first)));
6871  }
6872  else if(i == 1) // status
6873  {
6874  // enable/disable the target row
6875  typeTable.tableView_->setValueAsString(
6876  nodePair.second[i],
6877  row,
6878  typeTable.tableView_->getColStatus());
6879  }
6880  else if(i == 2) // hostname
6881  {
6882  // set hostname
6883  hostname = nodePair.second[i];
6884  typeTable.tableView_->setValueAsString(
6885  hostname,
6886  row,
6887  typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME));
6888  }
6889  else if(i == 3) // subsystemName
6890  {
6891  // set subsystemName
6892  if(nodePair.second[i] != "" &&
6893  nodePair.second[i] !=
6894  TableViewColumnInfo::DATATYPE_STRING_DEFAULT &&
6895  nodePair.second[i] !=
6896  TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT)
6897  {
6898  // real subsystem?
6899  if(subsystemObjectMap.find(nodePair.second[i]) ==
6900  subsystemObjectMap.end())
6901  {
6902  __SS__ << "Illegal subsystem '" << nodePair.second[i]
6903  << "' mismatch!" << __E__;
6904  __SS_THROW__;
6905  }
6906 
6907  typeTable.tableView_->setValueAsString(
6908  ARTDAQ_SUBSYSTEM_TABLE,
6909  row,
6910  typeTable.tableView_->findCol(
6911  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
6912  typeTable.tableView_->setValueAsString(
6913  nodePair.second[i],
6914  row,
6915  typeTable.tableView_->findCol(
6916  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID));
6917  }
6918  else // no subsystem (i.e. default subsystem)
6919  {
6920  typeTable.tableView_->setValueAsString(
6921  TableViewColumnInfo::DATATYPE_LINK_DEFAULT,
6922  row,
6923  typeTable.tableView_->findCol(
6924  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
6925  }
6926  }
6927  else if(
6928  i == 4 || i == 5 || i == 6 ||
6929  i ==
6930  7) //(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)
6931  {
6932  // fill multi-node and array hostname info to empty
6933  // then handle after all parameters in hand.
6934 
6935  __COUT__ << "Handling printer syntax i=" << i << __E__;
6936 
6937  std::vector<std::string> printerSyntaxArr =
6938  StringMacros::getVectorFromString(nodePair.second[i],
6939  {','} /*delimiter*/);
6940 
6941  if(printerSyntaxArr.size() == 2) // consider if fixed value
6942  {
6943  if(printerSyntaxArr[0] ==
6944  "nnfw") // then node name fixed width
6945  {
6946  sscanf(printerSyntaxArr[1].c_str(),
6947  "%u",
6948  &nodeNameFixedWidth);
6949  __COUTV__(nodeNameFixedWidth);
6950  continue;
6951  }
6952  else if(printerSyntaxArr[0] ==
6953  "hnfw") // then hostname fixed width
6954  {
6955  sscanf(printerSyntaxArr[1].c_str(),
6956  "%u",
6957  &hostnameFixedWidth);
6958  __COUTV__(hostnameFixedWidth);
6959  continue;
6960  }
6961  }
6962 
6963  // unsigned int count = 0;
6964  for(auto& printerSyntaxValue : printerSyntaxArr)
6965  {
6966  __COUTV__(printerSyntaxValue);
6967 
6968  std::vector<std::string> printerSyntaxRange =
6969  StringMacros::getVectorFromString(printerSyntaxValue,
6970  {'-'} /*delimiter*/);
6971  if(printerSyntaxRange.size() == 0 ||
6972  printerSyntaxRange.size() > 2)
6973  {
6974  __SS__ << "Illegal multi-node printer syntax string '"
6975  << printerSyntaxValue << "!'" << __E__;
6976  __SS_THROW__;
6977  }
6978  else if(printerSyntaxRange.size() == 1)
6979  {
6980  // unsigned int index;
6981  __COUTV__(printerSyntaxRange[0]);
6982  // sscanf(printerSyntaxRange[0].c_str(), "%u", &index);
6983  //__COUTV__(index);
6984 
6985  if(i == 4 /*nodeArrayString*/)
6986  nodeIndices.push_back(printerSyntaxRange[0]);
6987  else
6988  hostnameIndices.push_back(printerSyntaxRange[0]);
6989  }
6990  else // printerSyntaxRange.size() == 2
6991  {
6992  unsigned int lo, hi;
6993  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
6994  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
6995  if(hi < lo) // swap
6996  {
6997  lo = hi;
6998  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
6999  }
7000  for(; lo <= hi; ++lo)
7001  {
7002  __COUTVS__(5, lo);
7003  if(i == 4 /*nodeArrayString*/)
7004  nodeIndices.push_back(std::to_string(lo));
7005  else
7006  hostnameIndices.push_back(std::to_string(lo));
7007  }
7008  }
7009  }
7010  }
7011  else
7012  {
7013  __SS__ << "Unexpected parameter[" << i << " '"
7014  << nodePair.second[i] << "' for node " << nodePair.first
7015  << "!" << __E__;
7016  __SS_THROW__;
7017  }
7018  } // end node parameter loop
7019 
7020  __COUTV__(nodeIndices.size());
7021  __COUTV__(hostnameIndices.size());
7022 
7023  if(hostnameIndices.size()) // handle hostname array
7024  {
7025  if(hostnameIndices.size() != nodeIndices.size())
7026  {
7027  __SS__ << "Illegal associated hostname array has count "
7028  << hostnameIndices.size()
7029  << " which is not equal to the node count "
7030  << nodeIndices.size() << "!" << __E__;
7031  __SS_THROW__;
7032  }
7033  }
7034 
7035  if(nodeIndices.size()) // handle multi-node instances
7036  {
7037  unsigned int hostnameCol =
7038  typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME);
7039  // Steps:
7040  // first instance takes current row,
7041  // then copy for remaining instances
7042 
7043  std::vector<std::string> namePieces =
7045  nodePair.first.substr(0, nodePair.first.find(";status=")),
7046  {'*'} /*delimiter*/);
7047  __COUTV__(StringMacros::vectorToString(namePieces));
7048 
7049  if(namePieces.size() < 2)
7050  {
7051  __SS__
7052  << "Illegal multi-node name template - please use * to "
7053  "indicate where the multi-node index should be inserted!"
7054  << __E__;
7055  __SS_THROW__;
7056  }
7057 
7058  std::vector<std::string> hostnamePieces;
7059  if(hostnameIndices.size()) // handle hostname array
7060  {
7061  hostnamePieces = StringMacros::getVectorFromString(
7062  hostname, {'*'} /*delimiter*/);
7063  __COUTV__(StringMacros::vectorToString(hostnamePieces));
7064 
7065  if(hostnamePieces.size() < 2)
7066  {
7067  __SS__
7068  << "Illegal hostname array template - please use * to "
7069  "indicate where the hostname index should be inserted!"
7070  << __E__;
7071  __SS_THROW__;
7072  }
7073  }
7074 
7075  bool isFirst = true;
7076  unsigned int lastArtRow = TableView::INVALID;
7077  for(unsigned int i = 0; i < nodeIndices.size(); ++i)
7078  {
7079  std::string name = namePieces[0];
7080  std::string nodeNameIndex;
7081  for(unsigned int p = 1; p < namePieces.size(); ++p)
7082  {
7083  nodeNameIndex = nodeIndices[i];
7084  if(nodeNameFixedWidth > 1)
7085  {
7086  if(nodeNameIndex.size() > nodeNameFixedWidth)
7087  {
7088  // allow _clone suffix — GUI branching creates indices like "00_clone"
7089  if(nodeNameIndex.find("_clone") == std::string::npos)
7090  {
7091  __SS__ << "Illegal node name index '"
7092  << nodeNameIndex
7093  << "' - length is longer than fixed width "
7094  "requirement of "
7095  << nodeNameFixedWidth << "!" << __E__;
7096  __SS_THROW__;
7097  }
7098  }
7099 
7100  // 0 prepend as needed
7101  while(nodeNameIndex.size() < nodeNameFixedWidth)
7102  nodeNameIndex = "0" + nodeNameIndex;
7103  } // end fixed width handling
7104 
7105  name += nodeNameIndex + namePieces[p];
7106  }
7107  __COUTV__(name);
7108 
7109  if(hostnamePieces.size())
7110  {
7111  hostname = hostnamePieces[0];
7112  std::string hostnameIndex;
7113  for(unsigned int p = 1; p < hostnamePieces.size(); ++p)
7114  {
7115  hostnameIndex = hostnameIndices[i];
7116  if(hostnameFixedWidth > 1)
7117  {
7118  if(hostnameIndex.size() > hostnameFixedWidth)
7119  {
7120  // allow _clone suffix — GUI branching creates indices like "00_clone"
7121  if(hostnameIndex.find("_clone") ==
7122  std::string::npos)
7123  {
7124  __SS__ << "Illegal hostname index '"
7125  << hostnameIndex
7126  << "' - length is longer than fixed "
7127  "width "
7128  "requirement of "
7129  << hostnameFixedWidth << "!" << __E__;
7130  __SS_THROW__;
7131  }
7132  }
7133 
7134  // 0 prepend as needed
7135  while(hostnameIndex.size() < hostnameFixedWidth)
7136  hostnameIndex = "0" + hostnameIndex;
7137  } // end fixed width handling
7138 
7139  hostname += hostnameIndex + hostnamePieces[p];
7140  }
7141  __COUTV__(hostname);
7142  }
7143  // else use hostname from above
7144 
7145  if(isFirst) // take current row
7146  {
7147  __COUTT__
7148  << author << "... Replacing row UID '"
7149  << typeTable.tableView_
7150  ->getDataView()[row]
7151  [typeTable.tableView_->getColUID()]
7152  << "' with UID '" << name << "'" << __E__;
7153 
7154  // remove from delete map
7155  if(row < maxRowToDelete)
7156  deleteRecordMap[row] = false;
7157  }
7158  else // copy row
7159  {
7160  __COUTT__
7161  << author << "... Copying row UID '"
7162  << typeTable.tableView_
7163  ->getDataView()[row]
7164  [typeTable.tableView_->getColUID()]
7165  << "' to UID '" << name << "'" << __E__;
7166  unsigned int copyRow = typeTable.tableView_->copyRows(
7167  author,
7168  *(typeTable.tableView_),
7169  row,
7170  1 /*srcRowsToCopy*/,
7171  -1 /*destOffsetRow*/,
7172  true /*generateUniqueDataColumns*/);
7173 
7174  // remove from delete map
7175  if(row < maxRowToDelete)
7176  deleteRecordMap[copyRow] = false;
7177  row = copyRow;
7178  }
7179 
7180  typeTable.tableView_->setValueAsString(
7181  name, row, typeTable.tableView_->getColUID());
7182  typeTable.tableView_->setValueAsString(
7183  hostname, row, hostnameCol);
7184  //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
7185  typeTable.tableView_->setValueAsString(
7186  TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT,
7187  row,
7188  commentCol);
7189  typeTable.tableView_->setValueAsString(author, row, authorCol);
7190  typeTable.tableView_->setValue(time(0), row, timestampCol);
7191 
7192  __COUTTV__(typeTable
7193  .tableView_ //comment
7194  ->getDataView()[row][commentCol]);
7195  __COUTTV__(typeTable
7196  .tableView_ //author
7197  ->getDataView()[row][authorCol]);
7198  __COUTTV__(typeTable
7199  .tableView_ //creation time
7200  ->getDataView()[row][timestampCol]);
7201  // Strategy:
7202  // - Take values from best original node match
7203  // - Then overwrite with same values
7204  // - Then overwrite with embedded name values
7205  // - If name matches exactly the original name, then keep Comment, Author, and CreationTime
7206  //i.e., Customize row based on original value map, originalMultinodeSameSiblingValues and originalMultinodeAllSiblingEmbeddedName and originalMultinodeAllSiblingEmbeddedPrinterIndex
7207  {
7208  //find highest score match to original node
7209  __COUT__
7210  << "Looking for best original node match for row=" << row
7211  << " UID='" << name << "'" << __E__;
7212  size_t bestScore = 0;
7213  std::string bestOriginalNodeName;
7214  for(const auto& originalNodePair : originalMultinodeValues)
7215  {
7216  if(originalNodePair.second.find(
7217  ORIG_MAP_ART_PROC_NAME_COL) !=
7218  originalNodePair.second.end())
7219  __COUTTV__(originalNodePair.second.at(
7220  ORIG_MAP_ART_PROC_NAME_COL));
7221  size_t score = 0;
7222  for(size_t c = 0, d = 0;
7223  c < originalNodePair.first.size() && d < name.size();
7224  ++c, ++d)
7225  {
7226  if(name[d] == originalNodePair.first[c])
7227  ++score;
7228  else if(d + 1 < name.size() &&
7229  name[d + 1] == originalNodePair.first[c])
7230  --c; //rewind one for dropped character
7231  else if(c + 1 < originalNodePair.first.size() &&
7232  name[d] == originalNodePair.first[c + 1])
7233  --d; //rewind one for dropped character
7234  }
7235  if(originalNodePair.first.size() == name.size())
7236  ++score;
7237  __COUTVS__(2, score);
7238  if(score > bestScore)
7239  {
7240  bestOriginalNodeName = originalNodePair.first;
7241  bestScore = score;
7242  __COUTVS__(2, bestOriginalNodeName);
7243  __COUTVS__(2, bestScore);
7244  }
7245  } //end scoring loop for best match in originalMultinodeValues
7246 
7247  bool exactMatch = (bestOriginalNodeName == name);
7248  bool needToHandleArtProcessName = false;
7249  std::string artProcessName;
7250 
7251  if(exactMatch ||
7252  originalMultinodeValues.find(bestOriginalNodeName) !=
7253  originalMultinodeValues.end())
7254  {
7255  __COUT__ << "Populating original multinode value from '"
7256  << bestOriginalNodeName << "' into '" << name
7257  << ".'" << __E__;
7258 
7259  for(const auto& valuePair :
7260  originalMultinodeValues.at(bestOriginalNodeName))
7261  {
7262  //(keep new creation time always!) if not exact match then keep new meta info and skip Comment, Author, and CreationTime
7263  if(!exactMatch && (valuePair.first == commentCol ||
7264  valuePair.first == authorCol ||
7265  valuePair.first == timestampCol))
7266  {
7267  __COUTT__
7268  << "Not exact node name match, so keeping "
7269  "default meta info for node: "
7270  << name << "[" << row << "]["
7271  << valuePair.first
7272  << "] /= " << valuePair.second << " keep= "
7273  << typeTable.tableView_
7274  ->getDataView()[row][valuePair.first]
7275  << __E__;
7276  continue;
7277  }
7278 
7279  __COUTT__ << "Customizing node: " << name << "["
7280  << row << "][" << valuePair.first
7281  << "] = " << valuePair.second << __E__;
7282  //handle special columns, otherwise normal columns in type table
7283  if(valuePair.first == ORIG_MAP_ART_PROC_NAME_COL)
7284  {
7285  __COUTT__ << "NEED Special art Process Name "
7286  "column value: "
7287  << valuePair.second << __E__;
7288  needToHandleArtProcessName = true;
7289  artProcessName = valuePair.second;
7290  continue;
7291  artTable->tableView_->setValueAsString(
7292  valuePair.second, row, artProcessNameCol);
7293  }
7294  else
7295  typeTable.tableView_->setValueAsString(
7296  valuePair.second, row, valuePair.first);
7297  }
7298  }
7299  else
7300  __COUT__ << "Did not find '" << name
7301  << "' in original value cache. Looking for "
7302  "bestOriginalNodeName="
7303  << bestOriginalNodeName << __E__;
7304 
7305  __COUTV__(exactMatch);
7306  if(!exactMatch) //not exact match, so apply sibling rules
7307  {
7308  if(originalMultinodeSameSiblingValues.find(
7309  nodePair.first) !=
7310  originalMultinodeSameSiblingValues.end())
7311  {
7312  __COUT__ << "Applying multinode sibling same value "
7313  "rules for row="
7314  << row << " UID='" << name << "'" << __E__;
7315  for(const auto& sameValuePair :
7316  originalMultinodeSameSiblingValues.at(
7317  nodePair.first))
7318  {
7319  if(!sameValuePair.second.first)
7320  continue;
7321  __COUTT__
7322  << "Found originalMultinodeSameSiblingValues["
7323  << nodePair.first << "]["
7324  << sameValuePair.first /* col */ << "] = "
7325  << sameValuePair.second.first << " --> "
7326  << sameValuePair.second.second << __E__;
7327 
7328  //handle special columns, otherwise normal columns in type table
7329  if(sameValuePair.first ==
7330  ORIG_MAP_ART_PROC_NAME_COL)
7331  {
7332  __COUTT__ << "NEED Special art Process Name "
7333  "column value: "
7334  << sameValuePair.second.second
7335  << __E__;
7336  needToHandleArtProcessName = true;
7337  artProcessName = sameValuePair.second.second;
7338  continue;
7339  artTable->tableView_->setValueAsString(
7340  sameValuePair.second.second,
7341  row,
7342  artProcessNameCol);
7343  }
7344  else
7345  typeTable.tableView_->setValueAsString(
7346  sameValuePair.second.second,
7347  row,
7348  sameValuePair.first);
7349  } //end loop to apply multinode same sibling values
7350  }
7351 
7352  //do originalMultinodeAllSiblingEmbeddedPrinterIndex before originalMultinodeAllSiblingEmbeddedName, so that originalMultinodeAllSiblingEmbeddedName has priority
7353  if(originalMultinodeAllSiblingEmbeddedPrinterIndex.find(
7354  nodePair.first) !=
7355  originalMultinodeAllSiblingEmbeddedPrinterIndex.end())
7356  {
7357  __COUT__ << "Applying multinode sibling embbeded "
7358  "printer syntax index rules for row="
7359  << row << " UID='" << name
7360  << "' and printer index='" << nodeNameIndex
7361  << "'" << __E__;
7362  for(const auto& embedValuePair :
7363  originalMultinodeAllSiblingEmbeddedPrinterIndex
7364  .at(nodePair.first))
7365  {
7366  if(!embedValuePair.second.first ||
7367  embedValuePair.second.second.size() < 2)
7368  continue;
7369  __COUTT__
7370  << "Found "
7371  "originalMultinodeAllSiblingEmbeddedPrinte"
7372  "rIndex["
7373  << nodePair.first << "]["
7374  << embedValuePair.first /* col */ << "] = "
7375  << embedValuePair.second.first << " --> "
7377  embedValuePair.second.second)
7378  << __E__;
7379  std::string embedValue =
7381  embedValuePair.second.second,
7382  nodeNameIndex);
7383  __COUTTV__(embedValue);
7384 
7385  //handle special columns, otherwise normal columns in type table
7386  if(embedValuePair.first ==
7387  ORIG_MAP_ART_PROC_NAME_COL)
7388  {
7389  __COUTT__ << "NEED Special art Process Name "
7390  "column value: "
7391  << embedValue << __E__;
7392  needToHandleArtProcessName = true;
7393  artProcessName = embedValue;
7394  continue;
7395  artTable->tableView_->setValueAsString(
7396  embedValue, row, artProcessNameCol);
7397  }
7398  else
7399  typeTable.tableView_->setValueAsString(
7400  embedValue, row, embedValuePair.first);
7401  } //end loop to apply multinode same sibling values
7402  }
7403 
7404  if(originalMultinodeAllSiblingEmbeddedName.find(
7405  nodePair.first) !=
7406  originalMultinodeAllSiblingEmbeddedName.end())
7407  {
7408  __COUT__ << "Applying multinode sibling embbeded "
7409  "name rules for row="
7410  << row << " UID='" << name << "'" << __E__;
7411  for(const auto& embedValuePair :
7412  originalMultinodeAllSiblingEmbeddedName.at(
7413  nodePair.first))
7414  {
7415  if(!embedValuePair.second.first ||
7416  embedValuePair.second.second.size() < 2)
7417  continue;
7418  __COUTT__
7419  << "Found "
7420  "originalMultinodeAllSiblingEmbeddedName["
7421  << nodePair.first << "]["
7422  << embedValuePair.first /* col */ << "] = "
7423  << embedValuePair.second.first << " --> "
7425  embedValuePair.second.second)
7426  << __E__;
7427  std::string embedValue =
7429  embedValuePair.second.second, name);
7430  __COUTTV__(embedValue);
7431 
7432  //handle special columns, otherwise normal columns in type table
7433  if(embedValuePair.first ==
7434  ORIG_MAP_ART_PROC_NAME_COL)
7435  {
7436  __COUTT__ << "NEED Special art Process Name "
7437  "column value: "
7438  << embedValue << __E__;
7439  needToHandleArtProcessName = true;
7440  artProcessName = embedValue;
7441  continue;
7442  artTable->tableView_->setValueAsString(
7443  embedValue, row, artProcessNameCol);
7444  }
7445  else
7446  typeTable.tableView_->setValueAsString(
7447  embedValue, row, embedValuePair.first);
7448  } //end loop to apply multinode same sibling values
7449  }
7450 
7451  __COUTV__(needToHandleArtProcessName);
7452  if(needToHandleArtProcessName)
7453  {
7454  __COUTT__ << "Special art Process Name column value: "
7455  << artProcessName << __E__;
7456  //need to find row or make row for art record
7457  std::string artRecord =
7458  typeTable.tableView_->getDataView()
7459  [row][typeTable.tableView_->findCol(
7460  ARTDAQTableBase::colARTDAQNotReader_
7461  .colLinkToArtUID_)];
7462  __COUTTV__(artRecord);
7463 
7464  const unsigned int artCommentCol =
7465  artTable->tableView_->findColByType(
7466  TableViewColumnInfo::TYPE_COMMENT);
7467  const unsigned int artAuthorCol =
7468  artTable->tableView_->findColByType(
7469  TableViewColumnInfo::TYPE_AUTHOR);
7470  const unsigned int artTimestampCol =
7471  artTable->tableView_->findColByType(
7472  TableViewColumnInfo::TYPE_TIMESTAMP);
7473 
7474  unsigned int artRow = artTable->tableView_->findRow(
7475  artTable->tableView_->getColUID(),
7476  artRecord,
7477  0 /* offsetRow */,
7478  true /* doNotThrow*/);
7479  __COUTTV__(artRow);
7480  if(artRow == TableView::INVALID) //need to make row!
7481  {
7482  __COUTT__ << "Need to make art Process record... "
7483  "artRecord="
7484  << artRecord << __E__;
7485 
7486  //change lastArtRow to best match's art row
7487  {
7488  __COUTTV__(bestOriginalNodeName);
7489  const unsigned int bestMatchRow =
7490  typeTable.tableView_->findRow(
7491  typeTable.tableView_->getColUID(),
7492  bestOriginalNodeName);
7493  __COUTTV__(bestMatchRow);
7494 
7495  std::string bestMatchArtRecord =
7496  typeTable.tableView_->getDataView()
7497  [bestMatchRow]
7498  [typeTable.tableView_->findCol(
7499  ARTDAQTableBase::
7500  colARTDAQNotReader_
7501  .colLinkToArtUID_)];
7502  __COUTTV__(bestMatchArtRecord);
7503 
7504  unsigned int bestMatchArtRow =
7505  artTable->tableView_->findRow(
7506  artTable->tableView_->getColUID(),
7507  bestMatchArtRecord,
7508  0 /* offsetRow */,
7509  true /* doNotThrow*/);
7510  __COUTTV__(bestMatchArtRow);
7511  if(bestMatchArtRow !=
7512  TableView::
7513  INVALID) //found best match's art record
7514  lastArtRow =
7515  bestMatchArtRow; //use best match's art record for copy
7516  __COUTTV__(lastArtRow);
7517  }
7518 
7519  if(lastArtRow != TableView::INVALID)
7520  {
7521  __COUTT__ << "Copying art Process record... "
7522  "from lastArtRow="
7523  << lastArtRow << __E__;
7524  unsigned int copyRow =
7525  artTable->tableView_->copyRows(
7526  author,
7527  *(artTable->tableView_),
7528  lastArtRow,
7529  1 /*srcRowsToCopy*/,
7530  -1 /*destOffsetRow*/,
7531  true /*generateUniqueDataColumns*/);
7532  artTable->tableView_->setValueAsString(
7533  artRecord,
7534  copyRow,
7535  artTable->tableView_->getColUID());
7536  artRow = copyRow;
7537 
7538  //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
7539  artTable->tableView_->setValueAsString(
7541  DATATYPE_COMMENT_DEFAULT,
7542  artRow,
7543  artCommentCol);
7544  artTable->tableView_->setValueAsString(
7545  author, artRow, artAuthorCol);
7546  artTable->tableView_->setValue(
7547  time(0), artRow, artTimestampCol);
7548  }
7549  else
7550  {
7551  __COUTT__ << "Creating art Process record... "
7552  "artRecord="
7553  << artRecord << __E__;
7554 
7555  artRow = artTable->tableView_->addRow(
7556  author,
7557  true /*incrementUniqueData*/,
7558  artRecord);
7559  }
7560  __COUTT__
7561  << "Made art Process record... artRecord="
7562  << artRecord << __E__;
7563  } //end making row
7564 
7565  __COUTT__ << "Modify art Process record based on "
7566  "sibling rules... artRecord="
7567  << artRecord
7568  << " artProcessName=" << artProcessName
7569  << __E__;
7570 
7571  artTable->tableView_->setValueAsString(
7572  artProcessName, artRow, artProcessNameCol);
7573  lastArtRow = artRow;
7574  }
7575  } //end applying sibling value rules
7576  else if(
7577  needToHandleArtProcessName) //get lastArtRow for future multirecord siblings
7578  {
7579  std::string artRecord =
7580  typeTable.tableView_->getDataView()
7581  [row][typeTable.tableView_->findCol(
7582  ARTDAQTableBase::colARTDAQNotReader_
7583  .colLinkToArtUID_)];
7584  __COUTTV__(artRecord);
7585  unsigned int artRow = artTable->tableView_->findRow(
7586  artTable->tableView_->getColUID(),
7587  artRecord,
7588  0 /* offsetRow */,
7589  true /* doNotThrow*/);
7590  __COUTTV__(artRow);
7591  if(artRow !=
7592  TableView::INVALID) //found valid art record row
7593  lastArtRow = artRow;
7594  }
7595 
7596  __COUTTV__(lastArtRow);
7597 
7598  if(TTEST(1))
7599  {
7600  __COUTTV__(row);
7601  if(row < maxRowToDelete)
7602  __COUTTV__(deleteRecordMap[row]);
7603 
7604  __COUTTV__(typeTable
7605  .tableView_ //comment
7606  ->getDataView()[row][commentCol]);
7607  __COUTTV__(typeTable
7608  .tableView_ //author
7609  ->getDataView()[row][authorCol]);
7610  __COUTTV__(typeTable
7611  .tableView_ //creation time
7612  ->getDataView()[row][timestampCol]);
7613  }
7614  } // end copy and customize row handling
7615 
7616  isFirst = false;
7617  } // end multi-node loop
7618  } // end multi-node handling
7619  } // end node record loop
7620 
7621  { // delete record handling
7622  __COUT__ << "Deleting '" << nodeTypePair.first
7623  << "' records not specified..." << __E__;
7624 
7625  // unsigned int row;
7626  std::set<unsigned int> orderedRowSet; // need to delete in reverse order
7627  for(auto& deletePair : deleteRecordMap)
7628  {
7629  if(!deletePair.second)
7630  {
7631  __COUTT__ << "Row keep = " << deletePair.first << __E__;
7632  continue; // only delete if true
7633  }
7634 
7635  __COUTT__ << "Row delete = " << deletePair.first << __E__;
7636  orderedRowSet.emplace(deletePair.first);
7637  }
7638 
7639  // delete elements in reverse order
7640  for(std::set<unsigned int>::reverse_iterator rit = orderedRowSet.rbegin();
7641  rit != orderedRowSet.rend();
7642  rit++)
7643  typeTable.tableView_->deleteRow(*rit);
7644 
7645  } // end delete record handling
7646 
7647  if(TTEST(1) && artTable)
7648  {
7649  std::stringstream ss;
7650  artTable->tableView_->print(ss);
7651  __COUT_MULTI__(1, ss.str());
7652  }
7653 
7654  if(hasArtProcessName && artTable)
7655  artTable->tableView_
7656  ->init(); // verify new art table modifications (throws runtime_errors)
7657 
7658  if(TTEST(1))
7659  {
7660  std::stringstream ss;
7661  typeTable.tableView_->print(ss);
7662  __COUT_MULTI__(1, ss.str());
7663  }
7664 
7665  typeTable.tableView_->init(); // verify new table (throws runtime_errors)
7666 
7667  } // end node type loop
7668 
7669  if(TTEST(1))
7670  {
7671  {
7672  std::stringstream ss;
7673  artdaqSupervisorTable.tableView_->print(ss);
7674  __COUT_MULTI__(1, ss.str());
7675  }
7676  {
7677  std::stringstream ss;
7678  artdaqSubsystemTable.tableView_->print(ss);
7679  __COUT_MULTI__(1, ss.str());
7680  }
7681  }
7682 
7683  artdaqSupervisorTable.tableView_
7684  ->init(); // verify new table (throws runtime_errors)
7685  artdaqSubsystemTable.tableView_
7686  ->init(); // verify new table (throws runtime_errors)
7687  }
7688  catch(...)
7689  {
7690  __COUT__ << "Table errors while creating ARTDAQ nodes. Erasing all newly "
7691  "created table versions."
7692  << __E__;
7693  throw; // re-throw
7694  } // end catch
7695 
7696  __COUT__ << "Edits complete for artdaq nodes and subsystems.. now save and activate "
7697  "groups, and update aliases!"
7698  << __E__;
7699 
7700  TableGroupKey newConfigurationGroupKey;
7701  if(0) //keep for debugging save process
7702  {
7703  __SS__ << "DEBUG blocking save!" << __E__;
7704  __SS_THROW__;
7705  }
7706  {
7707  std::string localAccumulatedWarnings;
7708  configGroupEdit.saveChanges(configGroupEdit.originalGroupName_,
7709  newConfigurationGroupKey,
7710  nullptr /*foundEquivalentGroupKey*/,
7711  true /*activateNewGroup*/,
7712  true /*updateGroupAliases*/,
7713  true /*updateTableAliases*/,
7714  nullptr /*newBackboneKey*/,
7715  nullptr /*foundEquivalentBackboneKey*/,
7716  &localAccumulatedWarnings);
7717  }
7718 
7719 } // end setAndActivateARTDAQSystem()
7720 
7721 //==============================================================================
7722 int ARTDAQTableBase::getSubsytemId(ConfigurationTree subsystemNode)
7723 {
7724  // using row forces a unique ID from 0 to rows-1
7725  // note: default no defined subsystem link to id=1; so add 2
7726 
7727  return subsystemNode.getNodeRow() + 2;
7728 } // end getSubsytemId()
7729 
7730 //==============================================================================
7732 void ARTDAQTableBase::addCommentWhitespace(std::ostream& os, size_t lineLength)
7733 {
7734  for(size_t i = 0; true; i += 20)
7735  {
7736  if(lineLength < FCL_COMMENT_POSITION + i) // pad to FCL_COMMENT_POSITION + i
7737  {
7738  os << std::string(FCL_COMMENT_POSITION + i - lineLength, ' ');
7739  break;
7740  }
7741  }
7742  os << " // ";
7743 } //end addCommentWhitespace()
7744 
7745 //==============================================================================
7746 std::string ARTDAQTableBase::getStructureAsJSON(
7747  const ConfigurationManager* /* configManager */)
7748 {
7749  if(fclMap_.size() == 0) //assume was not generated (not first )
7750  genFlatFHiCL();
7751  std::stringstream oss;
7752 
7753  oss << "{" << __E__;
7754 
7755  if(fclMap_.size() > 1)
7756  {
7757  // Multiple types - keep grouped structure
7758  bool firstType = true;
7759  for(const auto& typePairMap : fclMap_)
7760  {
7761  if(!firstType)
7762  oss << ",";
7763  oss << "\t\"" << getTypeString(typePairMap.first) << "\": {" << __E__;
7764 
7765  bool firstEntry = true;
7766  for(const auto& fclPair : typePairMap.second)
7767  {
7768  if(!firstEntry)
7769  oss << ",";
7770  oss << "\t\t\"" << fclPair.first << "\": \""
7771  << StringMacros::escapeJSONStringEntities(fclPair.second) << "\""
7772  << __E__;
7773  firstEntry = false;
7774  }
7775 
7776  oss << "\t}" << __E__;
7777  firstType = false;
7778  }
7779  }
7780  else
7781  {
7782  // Single type (normal case) - flat structure
7783  bool firstEntry = true;
7784  for(const auto& typePairMap : fclMap_)
7785  {
7786  for(const auto& fclPair : typePairMap.second)
7787  {
7788  if(!firstEntry)
7789  oss << ",";
7790  oss << "\t\"" << fclPair.first << "\": \""
7791  << StringMacros::escapeJSONStringEntities(fclPair.second) << "\""
7792  << __E__;
7793  firstEntry = false;
7794  }
7795  }
7796  }
7797 
7798  oss << "}" << __E__;
7799 
7800  return oss.str();
7801 } //end getStructureAsJSON()
7802 
7803 //==============================================================================
7807  size_t maxFragmentSizeBytes,
7808  size_t routingTimeoutMs,
7809  size_t routingRetryCount,
7810  ProgressBar* progressBar)
7811 {
7812  if(artdaqSupervisorNode.isDisconnected())
7813  {
7814  __SS__ << "ARTDAQ Supervisor node is disconnected while generating boot.txt "
7815  << "content." << __E__;
7816  __SS_THROW__;
7817  }
7818 
7819  const ARTDAQInfo& info = extractARTDAQInfo(artdaqSupervisorNode,
7820  false /*getStatusFalseNodes*/,
7821  false /*doWriteFHiCL*/,
7822  maxFragmentSizeBytes,
7823  routingTimeoutMs,
7824  routingRetryCount,
7825  progressBar);
7826 
7827  int debugLevel =
7828  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colDAQInterfaceDebugLevel_)
7829  .getValue<int>();
7830  std::string setupScript =
7831  artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colDAQSetupScript_).getValue();
7832 
7833  return getBootFileContentFromInfo(info, setupScript, debugLevel);
7834 } //end getBootFileContent()
7835 
7836 //==============================================================================
7840  const std::string& setupScript,
7841  int debugLevel)
7842 {
7843  std::stringstream o;
7844 
7845  o << "DAQ setup script: " << setupScript << std::endl;
7846  o << "debug level: " << debugLevel << std::endl;
7847  o << std::endl;
7848 
7849  if(info.subsystems.size() > 1)
7850  {
7851  for(auto& ss : info.subsystems)
7852  {
7853  if(ss.first == 0)
7854  continue;
7855  o << "Subsystem id: " << ss.first << std::endl;
7856  if(ss.second.destination != 0)
7857  {
7858  o << "Subsystem destination: " << ss.second.destination << std::endl;
7859  }
7860  for(auto& sss : ss.second.sources)
7861  {
7862  o << "Subsystem source: " << sss << std::endl;
7863  }
7864  if(ss.second.eventMode)
7865  {
7866  o << "Subsystem fragmentMode: False" << std::endl;
7867  }
7868  o << std::endl;
7869  }
7870  }
7871 
7872  for(auto& builder : info.processes.at(ARTDAQAppType::EventBuilder))
7873  {
7874  o << "EventBuilder host: " << builder.hostname << std::endl;
7875  o << "EventBuilder label: " << builder.label << std::endl;
7876  if(builder.subsystem != 1)
7877  {
7878  o << "EventBuilder subsystem: " << builder.subsystem << std::endl;
7879  }
7880  if(builder.allowed_processors != "")
7881  {
7882  o << "EventBuilder allowed_processors: " << builder.allowed_processors
7883  << std::endl;
7884  }
7885  o << std::endl;
7886  }
7887 
7888  for(auto& logger : info.processes.at(ARTDAQAppType::DataLogger))
7889  {
7890  o << "DataLogger host: " << logger.hostname << std::endl;
7891  o << "DataLogger label: " << logger.label << std::endl;
7892  if(logger.subsystem != 1)
7893  {
7894  o << "DataLogger subsystem: " << logger.subsystem << std::endl;
7895  }
7896  if(logger.allowed_processors != "")
7897  {
7898  o << "DataLogger allowed_processors: " << logger.allowed_processors
7899  << std::endl;
7900  }
7901  o << std::endl;
7902  }
7903 
7904  for(auto& dispatcher : info.processes.at(ARTDAQAppType::Dispatcher))
7905  {
7906  o << "Dispatcher host: " << dispatcher.hostname << std::endl;
7907  o << "Dispatcher label: " << dispatcher.label << std::endl;
7908  if(dispatcher.port != 0)
7909  {
7910  o << "Dispatcher port: " << dispatcher.port << std::endl;
7911  }
7912  if(dispatcher.subsystem != 1)
7913  {
7914  o << "Dispatcher subsystem: " << dispatcher.subsystem << std::endl;
7915  }
7916  if(dispatcher.allowed_processors != "")
7917  {
7918  o << "Dispatcher allowed_processors: " << dispatcher.allowed_processors
7919  << std::endl;
7920  }
7921  o << std::endl;
7922  }
7923 
7924  for(auto& rm : info.processes.at(ARTDAQAppType::RoutingManager))
7925  {
7926  o << "RoutingManager host: " << rm.hostname << std::endl;
7927  o << "RoutingManager label: " << rm.label << std::endl;
7928  if(rm.subsystem != 1)
7929  {
7930  o << "RoutingManager subsystem: " << rm.subsystem << std::endl;
7931  }
7932  if(rm.allowed_processors != "")
7933  {
7934  o << "RoutingManager allowed_processors: " << rm.allowed_processors
7935  << std::endl;
7936  }
7937  o << std::endl;
7938  }
7939 
7940  return o.str();
7941 } //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:1087
void deleteRow(int r)
Definition: TableView.cc:3572
unsigned int getColStatus(void) const
Definition: TableView.cc:1404
unsigned int findColByType(const std::string &type, unsigned int startingCol=0) const
Definition: TableView.cc:1993
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:1107
void init(void)
Definition: TableView.cc:195
unsigned int getColUID(void) const
Definition: TableView.cc:1319
unsigned int findCol(const std::string &name) const
Definition: TableView.cc:1970
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:3487
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)