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