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