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