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