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