otsdaq  v2_05_02_indev
ARTDAQTableBase.cc
1 #include <fhiclcpp/ParameterSet.h>
2 #include <fhiclcpp/detail/print_mode.h>
3 #include <fhiclcpp/intermediate_table.h>
4 #include <fhiclcpp/make_ParameterSet.h>
5 #include <fhiclcpp/parse.h>
6 
7 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h"
8 #include "otsdaq/TablePlugins/XDAQContextTable.h"
9 
10 #include <fstream> // for std::ofstream
11 #include <iostream> // std::cout
12 #include <typeinfo>
13 
14 #include "otsdaq/ProgressBar/ProgressBar.h"
15 
16 using namespace ots;
17 
18 #undef __MF_SUBJECT__
19 #define __MF_SUBJECT__ "ARTDAQTableBase"
20 
21 // clang-format off
22 
23 const std::string ARTDAQTableBase::ARTDAQ_FCL_PATH = std::string(__ENV__("USER_DATA")) + "/" + "ARTDAQConfigurations/";
24 
25 
26 const std::string ARTDAQTableBase::ARTDAQ_SUPERVISOR_CLASS = "ots::ARTDAQSupervisor";
27 const std::string ARTDAQTableBase::ARTDAQ_SUPERVISOR_TABLE = "ARTDAQSupervisorTable";
28 
29 const std::string ARTDAQTableBase::ARTDAQ_READER_TABLE = "ARTDAQBoardReaderTable";
30 const std::string ARTDAQTableBase::ARTDAQ_BUILDER_TABLE = "ARTDAQEventBuilderTable";
31 const std::string ARTDAQTableBase::ARTDAQ_LOGGER_TABLE = "ARTDAQDataLoggerTable";
32 const std::string ARTDAQTableBase::ARTDAQ_DISPATCHER_TABLE = "ARTDAQDispatcherTable";
33 const std::string ARTDAQTableBase::ARTDAQ_MONITOR_TABLE = "ARTDAQMonitorTable";
34 const std::string ARTDAQTableBase::ARTDAQ_ROUTER_TABLE = "ARTDAQRoutingMasterTable";
35 
36 const std::string ARTDAQTableBase::ARTDAQ_SUBSYSTEM_TABLE = "ARTDAQSubsystemTable";
37 
38 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME = "ExecutionHostname";
39 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK = "SubsystemLink";
40 const std::string ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID = "SubsystemLinkUID";
41 
42 
43 const int ARTDAQTableBase::NULL_SUBSYSTEM_DESTINATION = 0;
44 const std::string ARTDAQTableBase::NULL_SUBSYSTEM_DESTINATION_LABEL = "nullDestinationSubsystem";
45 
46 ARTDAQTableBase::ARTDAQInfo ARTDAQTableBase::info_;
47 
48 ARTDAQTableBase::ColARTDAQSupervisor ARTDAQTableBase::colARTDAQSupervisor_;
49 ARTDAQTableBase::ColARTDAQSubsystem ARTDAQTableBase::colARTDAQSubsystem_;
50 
51 //Note: processTypes_ must be instantiate after the static artdaq table names (to construct map in constructor)
52 ARTDAQTableBase::ProcessTypes ARTDAQTableBase::processTypes_;
53 
54 // clang-format on
55 
56 //==============================================================================
57 // TableBase
58 // If a valid string pointer is passed in accumulatedExceptions
59 // then allowIllegalColumns is set for InfoReader
60 // If accumulatedExceptions pointer = 0, then illegal columns throw std::runtime_error
61 // exception
62 ARTDAQTableBase::ARTDAQTableBase(std::string tableName, std::string* accumulatedExceptions /* =0 */) : TableBase(tableName, accumulatedExceptions)
63 {
64  // make directory just in case
65  mkdir((ARTDAQ_FCL_PATH).c_str(), 0755);
66 } // end constuctor()
67 
68 //==============================================================================
69 // ARTDAQTableBase
70 // Default constructor should never be used because table type is lost
71 ARTDAQTableBase::ARTDAQTableBase(void) : TableBase("ARTDAQTableBase")
72 {
73  __SS__ << "Should not call void constructor, table type is lost!" << __E__;
74  __SS_THROW__;
75 } // end illegal default constructor()
76 
77 //==============================================================================
78 ARTDAQTableBase::~ARTDAQTableBase(void) {} // end destructor()
79 
80 //==============================================================================
81 const std::string& ARTDAQTableBase::getTypeString(ARTDAQAppType type)
82 {
83  switch(type)
84  {
85  case ARTDAQAppType::EventBuilder:
86  return processTypes_.BUILDER;
87  case ARTDAQAppType::DataLogger:
88  return processTypes_.LOGGER;
89  case ARTDAQAppType::Dispatcher:
90  return processTypes_.DISPATCHER;
91  case ARTDAQAppType::BoardReader:
92  return processTypes_.READER;
93  case ARTDAQAppType::Monitor:
94  return processTypes_.MONITOR;
95  case ARTDAQAppType::RoutingMaster:
96  return processTypes_.ROUTER;
97  }
98  // return "UNKNOWN";
99  __SS__ << "Illegal translation attempt for type '" << (unsigned int)type << "'" << __E__;
100  __SS_THROW__;
101 } // end getTypeString()
102 
103 //==============================================================================
104 std::string ARTDAQTableBase::getFHICLFilename(ARTDAQAppType type, const std::string& name)
105 {
106  //__COUT__ << "Type: " << getTypeString(type) << " Name: " << name
107  //<< __E__;
108  std::string filename = ARTDAQ_FCL_PATH + getTypeString(type) + "-";
109  std::string uid = name;
110  for(unsigned int i = 0; i < uid.size(); ++i)
111  if((uid[i] >= 'a' && uid[i] <= 'z') || (uid[i] >= 'A' && uid[i] <= 'Z') || (uid[i] >= '0' && uid[i] <= '9')) // only allow alpha numeric in file name
112  filename += uid[i];
113 
114  filename += ".fcl";
115 
116  //__COUT__ << "fcl: " << filename << __E__;
117 
118  return filename;
119 } // end getFHICLFilename()
120 
121 //==============================================================================
122 std::string ARTDAQTableBase::getFlatFHICLFilename(ARTDAQAppType type, const std::string& name)
123 {
124  //__COUT__ << "Type: " << getTypeString(type) << " Name: " << name
125  // << __E__;
126  std::string filename = ARTDAQ_FCL_PATH + getTypeString(type) + "-";
127  std::string uid = name;
128  for(unsigned int i = 0; i < uid.size(); ++i)
129  if((uid[i] >= 'a' && uid[i] <= 'z') || (uid[i] >= 'A' && uid[i] <= 'Z') || (uid[i] >= '0' && uid[i] <= '9')) // only allow alpha numeric in file name
130  filename += uid[i];
131 
132  filename += "_flattened.fcl";
133 
134  //__COUT__ << "fcl: " << filename << __E__;
135 
136  return filename;
137 } // end getFlatFHICLFilename()
138 
139 //==============================================================================
140 void ARTDAQTableBase::flattenFHICL(ARTDAQAppType type, const std::string& name)
141 {
142  //__COUT__ << "flattenFHICL()" << __ENV__("FHICL_FILE_PATH") << __E__;
143 
144  std::string inFile = getFHICLFilename(type, name);
145  std::string outFile = getFlatFHICLFilename(type, name);
146 
147  //__COUTV__(inFile);
148  //__COUTV__(outFile);
149 
150  cet::filepath_lookup_nonabsolute policy("FHICL_FILE_PATH");
151 
152  fhicl::ParameterSet pset;
153 
154  try
155  {
156  fhicl::intermediate_table tbl;
157  fhicl::parse_document(inFile, policy, tbl);
158  fhicl::ParameterSet pset;
159  fhicl::make_ParameterSet(tbl, pset);
160 
161  std::ofstream ofs{outFile};
162  ofs << pset.to_indented_string(0); // , fhicl::detail::print_mode::annotated); // Only really useful for debugging
163  }
164  catch(cet::exception const& e)
165  {
166  __SS__ << "Failed to parse fhicl: " << e.what() << __E__;
167  __SS_THROW__;
168  }
169 } // end flattenFHICL()
170 
171 //==============================================================================
172 // insertParameters
173 // Inserts parameters in FHiCL outputs stream.
174 //
175 // onlyInsertAtTableParameters allows @table:: parameters only,
176 // so that calling code can do two passes (i.e. top of fcl block, @table:: only,
177 // and bottom of fcl block, ignore/skip @table:: as default behavior).
178 void ARTDAQTableBase::insertParameters(std::ostream& out,
179  std::string& tabStr,
180  std::string& commentStr,
181  ConfigurationTree parameterGroupLink,
182  const std::string& parameterPreamble,
183  bool onlyInsertAtTableParameters /*=false*/,
184  bool includeAtTableParameters /*=false*/)
185 {
186  // skip if link is disconnected
187  if(!parameterGroupLink.isDisconnected())
188  {
190  auto otherParameters = parameterGroupLink.getChildren();
191 
192  std::string key;
193  //__COUTV__(otherParameters.size());
194  for(auto& parameter : otherParameters)
195  {
196  key = parameter.second.getNode(parameterPreamble + "Key").getValue();
197 
198  // handle special keyword @table:: (which imports full tables, usually as
199  // defaults)
200  if(key.find("@table::") != std::string::npos)
201  {
202  // include @table::
203  if(onlyInsertAtTableParameters || includeAtTableParameters)
204  {
205  if(!parameter.second.status())
206  PUSHCOMMENT;
207 
208  OUT << key;
209 
210  // skip connecting : if special keywords found
211  OUT << parameter.second.getNode(parameterPreamble + "Value").getValue() << "\n";
212 
213  if(!parameter.second.status())
214  POPCOMMENT;
215  }
216  // else skip it
217 
218  continue;
219  }
220  // else NOT @table:: keyword parameter
221 
222  if(onlyInsertAtTableParameters)
223  continue; // skip all other types
224 
225  if(!parameter.second.status())
226  PUSHCOMMENT;
227 
228  OUT << key;
229 
230  // skip connecting : if special keywords found
231  if(key.find("#include") == std::string::npos)
232  OUT << ":";
233  OUT << parameter.second.getNode(parameterPreamble + "Value").getValue() << "\n";
234 
235  if(!parameter.second.status())
236  POPCOMMENT;
237  }
238  }
239  // else
240  // __COUT__ << "No parameters found" << __E__;
241 
242 } // end insertParameters()
243 
244 //==============================================================================
245 // insertModuleType
246 // Inserts module type field, with consideration for @table::
247 std::string ARTDAQTableBase::insertModuleType(std::ostream& out, std::string& tabStr, std::string& commentStr, ConfigurationTree moduleTypeNode)
248 {
249  std::string value = moduleTypeNode.getValue();
250 
251  OUT;
252  if(value.find("@table::") == std::string::npos)
253  out << "module_type: ";
254  out << value << "\n";
255 
256  return value;
257 } // end insertModuleType()
258 
259 //==============================================================================
260 // insertMetricsBlock
261 void ARTDAQTableBase::insertMetricsBlock(std::ostream& out, std::string& tabStr, std::string& commentStr, ConfigurationTree daqNode)
262 {
263  OUT << "\n\nmetrics: {\n";
264 
265  PUSHTAB;
266  auto metricsGroup = daqNode.getNode("daqMetricsLink");
267  if(!metricsGroup.isDisconnected())
268  {
269  auto metrics = metricsGroup.getChildren();
270 
271  for(auto& metric : metrics)
272  {
273  if(!metric.second.status())
274  PUSHCOMMENT;
275 
276  OUT << metric.second.getNode("metricKey").getValue() << ": {\n";
277  PUSHTAB;
278 
279  OUT << "metricPluginType: " << metric.second.getNode("metricPluginType").getValue() << "\n";
280  OUT << "level: " << metric.second.getNode("metricLevel").getValue() << "\n";
281 
282  auto metricParametersGroup = metric.second.getNode("metricParametersLink");
283  if(!metricParametersGroup.isDisconnected())
284  {
285  auto metricParameters = metricParametersGroup.getChildren();
286  for(auto& metricParameter : metricParameters)
287  {
288  if(!metricParameter.second.status())
289  PUSHCOMMENT;
290 
291  OUT << metricParameter.second.getNode("metricParameterKey").getValue() << ": "
292  << metricParameter.second.getNode("metricParameterValue").getValue() << "\n";
293 
294  if(!metricParameter.second.status())
295  POPCOMMENT;
296  }
297  }
298  POPTAB;
299  OUT << "}\n\n"; // end metric
300 
301  if(!metric.second.status())
302  POPCOMMENT;
303  }
304  }
305  POPTAB;
306  OUT << "}\n\n"; // end metrics
307 } // end insertMetricsBlock()
308 
309 //==============================================================================
310 void ARTDAQTableBase::outputBoardReaderFHICL(const ConfigurationTree& boardReaderNode,
311  size_t maxFragmentSizeBytes /* = DEFAULT_MAX_FRAGMENT_SIZE */,
312  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
313  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */)
314 {
315  /*
316  the file will look something like this:
317 
318  daq: {
319  fragment_receiver: {
320  mpi_sync_interval: 50
321 
322  # CommandableFragmentGenerator Table:
323  fragment_ids: []
324  fragment_id: -99 # Please define only one of these
325 
326  sleep_on_stop_us: 0
327 
328  requests_enabled: false # Whether to set up the socket for listening for
329  trigger messages request_mode: "Ignored" # Possible values are: Ignored, Single,
330  Buffer, Window
331 
332  data_buffer_depth_fragments: 1000
333  data_buffer_depth_mb: 1000
334 
335  request_port: 3001
336  request_address: "227.128.12.26" # Multicast request address
337 
338  request_window_offset: 0 # Request message contains tzero. Window will be from
339  tzero - offset to tzero + width request_window_width: 0 stale_request_timeout:
340  "0xFFFFFFFF" # How long to wait before discarding request messages that are outside
341  the available data request_windows_are_unique: true # If request windows are
342  unique, avoids a copy operation, but the same data point cannot be used for two
343  requests. If this is not anticipated, leave set to "true"
344 
345  separate_data_thread: false # MUST be true for triggers to be applied! If
346  triggering is not desired, but a separate readout thread is, set this to true,
347  triggers_enabled to false and trigger_mode to ignored. separate_monitoring_thread:
348  false # Whether a thread should be started which periodically calls checkHWStatus_,
349  a user-defined function which should be used to check hardware status registers and
350  report to MetricMan. poll_hardware_status: false # Whether checkHWStatus_ will be
351  called, either through the thread or at the start of getNext
352  hardware_poll_interval_us: 1000000 # If hardware monitoring thread is enabled,
353  how often should it call checkHWStatus_
354 
355 
356  # Generated Parameters:
357  generator: ToySimulator
358  fragment_type: TOY1
359  fragment_id: 0
360  board_id: 0
361  starting_fragment_id: 0
362  random_seed: 5780
363  sleep_on_stop_us: 500000
364 
365  # Generator-Specific Table:
366 
367  nADCcounts: 40
368 
369  throttle_usecs: 100000
370 
371  distribution_type: 1
372 
373  timestamp_scale_factor: 1
374 
375 
376  destinations: {
377  d2: { transferPluginType: MPI
378  destination_rank: 2
379  max_fragment_size_bytes: 2097152
380  host_map: [
381  {
382  host: "mu2edaq01.fnal.gov"
383  rank: 0
384  },
385  {
386  host: "mu2edaq01.fnal.gov"
387  rank: 1
388  }]
389  }
390  d3: { transferPluginType: MPI
391  destination_rank: 3
392  max_fragment_size_bytes: 2097152
393  host_map: [
394  {
395  host: "mu2edaq01.fnal.gov"
396  rank: 0
397  },
398  {
399  host: "mu2edaq01.fnal.gov"
400  rank: 1
401  }]
402  }
403 
404  }
405  }
406 
407  metrics: {
408  brFile: {
409  metricPluginType: "file"
410  level: 3
411  fileName: "/tmp/boardreader/br_%UID%_metrics.log"
412  uniquify: true
413  }
414  # ganglia: {
415  # metricPluginType: "ganglia"
416  # level: %{ganglia_level}
417  # reporting_interval: 15.0
418  #
419  # configFile: "/etc/ganglia/gmond.conf"
420  # group: "ARTDAQ"
421  # }
422  # msgfac: {
423  # level: %{mf_level}
424  # metricPluginType: "msgFacility"
425  # output_message_application_name: "ARTDAQ Metric"
426  # output_message_severity: 0
427  # }
428  # graphite: {
429  # level: %{graphite_level}
430  # metricPluginType: "graphite"
431  # host: "localhost"
432  # port: 20030
433  # namespace: "artdaq."
434  # }
435  }
436  }
437 
438  */
439 
440  std::string filename = getFHICLFilename(ARTDAQAppType::BoardReader, boardReaderNode.getValue());
441 
443  // generate xdaq run parameter file
444  std::fstream out;
445 
446  std::string tabStr = "";
447  std::string commentStr = "";
448 
449  out.open(filename, std::fstream::out | std::fstream::trunc);
450  if(out.fail())
451  {
452  __SS__ << "Failed to open ARTDAQ BoardReader fcl file: " << filename << __E__;
453  __SS_THROW__;
454  }
455 
456  //--------------------------------------
457  // header
458  OUT << "###########################################################" << __E__;
459  OUT << "#" << __E__;
460  OUT << "# artdaq reader fcl configuration file produced by otsdaq." << __E__;
461  OUT << "# Creation time: \t" << StringMacros::getTimestampString() << __E__;
462  OUT << "# Original filename: \t" << filename << __E__;
463  OUT << "# otsdaq-ARTDAQ Reader UID:\t" << boardReaderNode.getValue() << __E__;
464  OUT << "#" << __E__;
465  OUT << "###########################################################" << __E__;
466  OUT << "\n\n";
467 
468  // no primary link to table tree for reader node!
469  try
470  {
471  if(boardReaderNode.isDisconnected())
472  {
473  // create empty fcl
474  OUT << "{}\n\n";
475  out.close();
476  return;
477  }
478  }
479  catch(const std::runtime_error&)
480  {
481  //__COUT__ << "Ignoring error, assume this a valid UID node." << __E__;
482  // error is expected here for UIDs.. so just ignore
483  // this check is valuable if source node is a unique-Link node, rather than UID
484  }
485 
486  //--------------------------------------
487  // handle daq
488  OUT << "daq: {\n";
489 
490  // fragment_receiver
491  PUSHTAB;
492  OUT << "fragment_receiver: {\n";
493 
494  PUSHTAB;
495  OUT << "max_fragment_size_bytes: " << maxFragmentSizeBytes << "\n";
496  {
497  // plugin type and fragment data-type
498  OUT << "generator"
499  << ": " << boardReaderNode.getNode("daqGeneratorPluginType").getValue() << ("\t #daq generator plug-in type") << "\n";
500  OUT << "fragment_type"
501  << ": " << boardReaderNode.getNode("daqGeneratorFragmentType").getValue() << ("\t #generator data fragment type") << "\n\n";
502 
503  // shared and unique parameters
504  auto parametersLink = boardReaderNode.getNode("daqParametersLink");
505  if(!parametersLink.isDisconnected())
506  {
507  auto parameters = parametersLink.getChildren();
508  for(auto& parameter : parameters)
509  {
510  if(!parameter.second.status())
511  PUSHCOMMENT;
512 
513  // __COUT__ <<
514  // parameter.second.getNode("daqParameterKey").getValue() <<
515  // ": " <<
516  // parameter.second.getNode("daqParameterValue").getValue()
517  // <<
518  // "\n";
519 
520  auto comment = parameter.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT);
521  OUT << parameter.second.getNode("daqParameterKey").getValue() << ": " << parameter.second.getNode("daqParameterValue").getValue()
522  << (comment.isDefaultValue() ? "" : ("\t # " + comment.getValue())) << "\n";
523 
524  if(!parameter.second.status())
525  POPCOMMENT;
526  }
527  }
528  OUT << "\n"; // end daq board reader parameters
529  }
530 
531  OUT << "destinations: {\n";
532 
533  OUT << "}\n\n"; // end destinations
534 
535  OUT << "routing_table_config: {\n";
536  PUSHTAB;
537 
538  auto readerSubsystemID = 1;
539  auto readerSubsystemLink = boardReaderNode.getNode("SubsystemLink");
540  if(!readerSubsystemLink.isDisconnected())
541  {
542  readerSubsystemID = getSubsytemId(readerSubsystemLink);
543  }
544  if(info_.subsystems[readerSubsystemID].hasRoutingMaster)
545  {
546  OUT << "use_routing_master: true\n";
547  OUT << "routing_master_hostname: \"" << info_.subsystems[readerSubsystemID].routingMasterHost << "\"\n";
548  OUT << "table_update_port: 0\n";
549  OUT << "table_update_address: \"0.0.0.0\"\n";
550  OUT << "table_update_multicast_interface: \"0.0.0.0\"\n";
551  OUT << "table_acknowledge_port : 0\n";
552  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
553  OUT << "routing_retry_count: " << routingRetryCount << "\n";
554  }
555  else
556  {
557  OUT << "use_routing_master: false\n";
558  }
559 
560  POPTAB;
561  OUT << "}\n"; // end routing_table_config
562 
563  POPTAB;
564  OUT << "}\n\n"; // end fragment_receiver
565 
566  insertMetricsBlock(OUT, tabStr, commentStr, boardReaderNode);
567 
568  POPTAB;
569  OUT << "}\n\n"; // end daq
570 
571  out.close();
572 } // end outputReaderFHICL()
573 
574 //==============================================================================
575 // outputDataReceiverFHICL
576 // Note: currently selfRank and selfPort are unused by artdaq fcl
577 void ARTDAQTableBase::outputDataReceiverFHICL(const ConfigurationTree& receiverNode,
578  ARTDAQAppType appType,
579  size_t maxFragmentSizeBytes /* = DEFAULT_MAX_FRAGMENT_SIZE */,
580  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
581  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */)
582 {
583  std::string filename = getFHICLFilename(appType, receiverNode.getValue());
584 
586  // generate xdaq run parameter file
587  std::fstream out;
588 
589  std::string tabStr = "";
590  std::string commentStr = "";
591 
592  out.open(filename, std::fstream::out | std::fstream::trunc);
593  if(out.fail())
594  {
595  __SS__ << "Failed to open ARTDAQ fcl file: " << filename << __E__;
596  __SS_THROW__;
597  }
598 
599  //--------------------------------------
600  // header
601  OUT << "###########################################################" << __E__;
602  OUT << "#" << __E__;
603  OUT << "# artdaq " << getTypeString(appType) << " fcl configuration file produced by otsdaq." << __E__;
604  OUT << "# Creation time: \t" << StringMacros::getTimestampString() << __E__;
605  OUT << "# Original filename: \t" << filename << __E__;
606  OUT << "# otsdaq-ARTDAQ " << getTypeString(appType) << " UID:\t" << receiverNode.getValue() << __E__;
607  OUT << "#" << __E__;
608  OUT << "###########################################################" << __E__;
609  OUT << "\n\n";
610 
611  // no primary link to table tree for data receiver node!
612  try
613  {
614  if(receiverNode.isDisconnected())
615  {
616  // create empty fcl
617  OUT << "{}\n\n";
618  out.close();
619  return;
620  }
621  }
622  catch(const std::runtime_error&)
623  {
624  //__COUT__ << "Ignoring error, assume this a valid UID node." << __E__;
625  // error is expected here for UIDs.. so just ignore
626  // this check is valuable if source node is a unique-Link node, rather than UID
627  }
628 
629  //--------------------------------------
630  // handle preamble parameters
631  //_COUT__ << "Inserting preamble parameters..." << __E__;
632  insertParameters(out,
633  tabStr,
634  commentStr,
635  receiverNode.getNode("preambleParametersLink"),
636  "daqParameter" /*parameterType*/,
637  false /*onlyInsertAtTableParameters*/,
638  true /*includeAtTableParameters*/);
639 
640  //--------------------------------------
641  // handle daq
642  //__COUT__ << "Generating daq block..." << __E__;
643  auto daq = receiverNode.getNode("daqLink");
644  if(!daq.isDisconnected())
645  {
647  OUT << "daq: {\n";
648 
649  PUSHTAB;
650  if(appType == ARTDAQAppType::EventBuilder)
651  {
652  // event_builder
653  OUT << "event_builder: {\n";
654  }
655  else
656  {
657  // both datalogger and dispatcher use aggregator for now
658  OUT << "aggregator: {\n";
659  }
660 
661  PUSHTAB;
662 
663  OUT << "max_fragment_size_bytes: " << maxFragmentSizeBytes << "\n";
664 
665  if(appType == ARTDAQAppType::DataLogger)
666  {
667  OUT << "is_datalogger: true\n";
668  }
669  else if(appType == ARTDAQAppType::Dispatcher)
670  {
671  OUT << "is_dispatcher: true\n";
672  }
673 
674  //--------------------------------------
675  // handle ALL daq parameters
676  //__COUT__ << "Inserting DAQ Parameters..." << __E__;
677  insertParameters(out,
678  tabStr,
679  commentStr,
680  daq.getNode("daqParametersLink"),
681  "daqParameter" /*parameterType*/,
682  false /*onlyInsertAtTableParameters*/,
683  true /*includeAtTableParameters*/);
684 
685  if(appType == ARTDAQAppType::EventBuilder)
686  {
687  OUT << "routing_token_config: {\n";
688  PUSHTAB;
689 
690  auto builderSubsystemID = 1;
691  auto builderSubsystemLink = receiverNode.getNode("SubsystemLink");
692  if(!builderSubsystemLink.isDisconnected())
693  {
694  builderSubsystemID = getSubsytemId(builderSubsystemLink);
695  }
696 
697  if(info_.subsystems[builderSubsystemID].hasRoutingMaster)
698  {
699  OUT << "use_routing_master: true\n";
700  OUT << "routing_master_hostname: \"" << info_.subsystems[builderSubsystemID].routingMasterHost << "\"\n";
701  OUT << "routing_token_port: 0\n";
702  }
703  else
704  {
705  OUT << "use_routing_master: false\n";
706  }
707  POPTAB;
708  OUT << "}\n"; // end routing_token_config
709  }
710 
711  //__COUT__ << "Adding sources placeholder" << __E__;
712  OUT << "sources: {\n"
713  << "}\n\n"; // end sources
714 
715  POPTAB;
716  OUT << "}\n\n"; // end event builder
717 
718  insertMetricsBlock(OUT, tabStr, commentStr, daq);
719 
720  POPTAB;
721  OUT << "}\n\n"; // end daq
722  }
723 
724  //--------------------------------------
725  // handle art
726  //__COUT__ << "Filling art block..." << __E__;
727  auto art = receiverNode.getNode("artLink");
728  if(!art.isDisconnected())
729  {
730  OUT << "art: {\n";
731 
732  PUSHTAB;
733 
734  //--------------------------------------
735  // handle services
736  //__COUT__ << "Filling art.services" << __E__;
737  auto services = art.getNode("servicesLink");
738  if(!services.isDisconnected())
739  {
740  OUT << "services: {\n";
741 
742  PUSHTAB;
743 
744  //--------------------------------------
745  // handle services @table:: parameters
746  insertParameters(out,
747  tabStr,
748  commentStr,
749  services.getNode("ServicesParametersLink"),
750  "daqParameter" /*parameterType*/,
751  true /*onlyInsertAtTableParameters*/,
752  false /*includeAtTableParameters*/);
753 
754  OUT << "ArtdaqSharedMemoryServiceInterface: { service_provider: ArtdaqSharedMemoryService \n";
755 
756  OUT << "waiting_time: " << services.getNode("sharedMemoryWaitingTime").getValue() << "\n";
757  OUT << "resume_after_timeout: " << (services.getNode("sharedMemoryResumeAfterTimeout").getValue<bool>() ? "true" : "false") << "\n";
758  OUT << "}\n\n";
759 
760  OUT << "ArtdaqFragmentNamingServiceInterface: { service_provider: " << (services.getNode("fragmentNamingServiceProvider").getValue<std::string>())
761  << "}\n\n";
762 
763 
764  //--------------------------------------
765  // handle services NOT @table:: parameters
766  insertParameters(out,
767  tabStr,
768  commentStr,
769  services.getNode("ServicesParametersLink"),
770  "daqParameter" /*parameterType*/,
771  false /*onlyInsertAtTableParameters*/,
772  false /*includeAtTableParameters*/);
773 
774  POPTAB;
775  OUT << "}\n\n"; // end services
776  }
777 
778  //--------------------------------------
779  // handle outputs
780  //__COUT__ << "Filling art.outputs" << __E__;
781  auto outputs = art.getNode("outputsLink");
782  if(!outputs.isDisconnected())
783  {
784  OUT << "outputs: {\n";
785 
786  PUSHTAB;
787 
788  auto outputPlugins = outputs.getChildren();
789  for(auto& outputPlugin : outputPlugins)
790  {
791  if(!outputPlugin.second.status())
792  PUSHCOMMENT;
793 
794  OUT << outputPlugin.second.getNode("outputKey").getValue() << ": {\n";
795  PUSHTAB;
796 
797  std::string moduleType = insertModuleType(out, tabStr, commentStr, outputPlugin.second.getNode("outputModuleType"));
798 
799  //--------------------------------------
800  // handle ALL output parameters
801  insertParameters(out,
802  tabStr,
803  commentStr,
804  outputPlugin.second.getNode("outputModuleParameterLink"),
805  "outputParameter" /*parameterType*/,
806  false /*onlyInsertAtTableParameters*/,
807  true /*includeAtTableParameters*/);
808 
809  if(outputPlugin.second.getNode("outputModuleType").getValue() == "BinaryNetOutput" ||
810  outputPlugin.second.getNode("outputModuleType").getValue() == "RootNetOutput")
811  {
812  OUT << "destinations: {\n";
813  OUT << "}\n\n"; // end destinations
814  OUT << "routing_table_config: {\n";
815  PUSHTAB;
816 
817  auto builderSubsystemID = 1;
818  auto builderSubsystemLink = receiverNode.getNode("SubsystemLink");
819  if(!builderSubsystemLink.isDisconnected())
820  {
821  builderSubsystemID = getSubsytemId(builderSubsystemLink);
822  }
823  builderSubsystemID = info_.subsystems[builderSubsystemID].destination;
824  if(info_.subsystems[builderSubsystemID].hasRoutingMaster)
825  {
826  OUT << "use_routing_master: true\n";
827  OUT << "routing_master_hostname: \"" << info_.subsystems[builderSubsystemID].routingMasterHost << "\"\n";
828  OUT << "table_update_port: 0\n";
829  OUT << "table_update_address: \"0.0.0.0\"\n";
830  OUT << "table_update_multicast_interface: \"0.0.0.0\"\n";
831  OUT << "table_acknowledge_port : 0\n";
832  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
833  OUT << "routing_retry_count: " << routingRetryCount << "\n";
834  }
835  else
836  {
837  OUT << "use_routing_master: false\n";
838  }
839 
840  POPTAB;
841  OUT << "}\n"; // end routing_table_config
842  }
843 
844  POPTAB;
845  OUT << "}\n\n"; // end output module
846 
847  if(!outputPlugin.second.status())
848  POPCOMMENT;
849  }
850 
851  POPTAB;
852  OUT << "}\n\n"; // end outputs
853  }
854 
855  //--------------------------------------
856  // handle physics
857  //__COUT__ << "Filling art.physics" << __E__;
858  auto physics = art.getNode("physicsLink");
859  if(!physics.isDisconnected())
860  {
862  OUT << "physics: {\n";
863 
864  PUSHTAB;
865 
866  //--------------------------------------
867  // handle only @table:: physics parameters
868  insertParameters(out,
869  tabStr,
870  commentStr,
871  physics.getNode("physicsOtherParametersLink"),
872  "physicsParameter" /*parameterType*/,
873  true /*onlyInsertAtTableParameters*/,
874  false /*includeAtTableParameters*/);
875 
876  auto analyzers = physics.getNode("analyzersLink");
877  if(!analyzers.isDisconnected())
878  {
880  OUT << "analyzers: {\n";
881 
882  PUSHTAB;
883 
884  auto modules = analyzers.getChildren();
885  for(auto& module : modules)
886  {
887  if(!module.second.status())
888  PUSHCOMMENT;
889 
890  //--------------------------------------
891  // handle only @table:: analyzer parameters
892  insertParameters(out,
893  tabStr,
894  commentStr,
895  module.second.getNode("analyzerModuleParameterLink"),
896  "analyzerParameter" /*parameterType*/,
897  true /*onlyInsertAtTableParameters*/,
898  false /*includeAtTableParameters*/);
899 
900  OUT << module.second.getNode("analyzerKey").getValue() << ": {\n";
901  PUSHTAB;
902  insertModuleType(out, tabStr, commentStr, module.second.getNode("analyzerModuleType"));
903 
904  //--------------------------------------
905  // handle NOT @table:: producer parameters
906  insertParameters(out,
907  tabStr,
908  commentStr,
909  module.second.getNode("analyzerModuleParameterLink"),
910  "analyzerParameter" /*parameterType*/,
911  false /*onlyInsertAtTableParameters*/,
912  false /*includeAtTableParameters*/);
913 
914  POPTAB;
915  OUT << "}\n\n"; // end analyzer module
916 
917  if(!module.second.status())
918  POPCOMMENT;
919  }
920  POPTAB;
921  OUT << "}\n\n"; // end analyzer
922  }
923 
924  auto producers = physics.getNode("producersLink");
925  if(!producers.isDisconnected())
926  {
928  OUT << "producers: {\n";
929 
930  PUSHTAB;
931 
932  auto modules = producers.getChildren();
933  for(auto& module : modules)
934  {
935  if(!module.second.status())
936  PUSHCOMMENT;
937 
938  //--------------------------------------
939  // handle only @table:: producer parameters
940  insertParameters(out,
941  tabStr,
942  commentStr,
943  module.second.getNode("producerModuleParameterLink"),
944  "producerParameter" /*parameterType*/,
945  true /*onlyInsertAtTableParameters*/,
946  false /*includeAtTableParameters*/);
947 
948  OUT << module.second.getNode("producerKey").getValue() << ": {\n";
949  PUSHTAB;
950 
951  insertModuleType(out, tabStr, commentStr, module.second.getNode("producerModuleType"));
952 
953  //--------------------------------------
954  // handle NOT @table:: producer parameters
955  insertParameters(out,
956  tabStr,
957  commentStr,
958  module.second.getNode("producerModuleParameterLink"),
959  "producerParameter" /*parameterType*/,
960  false /*onlyInsertAtTableParameters*/,
961  false /*includeAtTableParameters*/);
962 
963  POPTAB;
964  OUT << "}\n\n"; // end producer module
965 
966  if(!module.second.status())
967  POPCOMMENT;
968  }
969  POPTAB;
970  OUT << "}\n\n"; // end producer
971  }
972 
973  auto filters = physics.getNode("filtersLink");
974  if(!filters.isDisconnected())
975  {
977  OUT << "filters: {\n";
978 
979  PUSHTAB;
980 
981  auto modules = filters.getChildren();
982  for(auto& module : modules)
983  {
984  if(!module.second.status())
985  PUSHCOMMENT;
986 
987  //--------------------------------------
988  // handle only @table:: filter parameters
989  insertParameters(out,
990  tabStr,
991  commentStr,
992  module.second.getNode("filterModuleParameterLink"),
993  "filterParameter" /*parameterType*/,
994  true /*onlyInsertAtTableParameters*/,
995  false /*includeAtTableParameters*/);
996 
997  OUT << module.second.getNode("filterKey").getValue() << ": {\n";
998  PUSHTAB;
999 
1000  insertModuleType(out, tabStr, commentStr, module.second.getNode("filterModuleType"));
1001 
1002  //--------------------------------------
1003  // handle NOT @table:: filter parameters
1004  insertParameters(out,
1005  tabStr,
1006  commentStr,
1007  module.second.getNode("filterModuleParameterLink"),
1008  "filterParameter" /*parameterType*/,
1009  false /*onlyInsertAtTableParameters*/,
1010  false /*includeAtTableParameters*/);
1011 
1012  POPTAB;
1013  OUT << "}\n\n"; // end filter module
1014 
1015  if(!module.second.status())
1016  POPCOMMENT;
1017  }
1018  POPTAB;
1019  OUT << "}\n\n"; // end filter
1020  }
1021 
1022  //--------------------------------------
1023  // handle NOT @table:: physics parameters
1024  insertParameters(out,
1025  tabStr,
1026  commentStr,
1027  physics.getNode("physicsOtherParametersLink"),
1028  "physicsParameter" /*parameterType*/,
1029  false /*onlyInsertAtTableParameters*/,
1030  false /*includeAtTableParameters*/);
1031 
1032  POPTAB;
1033  OUT << "}\n\n"; // end physics
1034  }
1035 
1036  //--------------------------------------
1037  // handle source
1038  OUT << "source: {\n";
1039  PUSHTAB;
1040  OUT << "module_type: ArtdaqInput";
1041  POPTAB;
1042  OUT << "}\n\n"; // end source
1043 
1044  //--------------------------------------
1045  // handle process_name
1046  //__COUT__ << "Writing art.process_name" << __E__;
1047  OUT << "process_name: " << art.getNode("ProcessName") << "\n";
1048 
1049  //--------------------------------------
1050  // handle art @table:: art add on parameters
1051  insertParameters(out,
1052  tabStr,
1053  commentStr,
1054  art.getNode("AddOnParametersLink"),
1055  "artParameter" /*parameterType*/,
1056  false /*onlyInsertAtTableParameters*/,
1057  true /*includeAtTableParameters*/);
1058 
1059  POPTAB;
1060  OUT << "}\n\n"; // end art
1061  }
1062 
1063  //--------------------------------------
1064  // handle ALL add-on parameters
1065  //__COUT__ << "Inserting add-on parameters" << __E__;
1066  insertParameters(out,
1067  tabStr,
1068  commentStr,
1069  receiverNode.getNode("addOnParametersLink"),
1070  "daqParameter" /*parameterType*/,
1071  false /*onlyInsertAtTableParameters*/,
1072  true /*includeAtTableParameters*/);
1073 
1074  //__COUT__ << "outputDataReceiverFHICL DONE" << __E__;
1075  out.close();
1076 } // end outputDataReceiverFHICL()
1077 
1078 //==============================================================================
1079 void ARTDAQTableBase::outputRoutingMasterFHICL(const ConfigurationTree& routingMasterNode,
1080  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
1081  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */)
1082 {
1083  std::string filename = getFHICLFilename(ARTDAQAppType::RoutingMaster, routingMasterNode.getValue());
1084 
1086  // generate xdaq run parameter file
1087  std::fstream out;
1088 
1089  std::string tabStr = "";
1090  std::string commentStr = "";
1091 
1092  out.open(filename, std::fstream::out | std::fstream::trunc);
1093  if(out.fail())
1094  {
1095  __SS__ << "Failed to open ARTDAQ RoutingMaster fcl file: " << filename << __E__;
1096  __SS_THROW__;
1097  }
1098 
1099  //--------------------------------------
1100  // header
1101  OUT << "###########################################################" << __E__;
1102  OUT << "#" << __E__;
1103  OUT << "# artdaq routingMaster fcl configuration file produced by otsdaq." << __E__;
1104  OUT << "# Creation time: \t" << StringMacros::getTimestampString() << __E__;
1105  OUT << "# Original filename: \t" << filename << __E__;
1106  OUT << "# otsdaq-ARTDAQ RoutingMaster UID:\t" << routingMasterNode.getValue() << __E__;
1107  OUT << "#" << __E__;
1108  OUT << "###########################################################" << __E__;
1109  OUT << "\n\n";
1110 
1111  // no primary link to table tree for reader node!
1112  try
1113  {
1114  if(routingMasterNode.isDisconnected())
1115  {
1116  // create empty fcl
1117  OUT << "{}\n\n";
1118  out.close();
1119  return;
1120  }
1121  }
1122  catch(const std::runtime_error&)
1123  {
1124  //__COUT__ << "Ignoring error, assume this a valid UID node." << __E__;
1125  // error is expected here for UIDs.. so just ignore
1126  // this check is valuable if source node is a unique-Link node, rather than UID
1127  }
1128 
1129  //--------------------------------------
1130  // handle daq
1131  OUT << "daq: {\n";
1132  PUSHTAB;
1133 
1134  OUT << "policy: {\n";
1135  PUSHTAB;
1136  auto policyName = routingMasterNode.getNode("routingPolicyPluginType").getValue();
1137  if(policyName == "DEFAULT")
1138  policyName = "NoOp";
1139  OUT << "policy: " << policyName << "\n";
1140  OUT << "receiver_ranks: []\n";
1141 
1142  // shared and unique parameters
1143  auto parametersLink = routingMasterNode.getNode("routingPolicyParametersLink");
1144  if(!parametersLink.isDisconnected())
1145  {
1146  auto parameters = parametersLink.getChildren();
1147  for(auto& parameter : parameters)
1148  {
1149  if(!parameter.second.status())
1150  PUSHCOMMENT;
1151 
1152  // __COUT__ <<
1153  // parameter.second.getNode("daqParameterKey").getValue() <<
1154  // ": " <<
1155  // parameter.second.getNode("daqParameterValue").getValue()
1156  // <<
1157  // "\n";
1158 
1159  auto comment = parameter.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT);
1160  OUT << parameter.second.getNode("daqParameterKey").getValue() << ": " << parameter.second.getNode("daqParameterValue").getValue()
1161  << (comment.isDefaultValue() ? "" : ("\t # " + comment.getValue())) << "\n";
1162 
1163  if(!parameter.second.status())
1164  POPCOMMENT;
1165  }
1166  }
1167 
1168  POPTAB;
1169  OUT << "}\n";
1170 
1171  OUT << "use_routing_master: true\n";
1172 
1173  auto routingMasterSubsystemID = 1;
1174  auto routingMasterSubsystemLink = routingMasterNode.getNode("SubsystemLink");
1175  std::string rmHost = "localhost";
1176  if(!routingMasterSubsystemLink.isDisconnected())
1177  {
1178  routingMasterSubsystemID = getSubsytemId(routingMasterSubsystemLink);
1179  rmHost = info_.subsystems[routingMasterSubsystemID].routingMasterHost;
1180  }
1181  if(rmHost == "localhost" || rmHost == "127.0.0.1")
1182  {
1183  char hostbuf[HOST_NAME_MAX + 1];
1184  gethostname(hostbuf, HOST_NAME_MAX);
1185  rmHost = std::string(hostbuf);
1186  }
1187 
1188  // Bookkept parameters
1189  OUT << "routing_master_hostname: \"" << rmHost << "\"\n";
1190  OUT << "sender_ranks: []\n";
1191  OUT << "table_update_port: 0\n";
1192  OUT << "table_update_address: \"0.0.0.0\"\n";
1193  OUT << "table_acknowledge_port: 0\n";
1194  OUT << "token_receiver: {\n";
1195  PUSHTAB;
1196 
1197  OUT << "routing_token_port: 0\n";
1198 
1199  POPTAB;
1200  OUT << "}\n";
1201 
1202  // Optional parameters
1203  auto tableUpdateIntervalMs = routingMasterNode.getNode("tableUpdateIntervalMs").getValue();
1204  if(tableUpdateIntervalMs != "DEFAULT")
1205  {
1206  OUT << "table_update_interval_ms: " << tableUpdateIntervalMs << "\n";
1207  }
1208  auto tableAckRetryCount = routingMasterNode.getNode("tableAckRetryCount").getValue();
1209  if(tableAckRetryCount != "DEFAULT")
1210  {
1211  OUT << "table_ack_retry_count: " << tableAckRetryCount << "\n";
1212  }
1213 
1214  OUT << "routing_timeout_ms: " << routingTimeoutMs << "\n";
1215  OUT << "routing_retry_count: " << routingRetryCount << "\n";
1216 
1217  insertMetricsBlock(OUT, tabStr, commentStr, routingMasterNode);
1218 
1219  POPTAB;
1220  OUT << "}\n\n"; // end daq
1221 
1222  out.close();
1223 } // end outputReaderFHICL()
1224 
1225 //==============================================================================
1226 const ARTDAQTableBase::ARTDAQInfo& ARTDAQTableBase::extractARTDAQInfo(ConfigurationTree artdaqSupervisorNode,
1227  bool getStatusFalseNodes /* = false */,
1228  bool doWriteFHiCL /* = false */,
1229  size_t maxFragmentSizeBytes /* = DEFAULT_MAX_FRAGMENT_SIZE*/,
1230  size_t routingTimeoutMs /* = DEFAULT_ROUTING_TIMEOUT_MS */,
1231  size_t routingRetryCount /* = DEFAULT_ROUTING_RETRY_COUNT */,
1232  ProgressBar* progressBar /* = 0 */)
1233 {
1234  if(progressBar)
1235  progressBar->step();
1236 
1237  // reset info every time, because it could be called after configuration manipulations
1238  info_.subsystems.clear();
1239  info_.processes.clear();
1240 
1241  if(progressBar)
1242  progressBar->step();
1243 
1244  info_.subsystems[NULL_SUBSYSTEM_DESTINATION].id = NULL_SUBSYSTEM_DESTINATION;
1245  info_.subsystems[NULL_SUBSYSTEM_DESTINATION].label = NULL_SUBSYSTEM_DESTINATION_LABEL;
1246 
1247  // if no supervisor, then done
1248  if(artdaqSupervisorNode.isDisconnected())
1249  return info_;
1250 
1251  // We do RoutingMasters first so we can properly fill in routing tables later
1252  extractRoutingMastersInfo(artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, routingTimeoutMs, routingRetryCount);
1253 
1254  if(progressBar)
1255  progressBar->step();
1256 
1257  extractBoardReadersInfo(artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes, routingTimeoutMs, routingRetryCount);
1258 
1259  if(progressBar)
1260  progressBar->step();
1261 
1262  extractEventBuildersInfo(artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
1263 
1264  if(progressBar)
1265  progressBar->step();
1266 
1267  extractDataLoggersInfo(artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
1268 
1269  if(progressBar)
1270  progressBar->step();
1271 
1272  extractDispatchersInfo(artdaqSupervisorNode, getStatusFalseNodes, doWriteFHiCL, maxFragmentSizeBytes);
1273 
1274  if(progressBar)
1275  progressBar->step();
1276 
1277  return info_;
1278 } // end extractARTDAQInfo()
1279 
1280 //==============================================================================
1281 void ARTDAQTableBase::extractRoutingMastersInfo(
1282  ConfigurationTree artdaqSupervisorNode, bool getStatusFalseNodes, bool doWriteFHiCL, size_t routingTimeoutMs, size_t routingRetryCount)
1283 {
1284  __COUT__ << "Checking for Routing Masters..." << __E__;
1285  ConfigurationTree rmsLink = artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToRoutingMasters_);
1286  if(!rmsLink.isDisconnected() && rmsLink.getChildren().size() > 0)
1287  {
1288  std::vector<std::pair<std::string, ConfigurationTree>> routingMasters = rmsLink.getChildren();
1289 
1290  __COUT__ << "There are " << routingMasters.size() << " configured Routing Masters" << __E__;
1291 
1292  for(auto& routingMaster : routingMasters)
1293  {
1294  const std::string& rmUID = routingMaster.first;
1295 
1296  if(getStatusFalseNodes || routingMaster.second.status())
1297  {
1298  std::string rmHost = routingMaster.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME).getValueWithDefault("localhost");
1299  if(rmHost == "localhost" || rmHost == "127.0.0.1")
1300  {
1301  char hostbuf[HOST_NAME_MAX + 1];
1302  gethostname(hostbuf, HOST_NAME_MAX);
1303  rmHost = std::string(hostbuf);
1304  }
1305 
1306  int routingMasterSubsystemID = 1;
1307  ConfigurationTree routingMasterSubsystemLink = routingMaster.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
1308  if(!routingMasterSubsystemLink.isDisconnected())
1309  {
1310  routingMasterSubsystemID = getSubsytemId(routingMasterSubsystemLink);
1311 
1312  //__COUTV__(routingMasterSubsystemID);
1313  info_.subsystems[routingMasterSubsystemID].id = routingMasterSubsystemID;
1314 
1315  const std::string& routingMasterSubsystemName = routingMasterSubsystemLink.getUIDAsString();
1316  //__COUTV__(routingMasterSubsystemName);
1317 
1318  info_.subsystems[routingMasterSubsystemID].label = routingMasterSubsystemName;
1319 
1320  if(info_.subsystems[routingMasterSubsystemID].hasRoutingMaster)
1321  {
1322  __SS__ << "Error: You cannot have multiple Routing Masters in a subsystem!";
1323  __SS_THROW__;
1324  return;
1325  }
1326 
1327  auto routingMasterSubsystemDestinationLink = routingMasterSubsystemLink.getNode(colARTDAQSubsystem_.colLinkToDestination_);
1328  if(routingMasterSubsystemDestinationLink.isDisconnected())
1329  {
1330  // default to no destination when no link
1331  info_.subsystems[routingMasterSubsystemID].destination = 0;
1332  }
1333  else
1334  {
1335  // get destination subsystem id
1336  info_.subsystems[routingMasterSubsystemID].destination = getSubsytemId(routingMasterSubsystemDestinationLink);
1337  }
1338  //__COUTV__(info_.subsystems[routingMasterSubsystemID].destination);
1339 
1340  // add this subsystem to destination subsystem's sources, if not
1341  // there
1342  if(!info_.subsystems.count(info_.subsystems[routingMasterSubsystemID].destination) ||
1343  !info_.subsystems[info_.subsystems[routingMasterSubsystemID].destination].sources.count(routingMasterSubsystemID))
1344  {
1345  info_.subsystems[info_.subsystems[routingMasterSubsystemID].destination].sources.insert(routingMasterSubsystemID);
1346  }
1347 
1348  } // end subsystem instantiation
1349 
1350  __COUT__ << "Found Routing Master with UID " << rmUID << ", DAQInterface Hostname " << rmHost << ", and Subsystem " << routingMasterSubsystemID
1351  << __E__;
1352  info_.processes[ARTDAQAppType::RoutingMaster].emplace_back(
1353  rmUID, rmHost, routingMasterSubsystemID, ARTDAQAppType::RoutingMaster, routingMaster.second.status());
1354 
1355  info_.subsystems[routingMasterSubsystemID].hasRoutingMaster = true;
1356  info_.subsystems[routingMasterSubsystemID].routingMasterHost = rmHost;
1357 
1358  if(doWriteFHiCL)
1359  {
1360  outputRoutingMasterFHICL(routingMaster.second, routingTimeoutMs, routingRetryCount);
1361 
1362  flattenFHICL(ARTDAQAppType::RoutingMaster, routingMaster.second.getValue());
1363  }
1364  }
1365  else // disabled
1366  {
1367  __COUT__ << "Routing Master " << rmUID << " is disabled." << __E__;
1368  }
1369  } // end routing master loop
1370  }
1371 } // end extractRoutingMastersInfo()
1372 
1373 //==============================================================================
1374 void ARTDAQTableBase::extractBoardReadersInfo(ConfigurationTree artdaqSupervisorNode,
1375  bool getStatusFalseNodes,
1376  bool doWriteFHiCL,
1377  size_t maxFragmentSizeBytes,
1378  size_t routingTimeoutMs,
1379  size_t routingRetryCount)
1380 {
1381  __COUT__ << "Checking for Board Readers..." << __E__;
1382  ConfigurationTree readersLink = artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToBoardReaders_);
1383  if(!readersLink.isDisconnected() && readersLink.getChildren().size() > 0)
1384  {
1385  std::vector<std::pair<std::string, ConfigurationTree>> readers = readersLink.getChildren();
1386  __COUT__ << "There are " << readers.size() << " configured Board Readers." << __E__;
1387 
1388  for(auto& reader : readers)
1389  {
1390  const std::string& readerUID = reader.first;
1391 
1392  if(getStatusFalseNodes || reader.second.status())
1393  {
1394  std::string readerHost = reader.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME).getValueWithDefault("localhost");
1395 
1396  int readerSubsystemID = 1;
1397  ConfigurationTree readerSubsystemLink = reader.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
1398  if(!readerSubsystemLink.isDisconnected())
1399  {
1400  readerSubsystemID = getSubsytemId(readerSubsystemLink);
1401  //__COUTV__(readerSubsystemID);
1402  info_.subsystems[readerSubsystemID].id = readerSubsystemID;
1403 
1404  const std::string& readerSubsystemName = readerSubsystemLink.getUIDAsString();
1405  //__COUTV__(readerSubsystemName);
1406 
1407  info_.subsystems[readerSubsystemID].label = readerSubsystemName;
1408 
1409  auto readerSubsystemDestinationLink = readerSubsystemLink.getNode(colARTDAQSubsystem_.colLinkToDestination_);
1410  if(readerSubsystemDestinationLink.isDisconnected())
1411  {
1412  // default to no destination when no link
1413  info_.subsystems[readerSubsystemID].destination = 0;
1414  }
1415  else
1416  {
1417  // get destination subsystem id
1418  info_.subsystems[readerSubsystemID].destination = getSubsytemId(readerSubsystemDestinationLink);
1419  }
1420  //__COUTV__(info_.subsystems[readerSubsystemID].destination);
1421 
1422  // add this subsystem to destination subsystem's sources, if not
1423  // there
1424  if(!info_.subsystems.count(info_.subsystems[readerSubsystemID].destination) ||
1425  !info_.subsystems[info_.subsystems[readerSubsystemID].destination].sources.count(readerSubsystemID))
1426  {
1427  info_.subsystems[info_.subsystems[readerSubsystemID].destination].sources.insert(readerSubsystemID);
1428  }
1429 
1430  } // end subsystem instantiation
1431 
1432  __COUT__ << "Found Board Reader with UID " << readerUID << ", DAQInterface Hostname " << readerHost << ", and Subsystem " << readerSubsystemID
1433  << __E__;
1434  info_.processes[ARTDAQAppType::BoardReader].emplace_back(
1435  readerUID, readerHost, readerSubsystemID, ARTDAQAppType::BoardReader, reader.second.status());
1436 
1437  if(doWriteFHiCL)
1438  {
1439  outputBoardReaderFHICL(reader.second, maxFragmentSizeBytes, routingTimeoutMs, routingRetryCount);
1440 
1441  flattenFHICL(ARTDAQAppType::BoardReader, reader.second.getValue());
1442  }
1443  }
1444  else // disabled
1445  {
1446  __COUT__ << "Board Reader " << readerUID << " is disabled." << __E__;
1447  }
1448  } // end reader loop
1449  }
1450  else
1451  {
1452  __COUT_WARN__ << "There should be at least one Board Reader!";
1453  //__SS_THROW__;
1454  // return;
1455  }
1456 } // end extractBoardReadersInfo()
1457 
1458 //==============================================================================
1459 void ARTDAQTableBase::extractEventBuildersInfo(ConfigurationTree artdaqSupervisorNode, bool getStatusFalseNodes, bool doWriteFHiCL, size_t maxFragmentSizeBytes)
1460 {
1461  __COUT__ << "Checking for Event Builders..." << __E__;
1462  ConfigurationTree buildersLink = artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToEventBuilders_);
1463  if(!buildersLink.isDisconnected() && buildersLink.getChildren().size() > 0)
1464  {
1465  std::vector<std::pair<std::string, ConfigurationTree>> builders = buildersLink.getChildren();
1466 
1467  for(auto& builder : builders)
1468  {
1469  const std::string& builderUID = builder.first;
1470 
1471  if(getStatusFalseNodes || builder.second.status())
1472  {
1473  std::string builderHost = builder.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME).getValueWithDefault("localhost");
1474 
1475  int builderSubsystemID = 1;
1476  ConfigurationTree builderSubsystemLink = builder.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
1477  if(!builderSubsystemLink.isDisconnected())
1478  {
1479  builderSubsystemID = getSubsytemId(builderSubsystemLink);
1480  //__COUTV__(builderSubsystemID);
1481 
1482  info_.subsystems[builderSubsystemID].id = builderSubsystemID;
1483 
1484  const std::string& builderSubsystemName = builderSubsystemLink.getUIDAsString();
1485  //__COUTV__(builderSubsystemName);
1486 
1487  info_.subsystems[builderSubsystemID].label = builderSubsystemName;
1488 
1489  auto builderSubsystemDestinationLink = builderSubsystemLink.getNode(colARTDAQSubsystem_.colLinkToDestination_);
1490  if(builderSubsystemDestinationLink.isDisconnected())
1491  {
1492  // default to no destination when no link
1493  info_.subsystems[builderSubsystemID].destination = 0;
1494  }
1495  else
1496  {
1497  // get destination subsystem id
1498  info_.subsystems[builderSubsystemID].destination = getSubsytemId(builderSubsystemDestinationLink);
1499  }
1500  //__COUTV__(info_.subsystems[builderSubsystemID].destination);
1501 
1502  // add this subsystem to destination subsystem's sources, if not
1503  // there
1504  if(!info_.subsystems.count(info_.subsystems[builderSubsystemID].destination) ||
1505  !info_.subsystems[info_.subsystems[builderSubsystemID].destination].sources.count(builderSubsystemID))
1506  {
1507  info_.subsystems[info_.subsystems[builderSubsystemID].destination].sources.insert(builderSubsystemID);
1508  }
1509 
1510  } // end subsystem instantiation
1511 
1512  __COUT__ << "Found Event Builder with UID " << builderUID << ", on Hostname " << builderHost << ", in Subsystem " << builderSubsystemID
1513  << __E__;
1514  info_.processes[ARTDAQAppType::EventBuilder].emplace_back(
1515  builderUID, builderHost, builderSubsystemID, ARTDAQAppType::EventBuilder, builder.second.status());
1516 
1517  if(doWriteFHiCL)
1518  {
1519  outputDataReceiverFHICL(builder.second, ARTDAQAppType::EventBuilder, maxFragmentSizeBytes);
1520 
1521  flattenFHICL(ARTDAQAppType::EventBuilder, builder.second.getValue());
1522  }
1523  }
1524  else // disabled
1525  {
1526  __COUT__ << "Event Builder " << builderUID << " is disabled." << __E__;
1527  }
1528  } // end builder loop
1529  }
1530  else
1531  {
1532  __COUT_WARN__ << "There should be at least one Event Builder!";
1533  //__SS_THROW__;
1534  // return;
1535  }
1536 } // end extractEventBuildersInfo()
1537 
1538 //==============================================================================
1539 void ARTDAQTableBase::extractDataLoggersInfo(ConfigurationTree artdaqSupervisorNode, bool getStatusFalseNodes, bool doWriteFHiCL, size_t maxFragmentSizeBytes)
1540 {
1541  __COUT__ << "Checking for Data Loggers..." << __E__;
1542  ConfigurationTree dataloggersLink = artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToDataLoggers_);
1543  if(!dataloggersLink.isDisconnected())
1544  {
1545  std::vector<std::pair<std::string, ConfigurationTree>> dataloggers = dataloggersLink.getChildren();
1546 
1547  for(auto& datalogger : dataloggers)
1548  {
1549  const std::string& loggerUID = datalogger.first;
1550 
1551  if(getStatusFalseNodes || datalogger.second.status())
1552  {
1553  std::string loggerHost = datalogger.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME).getValueWithDefault("localhost");
1554 
1555  int loggerSubsystemID = 1;
1556  ConfigurationTree loggerSubsystemLink = datalogger.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
1557  if(!loggerSubsystemLink.isDisconnected())
1558  {
1559  loggerSubsystemID = getSubsytemId(loggerSubsystemLink);
1560  //__COUTV__(loggerSubsystemID);
1561  info_.subsystems[loggerSubsystemID].id = loggerSubsystemID;
1562 
1563  const std::string& loggerSubsystemName = loggerSubsystemLink.getUIDAsString();
1564  //__COUTV__(loggerSubsystemName);
1565 
1566  info_.subsystems[loggerSubsystemID].label = loggerSubsystemName;
1567 
1568  auto loggerSubsystemDestinationLink = loggerSubsystemLink.getNode(colARTDAQSubsystem_.colLinkToDestination_);
1569  if(loggerSubsystemDestinationLink.isDisconnected())
1570  {
1571  // default to no destination when no link
1572  info_.subsystems[loggerSubsystemID].destination = 0;
1573  }
1574  else
1575  {
1576  // get destination subsystem id
1577  info_.subsystems[loggerSubsystemID].destination = getSubsytemId(loggerSubsystemDestinationLink);
1578  }
1579  //__COUTV__(info_.subsystems[loggerSubsystemID].destination);
1580 
1581  // add this subsystem to destination subsystem's sources, if not
1582  // there
1583  if(!info_.subsystems.count(info_.subsystems[loggerSubsystemID].destination) ||
1584  !info_.subsystems[info_.subsystems[loggerSubsystemID].destination].sources.count(loggerSubsystemID))
1585  {
1586  info_.subsystems[info_.subsystems[loggerSubsystemID].destination].sources.insert(loggerSubsystemID);
1587  }
1588 
1589  } // end subsystem instantiation
1590 
1591  __COUT__ << "Found Data Logger with UID " << loggerUID << ", DAQInterface Hostname " << loggerHost << ", and Subsystem " << loggerSubsystemID
1592  << __E__;
1593  info_.processes[ARTDAQAppType::DataLogger].emplace_back(
1594  loggerUID, loggerHost, loggerSubsystemID, ARTDAQAppType::DataLogger, datalogger.second.status());
1595 
1596  if(doWriteFHiCL)
1597  {
1598  outputDataReceiverFHICL(datalogger.second, ARTDAQAppType::DataLogger, maxFragmentSizeBytes);
1599 
1600  flattenFHICL(ARTDAQAppType::DataLogger, datalogger.second.getValue());
1601  }
1602  }
1603  else // disabled
1604  {
1605  __COUT__ << "Data Logger " << loggerUID << " is disabled." << __E__;
1606  }
1607  } // end logger loop
1608  }
1609  else
1610  {
1611  __COUT_WARN__ << "There were no Data Loggers found!";
1612  }
1613 } // end extractDataLoggersInfo()
1614 
1615 //==============================================================================
1616 void ARTDAQTableBase::extractDispatchersInfo(ConfigurationTree artdaqSupervisorNode, bool getStatusFalseNodes, bool doWriteFHiCL, size_t maxFragmentSizeBytes)
1617 {
1618  __COUT__ << "Checking for Dispatchers..." << __E__;
1619  ConfigurationTree dispatchersLink = artdaqSupervisorNode.getNode(colARTDAQSupervisor_.colLinkToDispatchers_);
1620  if(!dispatchersLink.isDisconnected())
1621  {
1622  std::vector<std::pair<std::string, ConfigurationTree>> dispatchers = dispatchersLink.getChildren();
1623 
1624  for(auto& dispatcher : dispatchers)
1625  {
1626  const std::string& dispatcherUID = dispatcher.first;
1627 
1628  if(getStatusFalseNodes || dispatcher.second.status())
1629  {
1630  std::string dispatcherHost = dispatcher.second.getNode(ARTDAQTableBase::ARTDAQ_TYPE_TABLE_HOSTNAME).getValueWithDefault("localhost");
1631 
1632  auto dispatcherSubsystemID = 1;
1633  ConfigurationTree dispatcherSubsystemLink = dispatcher.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK);
1634  if(!dispatcherSubsystemLink.isDisconnected())
1635  {
1636  dispatcherSubsystemID = getSubsytemId(dispatcherSubsystemLink);
1637  //__COUTV__(dispatcherSubsystemID);
1638  info_.subsystems[dispatcherSubsystemID].id = dispatcherSubsystemID;
1639 
1640  const std::string& dispatcherSubsystemName = dispatcherSubsystemLink.getUIDAsString();
1641  //__COUTV__(dispatcherSubsystemName);
1642 
1643  info_.subsystems[dispatcherSubsystemID].label = dispatcherSubsystemName;
1644 
1645  auto dispatcherSubsystemDestinationLink = dispatcherSubsystemLink.getNode(colARTDAQSubsystem_.colLinkToDestination_);
1646  if(dispatcherSubsystemDestinationLink.isDisconnected())
1647  {
1648  // default to no destination when no link
1649  info_.subsystems[dispatcherSubsystemID].destination = 0;
1650  }
1651  else
1652  {
1653  // get destination subsystem id
1654  info_.subsystems[dispatcherSubsystemID].destination = getSubsytemId(dispatcherSubsystemDestinationLink);
1655  }
1656  //__COUTV__(info_.subsystems[dispatcherSubsystemID].destination);
1657 
1658  // add this subsystem to destination subsystem's sources, if not
1659  // there
1660  if(!info_.subsystems.count(info_.subsystems[dispatcherSubsystemID].destination) ||
1661  !info_.subsystems[info_.subsystems[dispatcherSubsystemID].destination].sources.count(dispatcherSubsystemID))
1662  {
1663  info_.subsystems[info_.subsystems[dispatcherSubsystemID].destination].sources.insert(dispatcherSubsystemID);
1664  }
1665  }
1666 
1667  __COUT__ << "Found Dispatcher with UID " << dispatcherUID << ", DAQInterface Hostname " << dispatcherHost << ", and Subsystem "
1668  << dispatcherSubsystemID << __E__;
1669  info_.processes[ARTDAQAppType::Dispatcher].emplace_back(
1670  dispatcherUID, dispatcherHost, dispatcherSubsystemID, ARTDAQAppType::Dispatcher, dispatcher.second.status());
1671 
1672  if(doWriteFHiCL)
1673  {
1674  outputDataReceiverFHICL(dispatcher.second, ARTDAQAppType::Dispatcher, maxFragmentSizeBytes);
1675 
1676  flattenFHICL(ARTDAQAppType::Dispatcher, dispatcher.second.getValue());
1677  }
1678  }
1679  else // disabled
1680  {
1681  __COUT__ << "Dispatcher " << dispatcherUID << " is disabled." << __E__;
1682  }
1683  } // end dispatcher loop
1684  }
1685  else
1686  {
1687  __COUT_WARN__ << "There were no Dispatchers found!";
1688  }
1689 } // end extractDispatchersInfo()
1690 
1691 //==============================================================================
1692 // getARTDAQSystem
1693 //
1694 // static function to retrive the active ARTDAQ system configuration.
1695 //
1696 // Subsystem map to destination subsystem name.
1697 // Node properties: {status,hostname,subsystemName,(nodeArrString),(hostnameArrString),(hostnameFixedWidth)}
1698 // artdaqSupervisoInfo: {name, status, context address, context port}
1699 //
1700 const ARTDAQTableBase::ARTDAQInfo& ARTDAQTableBase::getARTDAQSystem(
1701  ConfigurationManagerRW* cfgMgr,
1702  std::map<std::string /*type*/, std::map<std::string /*record*/, std::vector<std::string /*property*/>>>& nodeTypeToObjectMap,
1703  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>& subsystemObjectMap,
1704  std::vector<std::string /*property*/>& artdaqSupervisoInfo)
1705 {
1706  __COUT__ << "getARTDAQSystem()" << __E__;
1707 
1708  artdaqSupervisoInfo.clear(); // init
1709 
1710  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
1711 
1712  // for each artdaq context, output all artdaq apps
1713 
1714  const XDAQContextTable::XDAQContext* artdaqContext = contextTable->getTheARTDAQSupervisorContext();
1715 
1716  // return empty info
1717  if(!artdaqContext)
1718  return ARTDAQTableBase::info_;
1719 
1720  __COUTV__(artdaqContext->contextUID_);
1721  __COUTV__(artdaqContext->applications_.size());
1722 
1723  for(auto& artdaqApp : artdaqContext->applications_)
1724  {
1725  if(artdaqApp.class_ != ARTDAQ_SUPERVISOR_CLASS)
1726  continue;
1727 
1728  __COUTV__(artdaqApp.applicationUID_);
1729  artdaqSupervisoInfo.push_back(artdaqApp.applicationUID_);
1730  artdaqSupervisoInfo.push_back((artdaqContext->status_ && artdaqApp.status_) ? "1" : "0");
1731  artdaqSupervisoInfo.push_back(artdaqContext->address_);
1732  artdaqSupervisoInfo.push_back(std::to_string(artdaqContext->port_));
1733 
1734  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::extractARTDAQInfo(XDAQContextTable::getSupervisorConfigNode(/*artdaqSupervisorNode*/
1735  cfgMgr,
1736  artdaqContext->contextUID_,
1737  artdaqApp.applicationUID_),
1738  true /*getStatusFalseNodes*/);
1739 
1740  __COUT__ << "========== "
1741  << "Found " << info.subsystems.size() << " subsystems." << __E__;
1742 
1743  // build subsystem desintation map
1744  for(auto& subsystem : info.subsystems)
1745  subsystemObjectMap.emplace(std::make_pair(subsystem.second.label, std::to_string(subsystem.second.destination)));
1746 
1747  __COUT__ << "========== "
1748  << "Found " << info.processes.size() << " process types." << __E__;
1749 
1750  for(auto& nameTypePair : ARTDAQTableBase::processTypes_.mapToType_)
1751  {
1752  const std::string& typeString = nameTypePair.first;
1753  __COUTV__(typeString);
1754 
1755  nodeTypeToObjectMap.emplace(std::make_pair(typeString, std::map<std::string /*record*/, std::vector<std::string /*property*/>>()));
1756 
1757  auto it = info.processes.find(nameTypePair.second);
1758  if(it == info.processes.end())
1759  {
1760  __COUT__ << "\t"
1761  << "Found 0 " << typeString << __E__;
1762  continue;
1763  }
1764  __COUT__ << "\t"
1765  << "Found " << it->second.size() << " " << typeString << "(s)" << __E__;
1766 
1767  auto tableIt = processTypes_.mapToTable_.find(typeString);
1768  if(tableIt == processTypes_.mapToTable_.end())
1769  {
1770  __SS__ << "Invalid artdaq node type '" << typeString << "' attempted!" << __E__;
1771  __SS_THROW__;
1772  }
1773  __COUTV__(tableIt->second);
1774 
1775  auto allNodes = cfgMgr->getNode(tableIt->second).getChildren();
1776 
1777  std::set<std::string /*nodeName*/> skipSet; // use to skip nodes when constructing multi-nodes
1778 
1779  const std::set<std::string /*colName*/> skipColumns({ARTDAQ_TYPE_TABLE_HOSTNAME,
1780  ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK,
1781  TableViewColumnInfo::COL_NAME_COMMENT,
1782  TableViewColumnInfo::COL_NAME_AUTHOR,
1783  TableViewColumnInfo::COL_NAME_CREATION}); // note: also skip UID and Status
1784 
1785  // loop through all nodes of this type
1786  for(auto& artdaqNode : it->second)
1787  {
1788  // check skip set
1789  if(skipSet.find(artdaqNode.label) != skipSet.end())
1790  continue;
1791 
1792  __COUT__ << "\t\t"
1793  << "Found '" << artdaqNode.label << "' " << typeString << __E__;
1794 
1795  std::string nodeName = artdaqNode.label;
1796  bool status = artdaqNode.status;
1797  std::string hostname = artdaqNode.hostname;
1798  std::string subsystemId = std::to_string(artdaqNode.subsystem);
1799  std::string subsystemName = info.subsystems.at(artdaqNode.subsystem).label;
1800 
1801  ConfigurationTree thisNode = cfgMgr->getNode(tableIt->second).getNode(nodeName);
1802  auto thisNodeColumns = thisNode.getChildren();
1803 
1804  // check for multi-node
1805  // Steps:
1806  // search for other records with same values/links except hostname/name
1807 
1808  std::vector<std::string> multiNodeNames, hostnameArray;
1809  //unsigned int hostnameFixedWidth = 0;
1810 
1811  __COUTV__(allNodes.size());
1812  for(auto& otherNode : allNodes) // start multi-node search loop
1813  {
1814  if(otherNode.first == nodeName || skipSet.find(otherNode.first) != skipSet.end() ||
1815  otherNode.second.status() != status) // skip if status mismatch
1816  continue; // skip unless 'other' and not in skip set
1817 
1818  //__COUTV__(subsystemName);
1819  //__COUTV__(otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID).getValue());
1820 
1821  if(subsystemName == otherNode.second.getNode(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID).getValue())
1822  {
1823  // possible multi-node situation
1824  //__COUT__ << "Checking for multi-node..." << __E__;
1825 
1826  //__COUTV__(thisNode.getNodeRow());
1827  //__COUTV__(otherNode.second.getNodeRow());
1828 
1829  auto otherNodeColumns = otherNode.second.getChildren();
1830 
1831  bool isMultiNode = true;
1832  for(unsigned int i = 0; i < thisNodeColumns.size() && i < otherNodeColumns.size(); ++i)
1833  {
1834  // skip columns that do not need to be checked for multi-node consideration
1835  if(skipColumns.find(thisNodeColumns[i].first) != skipColumns.end() || thisNodeColumns[i].second.isLinkNode())
1836  continue;
1837 
1838  // at this point must match for multinode
1839 
1840  //__COUTV__(thisNodeColumns[i].first);
1841  //__COUTV__(otherNodeColumns[i].first);
1842 
1843  //__COUTV__(thisNodeColumns[i].second.getValue());
1844  //__COUTV__(otherNodeColumns[i].second.getValue());
1845 
1846  if(thisNodeColumns[i].second.getValue() != otherNodeColumns[i].second.getValue())
1847  {
1848  __COUT__ << "Mismatch, not multi-node member." << __E__;
1849  isMultiNode = false;
1850  break;
1851  }
1852  }
1853 
1854  if(isMultiNode)
1855  {
1856  __COUT__ << "Found '" << nodeName << "' multi-node member candidate '" << otherNode.first << "'" << __E__;
1857 
1858  if(!multiNodeNames.size()) // add this node first!
1859  {
1860  multiNodeNames.push_back(nodeName);
1861  hostnameArray.push_back(hostname);
1862  }
1863  multiNodeNames.push_back(otherNode.first);
1864  hostnameArray.push_back(otherNode.second.getNode(ARTDAQ_TYPE_TABLE_HOSTNAME).getValue());
1865  skipSet.emplace(otherNode.first);
1866  }
1867  }
1868  } // end loop to search for multi-node members
1869 
1870  unsigned int nodeFixedWildcardLength = 0, hostFixedWildcardLength = 0;
1871  std::string multiNodeString = "", hostArrayString = "";
1872 
1873  if(multiNodeNames.size() > 1)
1874  {
1875  __COUT__ << "Handling multi-node printer syntax" << __E__;
1876 
1877  __COUTV__(StringMacros::vectorToString(multiNodeNames));
1878  __COUTV__(StringMacros::vectorToString(hostnameArray));
1879  __COUTV__(StringMacros::setToString(skipSet));
1880 
1881  {
1882  // check for alpha-based similarity groupings (ignore numbers and special characters)
1883  unsigned int maxScore = 0;
1884  unsigned int score;
1885  unsigned int numberAtMaxScore = 0;
1886  unsigned int minScore = -1;
1887  unsigned int numberAtMinScore = 0;
1888  std::vector<unsigned int> scoreVector;
1889  scoreVector.push_back(-1); // for 0 index (it's perfect)
1890  for(unsigned int i = 1; i < multiNodeNames.size(); ++i)
1891  {
1892  score = 0;
1893 
1894  //__COUT__ << multiNodeNames[0] << " vs " << multiNodeNames[i] << __E__;
1895 
1896  // start forward score loop
1897  for(unsigned int j = 0, k = 0; j < multiNodeNames[0].size() && k < multiNodeNames[i].size(); ++j, ++k)
1898  {
1899  while(j < multiNodeNames[0].size() && !(multiNodeNames[0][j] >= 'a' && multiNodeNames[0][j] <= 'z') &&
1900  !(multiNodeNames[0][j] >= 'A' && multiNodeNames[0][j] <= 'Z'))
1901  ++j; // skip non-alpha characters
1902  while(k < multiNodeNames[i].size() && !(multiNodeNames[i][k] >= 'a' && multiNodeNames[i][k] <= 'z') &&
1903  !(multiNodeNames[i][k] >= 'A' && multiNodeNames[i][k] <= 'Z'))
1904  ++k; // skip non-alpha characters
1905 
1906  while(k < multiNodeNames[i].size() && multiNodeNames[0][j] != multiNodeNames[i][k])
1907  ++k; // skip non-matching alpha characters
1908 
1909  //__COUT__ << j << "-" << k << " of " <<
1910  // multiNodeNames[0].size() << "-" <<
1911  // multiNodeNames[i].size() << __E__;
1912 
1913  if(j < multiNodeNames[0].size() && k < multiNodeNames[i].size())
1914  ++score; // found a matching letter!
1915  } // end forward score loop
1916 
1917  //__COUTV__(score);
1918 
1919  // start backward score loop
1920  for(unsigned int j = multiNodeNames[0].size() - 1, k = multiNodeNames[i].size() - 1;
1921  j < multiNodeNames[0].size() && k < multiNodeNames[i].size();
1922  --j, --k)
1923  {
1924  while(j < multiNodeNames[0].size() && !(multiNodeNames[0][j] >= 'a' && multiNodeNames[0][j] <= 'z') &&
1925  !(multiNodeNames[0][j] >= 'A' && multiNodeNames[0][j] <= 'Z'))
1926  --j; // skip non-alpha characters
1927  while(k < multiNodeNames[i].size() && !(multiNodeNames[i][k] >= 'a' && multiNodeNames[i][k] <= 'z') &&
1928  !(multiNodeNames[i][k] >= 'A' && multiNodeNames[i][k] <= 'Z'))
1929  --k; // skip non-alpha characters
1930 
1931  while(k < multiNodeNames[i].size() && multiNodeNames[0][j] != multiNodeNames[i][k])
1932  --k; // skip non-matching alpha characters
1933 
1934  //__COUT__ << "BACK" << j << "-" << k << " of " <<
1935  // multiNodeNames[0].size() << "-" <<
1936  // multiNodeNames[i].size() << __E__;
1937 
1938  if(j < multiNodeNames[0].size() && k < multiNodeNames[i].size())
1939  ++score; // found a matching letter!
1940  } // end backward score loop
1941 
1942  //__COUTV__(score/2.0);
1943 
1944  scoreVector.push_back(score);
1945 
1946  if(score > maxScore)
1947  {
1948  maxScore = score;
1949  numberAtMaxScore = 1;
1950  }
1951  else if(score == maxScore)
1952  ++numberAtMaxScore;
1953 
1954  if(score < minScore)
1955  {
1956  minScore = score;
1957  numberAtMinScore = 1;
1958  }
1959  else if(score == minScore)
1960  ++numberAtMinScore;
1961 
1962  } // end multi-node member scoring loop
1963 
1964  //__COUTV__(minScore);
1965  //__COUTV__(maxScore);
1966  //__COUTV__(numberAtMaxScore);
1967  //__COUTV__(numberAtMinScore);
1968 
1969  __COUT__ << "Trimming multi-node members with low match score..." << __E__;
1970 
1971  // go backwards, to not mess up indices as deleted
1972  // do not delete index 0
1973  for(unsigned int i = multiNodeNames.size() - 1; i > 0 && i < multiNodeNames.size(); --i)
1974  {
1975  //__COUTV__(scoreVector[i]);
1976  //__COUTV__(i);
1977  if(maxScore > multiNodeNames[0].size() && scoreVector[i] >= maxScore)
1978  continue;
1979 
1980  // else trim
1981  __COUT__ << "Trimming " << multiNodeNames[i] << __E__;
1982 
1983  skipSet.erase(multiNodeNames[i]);
1984  multiNodeNames.erase(multiNodeNames.begin() + i);
1985  hostnameArray.erase(hostnameArray.begin() + i);
1986 
1987  } // end multi-node trim loop
1988 
1989  } // done with multi-node member trim
1990 
1991  __COUTV__(StringMacros::vectorToString(multiNodeNames));
1992  __COUTV__(StringMacros::vectorToString(hostnameArray));
1993  __COUTV__(StringMacros::setToString(skipSet));
1994 
1995  // from set of nodename wildcards, make printer syntax
1996  if(multiNodeNames.size() > 1)
1997  {
1998  std::vector<std::string> commonChunks;
1999  std::vector<std::string> wildcards;
2000 
2001  bool wildcardsNeeded = StringMacros::extractCommonChunks(multiNodeNames, commonChunks, wildcards, nodeFixedWildcardLength);
2002 
2003  if(!wildcardsNeeded)
2004  {
2005  __SS__ << "Impossible extractCommonChunks result! Please notify admins or try to simplify record naming convention." << __E__;
2006  __SS_THROW__;
2007  }
2008 
2009  nodeName = "";
2010  bool first = true;
2011  for(auto& commonChunk : commonChunks)
2012  {
2013  nodeName += (!first ? "*" : "") + commonChunk;
2014  if(first)
2015  first = false;
2016  }
2017  if(commonChunks.size() == 1)
2018  nodeName += '*';
2019 
2020  __COUTV__(nodeName);
2021 
2022  // steps:
2023  // determine if all unsigned ints
2024  // if int, then order and attempt to hyphenate
2025  // if not ints, then comma separated
2026 
2027  bool allIntegers = true;
2028  for(auto& wildcard : wildcards)
2029  if(!allIntegers)
2030  break;
2031  else if(wildcard.size() == 0) // emtpy string is not a number
2032  {
2033  allIntegers = false;
2034  break;
2035  }
2036  else
2037  for(unsigned int i = 0; i < wildcard.size(); ++i)
2038  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
2039  {
2040  allIntegers = false;
2041  break;
2042  }
2043 
2044  __COUTV__(allIntegers);
2045  if(allIntegers)
2046  {
2047  std::set<unsigned int> intSortWildcards;
2048  //unsigned int tmpInt;
2049  for(auto& wildcard : wildcards)
2050  intSortWildcards.emplace(strtol(wildcard.c_str(), 0, 10));
2051 
2052  // need ints in vector for random access to for hyphenating
2053  std::vector<unsigned int> intWildcards;
2054  for(auto& wildcard : intSortWildcards)
2055  intWildcards.push_back(wildcard);
2056 
2057  __COUTV__(StringMacros::vectorToString(intWildcards));
2058 
2059  unsigned int hyphenLo = -1;
2060  bool isFirst = true;
2061  for(unsigned int i = 0; i < intWildcards.size(); ++i)
2062  {
2063  if(i + 1 < intWildcards.size() && intWildcards[i] + 1 == intWildcards[i + 1])
2064  {
2065  if(i < hyphenLo)
2066  hyphenLo = i; // start hyphen
2067  }
2068  else // new comma
2069  {
2070  if(i < hyphenLo)
2071  {
2072  // single number
2073  multiNodeString += (isFirst ? "" : ",") + std::to_string(intWildcards[i]);
2074  }
2075  else
2076  {
2077  // hyphen numbers
2078  multiNodeString +=
2079  (isFirst ? "" : ",") + std::to_string(intWildcards[hyphenLo]) + "-" + std::to_string(intWildcards[i]);
2080  hyphenLo = -1; // reset for next
2081  }
2082  isFirst = false;
2083  }
2084  }
2085  } // end all integer handling
2086  else // not all integers, so csv
2087  {
2088  multiNodeString = StringMacros::vectorToString(wildcards);
2089  } // end not-all integer handling
2090 
2091  __COUTV__(multiNodeString);
2092  __COUTV__(nodeFixedWildcardLength);
2093  } // end node name printer syntax handling
2094 
2095  if(hostnameArray.size() > 1)
2096  {
2097  std::vector<std::string> commonChunks;
2098  std::vector<std::string> wildcards;
2099 
2100  bool wildcardsNeeded = StringMacros::extractCommonChunks(hostnameArray, commonChunks, wildcards, hostFixedWildcardLength);
2101 
2102  hostname = "";
2103  bool first = true;
2104  for(auto& commonChunk : commonChunks)
2105  {
2106  hostname += (!first ? "*" : "") + commonChunk;
2107  if(first)
2108  first = false;
2109  }
2110  if(wildcardsNeeded && commonChunks.size() == 1)
2111  hostname += '*';
2112 
2113  __COUTV__(hostname);
2114 
2115  if(wildcardsNeeded)
2116  // else if not wildcards needed, then do not make hostname array string
2117  {
2118  // steps:
2119  // determine if all unsigned ints
2120  // if int, then order and attempt to hyphenate
2121  // if not ints, then comma separated
2122 
2123  bool allIntegers = true;
2124  for(auto& wildcard : wildcards)
2125  for(unsigned int i = 0; i < wildcard.size(); ++i)
2126  if(!(wildcard[i] >= '0' && wildcard[i] <= '9'))
2127  {
2128  allIntegers = false;
2129  break;
2130  }
2131 
2132  __COUTV__(allIntegers);
2133 
2134  if(allIntegers)
2135  {
2136  std::set<unsigned int> intSortWildcards;
2137  //unsigned int tmpInt;
2138  for(auto& wildcard : wildcards)
2139  intSortWildcards.emplace(strtol(wildcard.c_str(), 0, 10));
2140 
2141  // need ints in vector for random access to for hyphenating
2142  std::vector<unsigned int> intWildcards;
2143  for(auto& wildcard : intSortWildcards)
2144  intWildcards.push_back(wildcard);
2145 
2146  __COUTV__(StringMacros::vectorToString(intWildcards));
2147 
2148  unsigned int hyphenLo = -1;
2149  bool isFirst = true;
2150  for(unsigned int i = 0; i < intWildcards.size(); ++i)
2151  {
2152  if(i + 1 < intWildcards.size() && intWildcards[i] + 1 == intWildcards[i + 1])
2153  {
2154  if(i < hyphenLo)
2155  hyphenLo = i; // start hyphen
2156  }
2157  else // new comma
2158  {
2159  if(i < hyphenLo)
2160  {
2161  // single number
2162  hostArrayString += (isFirst ? "" : ",") + std::to_string(intWildcards[i]);
2163  }
2164  else
2165  {
2166  // hyphen numbers
2167  hostArrayString +=
2168  (isFirst ? "" : ",") + std::to_string(intWildcards[hyphenLo]) + "-" + std::to_string(intWildcards[i]);
2169  hyphenLo = -1; // reset for next
2170  }
2171  isFirst = false;
2172  }
2173  }
2174  } // end all integer handling
2175  else // not all integers, so csv
2176  {
2177  hostArrayString = StringMacros::vectorToString(wildcards);
2178  } // end not-all integer handling
2179  } // end wildcard need handling
2180  __COUTV__(hostArrayString);
2181  __COUTV__(hostFixedWildcardLength);
2182  } // end node name printer syntax handling
2183 
2184  } // end multi node printer syntax handling
2185 
2186  nodeTypeToObjectMap.at(typeString).emplace(std::make_pair(nodeName, std::vector<std::string /*property*/>()));
2187 
2188  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(status ? "1" : "0");
2189 
2190  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(hostname);
2191 
2192  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(subsystemId);
2193  if(multiNodeNames.size() > 1)
2194  {
2195  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(multiNodeString);
2196 
2197  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(std::to_string(nodeFixedWildcardLength));
2198 
2199  if(hostnameArray.size() > 1)
2200  {
2201  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(hostArrayString);
2202 
2203  nodeTypeToObjectMap.at(typeString).at(nodeName).push_back(std::to_string(hostFixedWildcardLength));
2204  }
2205  } // done adding multinode parameters
2206  }
2207  } // end processor type handling
2208 
2209  } // end artdaq app loop
2210 
2211  __COUT__ << "Done getting artdaq nodes." << __E__;
2212 
2213  return ARTDAQTableBase::info_;
2214 } // end getARTDAQSystem()
2215 
2216 //==============================================================================
2217 // setAndActivateARTDAQSystem
2218 //
2219 // static function to modify the active configuration based on
2220 // node object and subsystem object.
2221 //
2222 // Subsystem map to destination subsystem name.
2223 // Node properties: {originalName,status,hostname,subsystemName,(nodeArrString),(hostnameArrString),(hostnameFixedWidth)}
2224 //
2226  ConfigurationManagerRW* cfgMgr,
2227  const std::map<std::string /*type*/, std::map<std::string /*record*/, std::vector<std::string /*property*/>>>& nodeTypeToObjectMap,
2228  const std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>& subsystemObjectMap)
2229 {
2230  __COUT__ << "setAndActivateARTDAQSystem()" << __E__;
2231 
2232  const std::string& author = cfgMgr->getUsername();
2233 
2234  // Steps:
2235  // 0. Check for one and only artdaq Supervisor
2236  // 1. create/verify subsystems and destinations
2237  // 2. for each node
2238  // create/verify records
2239 
2240  //------------------------
2241  // 0. Check for one and only artdaq Supervisor
2242 
2243  GroupEditStruct configGroupEdit(ConfigurationManager::GroupType::CONFIGURATION_TYPE, cfgMgr);
2244 
2245  unsigned int artdaqSupervisorRow = TableView::INVALID;
2246 
2247  const XDAQContextTable* contextTable = cfgMgr->__GET_CONFIG__(XDAQContextTable);
2248 
2249  const XDAQContextTable::XDAQContext* artdaqContext = contextTable->getTheARTDAQSupervisorContext();
2250 
2251  bool needArtdaqSupervisorParents = true;
2252  bool needArtdaqSupervisorCreation = false;
2253 
2254  if(artdaqContext) // check for full connection to supervisor
2255  {
2256  try
2257  {
2258  ConfigurationTree artdaqSupervisorNode = cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
2259  .getNode(artdaqContext->contextUID_)
2260  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
2261  .getNode(artdaqContext->applications_[0].applicationUID_)
2262  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_);
2263 
2264  if(artdaqSupervisorNode.isDisconnected())
2265  needArtdaqSupervisorCreation = true;
2266  else
2267  artdaqSupervisorRow = artdaqSupervisorNode.getRow();
2268 
2269  needArtdaqSupervisorParents = false;
2270  }
2271  catch(...) // parents are a problem if error
2272  {
2273  needArtdaqSupervisorCreation = true;
2274  }
2275  }
2276 
2277  if(!artdaqContext || needArtdaqSupervisorCreation)
2278  {
2279  __COUT__ << "No artdaq Supervisor found! Creating..." << __E__;
2280  __COUTV__(needArtdaqSupervisorParents);
2281 
2282  std::string artdaqSupervisorUID;
2283  unsigned int row;
2284 
2285  // create record in ARTDAQ Supervisor table
2286  // connect to an App in a Context
2287 
2288  // now create artdaq Supervisor in configuration group
2289  {
2290  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
2291 
2292  // create artdaq Supervisor context record
2293  row = artdaqSupervisorTable.tableView_->addRow(author, true /*incrementUniqueData*/, "artdaqSupervisor");
2294 
2295  // get UID
2296  artdaqSupervisorUID = artdaqSupervisorTable.tableView_->getDataView()[row][artdaqSupervisorTable.tableView_->getColUID()];
2297  artdaqSupervisorRow = row;
2298 
2299  __COUTV__(artdaqSupervisorRow);
2300  __COUTV__(artdaqSupervisorUID);
2301 
2302  // set DAQInterfaceDebugLevel
2303  artdaqSupervisorTable.tableView_->setValueAsString(
2304  "1", row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colDAQInterfaceDebugLevel_));
2305  // set DAQSetupScript
2306  artdaqSupervisorTable.tableView_->setValueAsString(
2307  "${MRB_BUILDDIR}/../setup_ots.sh", row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colDAQSetupScript_));
2308 
2309  // create group link to board readers
2310  artdaqSupervisorTable.tableView_->setValueAsString(
2311  ARTDAQ_READER_TABLE, row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToBoardReaders_));
2312  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2313 
2314  row,
2315  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
2316  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
2317  // create group link to event builders
2318  artdaqSupervisorTable.tableView_->setValueAsString(
2319  ARTDAQ_BUILDER_TABLE, row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToEventBuilders_));
2320  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2321  row,
2322  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
2323  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
2324  // create group link to data loggers
2325  artdaqSupervisorTable.tableView_->setValueAsString(
2326  ARTDAQ_LOGGER_TABLE, row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDataLoggers_));
2327  artdaqSupervisorTable.tableView_->setUniqueColumnValue(row,
2328  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
2329  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
2330  // create group link to dispatchers
2331  artdaqSupervisorTable.tableView_->setValueAsString(
2332  ARTDAQ_DISPATCHER_TABLE, row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDispatchers_));
2333  artdaqSupervisorTable.tableView_->setUniqueColumnValue(row,
2334  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
2335  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
2336 
2337  // create group link to routing masters
2338  artdaqSupervisorTable.tableView_->setValueAsString(
2339  ARTDAQ_ROUTER_TABLE, row, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToRoutingMasters_));
2340  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2341  row,
2342  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToRoutingMastersGroupID_),
2343  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
2344 
2345  {
2346  std::stringstream ss;
2347  artdaqSupervisorTable.tableView_->print(ss);
2348  __COUT__ << ss.str();
2349  }
2350  } // end create artdaq Supervisor in configuration group
2351 
2352  // now create artdaq Supervisor parents in context group
2353  {
2354  GroupEditStruct contextGroupEdit(ConfigurationManager::GroupType::CONTEXT_TYPE, cfgMgr);
2355 
2356  TableEditStruct& contextTable = contextGroupEdit.getTableEditStruct(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME, true /*markModified*/);
2357  TableEditStruct& appTable = contextGroupEdit.getTableEditStruct(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME, true /*markModified*/);
2358  TableEditStruct& appPropertyTable = contextGroupEdit.getTableEditStruct(ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME, true /*markModified*/);
2359 
2360  // open try for decorating errors and for clean code scope
2361  try
2362  {
2363  std::string contextUID;
2364  std::string contextAppGroupID;
2365 
2366  if(needArtdaqSupervisorParents)
2367  {
2368  // create artdaq Supervisor context record
2369  row = contextTable.tableView_->addRow(author, true /*incrementUniqueData*/, "artdaqContext");
2370  // set context status true
2371  contextTable.tableView_->setValueAsString("1", row, contextTable.tableView_->getColStatus());
2372 
2373  contextUID = contextTable.tableView_->getDataView()[row][contextTable.tableView_->getColUID()];
2374 
2375  __COUTV__(row);
2376  __COUTV__(contextUID);
2377 
2378  // set address/port
2379  contextTable.tableView_->setValueAsString(
2380  "http://${HOSTNAME}", row, contextTable.tableView_->findCol(XDAQContextTable::colContext_.colAddress_));
2381  contextTable.tableView_->setUniqueColumnValue(
2382  row, contextTable.tableView_->findCol(XDAQContextTable::colContext_.colPort_), "${OTS_MAIN_PORT}", true /*doMathAppendStrategy*/);
2383 
2384  // create group link to artdaq Supervisor app
2385  contextTable.tableView_->setValueAsString(ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME,
2386  row,
2387  contextTable.tableView_->findCol(XDAQContextTable::colContext_.colLinkToApplicationTable_));
2388  contextAppGroupID = contextTable.tableView_->setUniqueColumnValue(
2389  row, contextTable.tableView_->findCol(XDAQContextTable::colContext_.colLinkToApplicationGroupID_), "artdaqContextApps");
2390 
2391  __COUTV__(contextAppGroupID);
2392 
2393  } // end create context entry
2394 
2395  std::string appUID;
2396  std::string appPropertiesGroupID;
2397 
2398  // create artdaq Supervisor app
2399  {
2400  unsigned int row;
2401 
2402  if(needArtdaqSupervisorParents)
2403  {
2404  // first disable any existing artdaq supervisor apps
2405  {
2406  unsigned int c = appTable.tableView_->findCol(XDAQContextTable::colApplication_.colClass_);
2407  for(unsigned int r = 0; r < appTable.tableView_->getNumberOfRows(); ++r)
2408  if(appTable.tableView_->getDataView()[r][c] == ARTDAQ_SUPERVISOR_CLASS)
2409  {
2410  __COUT_WARN__ << "Found partially existing artdaq Supervisor application '"
2411  << appTable.tableView_->getDataView()[r][appTable.tableView_->getColUID()] << "'... Disabling it." << __E__;
2412  appTable.tableView_->setValueAsString("0", r, appTable.tableView_->getColStatus());
2413  }
2414  }
2415 
2416  // create artdaq Supervisor context record
2417  row = appTable.tableView_->addRow(author, true /*incrementUniqueData*/, "artdaqSupervisor");
2418  // set app status true
2419  appTable.tableView_->setValueAsString("1", row, appTable.tableView_->getColStatus());
2420 
2421  appUID = appTable.tableView_->getDataView()[row][appTable.tableView_->getColUID()];
2422 
2423  __COUTV__(row);
2424  __COUTV__(appUID);
2425 
2426  // set class
2427  appTable.tableView_->setValueAsString(
2428  ARTDAQ_SUPERVISOR_CLASS, row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colClass_));
2429  // set module
2430  appTable.tableView_->setValueAsString(
2431  "${OTSDAQ_LIB}/libARTDAQSupervisor.so", row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colModule_));
2432  // set groupid
2433  appTable.tableView_->setValueAsString(
2434  contextAppGroupID, row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colApplicationGroupID_));
2435 
2436  // create group link to artdaq Supervisor app properties
2437  appTable.tableView_->setValueAsString(ConfigurationManager::XDAQ_APP_PROPERTY_TABLE_NAME,
2438  row,
2439  appTable.tableView_->findCol(XDAQContextTable::colApplication_.colLinkToPropertyTable_));
2440  appPropertiesGroupID = appTable.tableView_->setUniqueColumnValue(
2441  row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colLinkToPropertyGroupID_), appUID + "Properties");
2442 
2443  __COUTV__(appPropertiesGroupID);
2444  }
2445  else
2446  {
2447  __COUT__ << "Getting row of existing parent supervisor." << __E__;
2448 
2449  // get row of current artdaq supervisor app
2450  row = cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
2451  .getNode(artdaqContext->contextUID_)
2452  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
2453  .getNode(artdaqContext->applications_[0].applicationUID_)
2454  .getRow();
2455  __COUTV__(row);
2456  }
2457 
2458  // create group link to artdaq Supervisor app properties
2459  // create link whether or not parents were created
2460  // because, if here, then artdaq supervisor record was created.
2461  appTable.tableView_->setValueAsString(
2462  ARTDAQ_SUPERVISOR_TABLE, row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colLinkToSupervisorTable_));
2463  appTable.tableView_->setValueAsString(
2464  artdaqSupervisorUID, row, appTable.tableView_->findCol(XDAQContextTable::colApplication_.colLinkToSupervisorUID_));
2465 
2466  } // end create app entry
2467 
2468  // create artdaq Supervisor properties
2469  if(needArtdaqSupervisorParents)
2470  {
2471  unsigned int row;
2472 
2473  const std::vector<std::string> propertyUIDs = {
2474  "Partition0", "ProductsDir", "FragmentSize", "BoardReaderTimeout", "EventBuilderTimeout", "DataLoggerTimeout", "DispatcherTimeout"};
2475  const std::vector<std::string> propertyNames = {
2476  "partition", //"Partition0",
2477  "productsdir_for_bash_scripts", //"ProductsDir",
2478  "max_fragment_size_bytes", //"FragmentSize",
2479  "boardreader_timeout", //"BoardReaderTimeout",
2480  "eventbuilder_timeout", //"EventBuilderTimeout",
2481  "datalogger_timeout", //"DataLoggerTimeout",
2482  "dispatcher_timeout" //"DispatcherTimeout"
2483  };
2484  const std::vector<std::string> propertyValues = {
2485  "0", //"Partition0",
2486  "${OTS_PRODUCTS}", //"ProductsDir",
2487  "1284180560", //"FragmentSize",
2488  "600", //"BoardReaderTimeout",
2489  "600", //"EventBuilderTimeout",
2490  "600", //"DataLoggerTimeout",
2491  "600" //"DispatcherTimeout"
2492  };
2493 
2494  for(unsigned int i = 0; i < propertyNames.size(); ++i)
2495  {
2496  // create artdaq Supervisor context record
2497  row = appPropertyTable.tableView_->addRow(author, true /*incrementUniqueData*/, appUID + propertyUIDs[i]);
2498  // set app status true
2499  appPropertyTable.tableView_->setValueAsString("1", row, appPropertyTable.tableView_->getColStatus());
2500 
2501  // set type
2502  appPropertyTable.tableView_->setValueAsString(
2503  "ots::SupervisorProperty", row, appPropertyTable.tableView_->findCol(XDAQContextTable::colAppProperty_.colPropertyType_));
2504  // set name
2505  appPropertyTable.tableView_->setValueAsString(
2506  propertyNames[i], row, appPropertyTable.tableView_->findCol(XDAQContextTable::colAppProperty_.colPropertyName_));
2507  // set value
2508  appPropertyTable.tableView_->setValueAsString(
2509  propertyValues[i], row, appPropertyTable.tableView_->findCol(XDAQContextTable::colAppProperty_.colPropertyValue_));
2510  // set groupid
2511  appPropertyTable.tableView_->setValueAsString(
2512  appPropertiesGroupID, row, appPropertyTable.tableView_->findCol(XDAQContextTable::colAppProperty_.colPropertyGroupID_));
2513  } // end property create loop
2514  } // end create app property entries
2515 
2516  {
2517  std::stringstream ss;
2518  contextTable.tableView_->print(ss);
2519  __COUT__ << ss.str();
2520  }
2521  {
2522  std::stringstream ss;
2523  appTable.tableView_->print(ss);
2524  __COUT__ << ss.str();
2525  }
2526  {
2527  std::stringstream ss;
2528  appPropertyTable.tableView_->print(ss);
2529  __COUT__ << ss.str();
2530  }
2531 
2532  contextTable.tableView_->init(); // verify new table (throws runtime_errors)
2533  appTable.tableView_->init(); // verify new table (throws runtime_errors)
2534  appPropertyTable.tableView_->init(); // verify new table (throws runtime_errors)
2535  }
2536  catch(...)
2537  {
2538  __COUT__ << "Table errors while creating ARTDAQ Supervisor. Erasing all newly "
2539  "created table versions."
2540  << __E__;
2541  throw; // re-throw
2542  } // end catch
2543 
2544  __COUT__ << "Edits complete for new artdaq Supervisor!" << __E__;
2545 
2546  TableGroupKey newContextGroupKey;
2547  contextGroupEdit.saveChanges(contextGroupEdit.originalGroupName_,
2548  newContextGroupKey,
2549  nullptr /*foundEquivalentGroupKey*/,
2550  true /*activateNewGroup*/,
2551  true /*updateGroupAliases*/,
2552  true /*updateTableAliases*/);
2553 
2554  } // end create artdaq Supervisor in context group
2555 
2556  } // end artdaq Supervisor verification
2557  else
2558  {
2559  artdaqSupervisorRow = cfgMgr->getNode(ConfigurationManager::XDAQ_CONTEXT_TABLE_NAME)
2560  .getNode(artdaqContext->contextUID_)
2561  .getNode(XDAQContextTable::colContext_.colLinkToApplicationTable_)
2562  .getNode(artdaqContext->applications_[0].applicationUID_)
2563  .getNode(XDAQContextTable::colApplication_.colLinkToSupervisorTable_)
2564  .getRow();
2565  }
2566 
2567  __COUT__ << "------------------------- artdaq nodes to save:" << __E__;
2568  for(auto& subsystemPair : subsystemObjectMap)
2569  {
2570  __COUTV__(subsystemPair.first);
2571 
2572  } // end subsystem loop
2573 
2574  for(auto& nodeTypePair : nodeTypeToObjectMap)
2575  {
2576  __COUTV__(nodeTypePair.first);
2577 
2578  for(auto& nodePair : nodeTypePair.second)
2579  {
2580  __COUTV__(nodePair.first);
2581  }
2582 
2583  } // end node type loop
2584  __COUT__ << "------------------------- end artdaq nodes to save." << __E__;
2585 
2586  //==================================
2587  // at this point artdaqSupervisor is verified and we have row
2588  __COUTV__(artdaqSupervisorRow);
2589  if(artdaqSupervisorRow >= TableView::INVALID)
2590  {
2591  __SS__ << "Invalid artdaq Supervisor row " << artdaqSupervisorRow << " found!" << __E__;
2592  __SS_THROW__;
2593  }
2594 
2595  // Remaining steps:
2596  // Step 1. create/verify subsystems and destinations
2597  // Step 2. for each node, create/verify records
2598 
2599  // open try for decorating configuration group errors and for clean code scope
2600  try
2601  {
2602  unsigned int row;
2603 
2604  TableEditStruct& artdaqSupervisorTable = configGroupEdit.getTableEditStruct(ARTDAQ_SUPERVISOR_TABLE, true /*markModified*/);
2605 
2606  // for any NO_LINK links in artdaqSupervisor record, fix them
2607  {
2608  std::string artdaqSupervisorUID =
2609  artdaqSupervisorTable.tableView_->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->getColUID()];
2610 
2611  // create group link to board readers
2612  if(artdaqSupervisorTable.tableView_
2613  ->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToBoardReaders_)] ==
2614  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
2615  {
2616  __COUT__ << "Fixing missing link to Readers" << __E__;
2617  artdaqSupervisorTable.tableView_->setValueAsString(
2618  ARTDAQ_READER_TABLE, artdaqSupervisorRow, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToBoardReaders_));
2619  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2620  artdaqSupervisorRow,
2621  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToBoardReadersGroupID_),
2622  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.READER));
2623  }
2624 
2625  // create group link to event builders
2626  if(artdaqSupervisorTable.tableView_
2627  ->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToEventBuilders_)] ==
2628  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
2629  {
2630  __COUT__ << "Fixing missing link to Builders" << __E__;
2631  artdaqSupervisorTable.tableView_->setValueAsString(
2632  ARTDAQ_BUILDER_TABLE, artdaqSupervisorRow, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToEventBuilders_));
2633  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2634  artdaqSupervisorRow,
2635  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToEventBuildersGroupID_),
2636  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.BUILDER));
2637  }
2638 
2639  // create group link to data loggers
2640  if(artdaqSupervisorTable.tableView_
2641  ->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDataLoggers_)] ==
2642  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
2643  {
2644  __COUT__ << "Fixing missing link to Loggers" << __E__;
2645  artdaqSupervisorTable.tableView_->setValueAsString(
2646  ARTDAQ_LOGGER_TABLE, artdaqSupervisorRow, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDataLoggers_));
2647  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2648  artdaqSupervisorRow,
2649  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDataLoggersGroupID_),
2650  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.LOGGER));
2651  }
2652 
2653  // create group link to dispatchers
2654  if(artdaqSupervisorTable.tableView_
2655  ->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDispatchers_)] ==
2656  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
2657  {
2658  __COUT__ << "Fixing missing link to Dispatchers" << __E__;
2659  artdaqSupervisorTable.tableView_->setValueAsString(
2660  ARTDAQ_DISPATCHER_TABLE, artdaqSupervisorRow, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDispatchers_));
2661  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2662  artdaqSupervisorRow,
2663  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToDispatchersGroupID_),
2664  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.DISPATCHER));
2665  }
2666 
2667  // create group link to routing masters
2668  if(artdaqSupervisorTable.tableView_
2669  ->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToRoutingMasters_)] ==
2670  TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
2671  {
2672  __COUT__ << "Fixing missing link to Routers" << __E__;
2673  artdaqSupervisorTable.tableView_->setValueAsString(
2674  ARTDAQ_ROUTER_TABLE, artdaqSupervisorRow, artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToRoutingMasters_));
2675  artdaqSupervisorTable.tableView_->setUniqueColumnValue(
2676  artdaqSupervisorRow,
2677  artdaqSupervisorTable.tableView_->findCol(colARTDAQSupervisor_.colLinkToRoutingMastersGroupID_),
2678  artdaqSupervisorUID + processTypes_.mapToGroupIDAppend_.at(processTypes_.ROUTER));
2679  }
2680 
2681  {
2682  std::stringstream ss;
2683  artdaqSupervisorTable.tableView_->print(ss);
2684  __COUT__ << ss.str();
2685  }
2686  } // end fixing links
2687 
2688  // Step 1. create/verify subsystems and destinations
2689  TableEditStruct& artdaqSubsystemTable = configGroupEdit.getTableEditStruct(ARTDAQ_SUBSYSTEM_TABLE, true /*markModified*/);
2690 
2691  // clear all records
2692  artdaqSubsystemTable.tableView_->deleteAllRows();
2693 
2694  for(auto& subsystemPair : subsystemObjectMap)
2695  {
2696  __COUTV__(subsystemPair.first);
2697  __COUTV__(subsystemPair.second);
2698 
2699  // create artdaq Subsystem record
2700  row = artdaqSubsystemTable.tableView_->addRow(author, true /*incrementUniqueData*/, subsystemPair.first);
2701 
2702  if(subsystemPair.second != "" && subsystemPair.second != TableViewColumnInfo::DATATYPE_STRING_DEFAULT &&
2703  subsystemPair.second != NULL_SUBSYSTEM_DESTINATION_LABEL)
2704  {
2705  // set subsystem link
2706  artdaqSubsystemTable.tableView_->setValueAsString(
2707  ARTDAQ_SUBSYSTEM_TABLE, row, artdaqSubsystemTable.tableView_->findCol(colARTDAQSubsystem_.colLinkToDestination_));
2708  artdaqSubsystemTable.tableView_->setValueAsString(
2709  subsystemPair.second, row, artdaqSubsystemTable.tableView_->findCol(colARTDAQSubsystem_.colLinkToDestinationUID_));
2710  }
2711  // else leave disconnected link
2712 
2713  } // end subsystem loop
2714 
2715  // Step 2. for each node, create/verify records
2716  for(auto& nodeTypePair : nodeTypeToObjectMap)
2717  {
2718  __COUTV__(nodeTypePair.first);
2719 
2720  //__COUTV__(StringMacros::mapToString(processTypes_.mapToTable_));
2721 
2722  auto it = processTypes_.mapToTable_.find(nodeTypePair.first);
2723  if(it == processTypes_.mapToTable_.end())
2724  {
2725  __SS__ << "Invalid artdaq node type '" << nodeTypePair.first << "' attempted!" << __E__;
2726  __SS_THROW__;
2727  }
2728  __COUTV__(it->second);
2729 
2730  // test the table before getting for real
2731  try
2732  {
2733  /* TableEditStruct& tmpTypeTable = */ configGroupEdit.getTableEditStruct(it->second, true /*markModified*/);
2734  }
2735  catch(...)
2736  {
2737  if(nodeTypePair.second.size())
2738  throw; // do not ignore if user was trying to save records
2739 
2740  __COUT__ << "Ignoring missing table '" << it->second << "' since there were no user records attempted of type '" << nodeTypePair.first << ".'"
2741  << __E__;
2742  continue;
2743  }
2744  TableEditStruct& typeTable = configGroupEdit.getTableEditStruct(it->second, true /*markModified*/);
2745 
2746  // keep track of records to delete, initialize to all in current table
2747  std::map<unsigned int /*type record row*/, bool /*doDelete*/> deleteRecordMap;
2748  for(unsigned int r = 0; r < typeTable.tableView_->getNumberOfRows(); ++r)
2749  deleteRecordMap.emplace(std::make_pair(r, // typeTable.tableView_->getDataView()[i][typeTable.tableView_->getColUID()],
2750  true)); // init to delete
2751 
2752  //node instance loop
2753  for(auto& nodePair : nodeTypePair.second)
2754  {
2755  __COUTV__(nodePair.first);
2756 
2757  // default multi-node and array hostname info to empty
2758  std::vector<std::string> nodeIndices, hostnameIndices;
2759  unsigned int hostnameFixedWidth = 0, nodeNameFixedWidth = 0;
2760  std::string hostname;
2761 
2762  // keep a map original multinode values, to maintain node specific links
2763  // (emplace when original node is delete)
2764  std::map<std::string /*originalMultiNode name*/, std::map<unsigned int /*col*/, std::string /*value*/>> originalMultinodeValues;
2765 
2766  // if original record is found, then commandeer that record
2767  // else create a new record
2768  // Node properties: {originalName,hostname,subsystemName,(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)}
2769 
2770  // node parameter loop
2771  for(unsigned int i = 0; i < nodePair.second.size(); ++i)
2772  {
2773  __COUTV__(nodePair.second[i]);
2774 
2775  if(i == 0) // original UID
2776  {
2777  std::string nodeName;
2778  // Steps:
2779  // if original was multi-node,
2780  // then delete all but one
2781  // else
2782  // take over the row, or create new
2783  if(nodePair.second[i][0] == ':')
2784  {
2785  __COUT__ << "Handling original multi-node." << __E__;
2786 
2787  // format:
2788  // :<nodeNameFixedWidth>:<nodeVectorIndexString>:<nodeNameTemplate>
2789 
2790  std::vector<std::string> originalParameterArr =
2791  StringMacros::getVectorFromString(&(nodePair.second[i].c_str()[1]), {':'} /*delimiter*/);
2792 
2793  if(originalParameterArr.size() != 3)
2794  {
2795  __SS__ << "Illegal original name parameter string '" << nodePair.second[i] << "!'" << __E__;
2796  __SS_THROW__;
2797  }
2798 
2799  unsigned int fixedWidth;
2800  sscanf(originalParameterArr[0].c_str(), "%u", &fixedWidth);
2801  __COUTV__(fixedWidth);
2802 
2803  std::vector<std::string> printerSyntaxArr = StringMacros::getVectorFromString(originalParameterArr[1], {','} /*delimiter*/);
2804 
2805  //unsigned int count = 0;
2806  std::vector<std::string> originalNodeIndices;
2807  for(auto& printerSyntaxValue : printerSyntaxArr)
2808  {
2809  __COUTV__(printerSyntaxValue);
2810 
2811  std::vector<std::string> printerSyntaxRange = StringMacros::getVectorFromString(printerSyntaxValue, {'-'} /*delimiter*/);
2812 
2813  if(printerSyntaxRange.size() == 0 || printerSyntaxRange.size() > 2)
2814  {
2815  __SS__ << "Illegal multi-node printer syntax string '" << printerSyntaxValue << "!'" << __E__;
2816  __SS_THROW__;
2817  }
2818  else if(printerSyntaxRange.size() == 1)
2819  {
2820  __COUTV__(printerSyntaxRange[0]);
2821  originalNodeIndices.push_back(printerSyntaxRange[0]);
2822  }
2823  else // printerSyntaxRange.size() == 2
2824  {
2825  unsigned int lo, hi;
2826  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
2827  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
2828  if(hi < lo) // swap
2829  {
2830  lo = hi;
2831  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
2832  }
2833  for(; lo <= hi; ++lo)
2834  {
2835  __COUTV__(lo);
2836  originalNodeIndices.push_back(std::to_string(lo));
2837  }
2838  }
2839  } // end printer syntax loop
2840 
2841  std::vector<std::string> originalNamePieces = StringMacros::getVectorFromString(originalParameterArr[2], {'*'} /*delimiter*/);
2842  __COUTV__(StringMacros::vectorToString(originalNamePieces));
2843 
2844  if(originalNamePieces.size() < 2)
2845  {
2846  __SS__ << "Illegal original multi-node name template - please use * to indicate where the multi-node index should be inserted!"
2847  << __E__;
2848  __SS_THROW__;
2849  }
2850 
2851  //bool isFirst = true;
2852  unsigned int originalRow = TableView::INVALID, lastOriginalRow = TableView::INVALID;
2853  for(unsigned int i = 0; i < originalNodeIndices.size(); ++i)
2854  {
2855  std::string originalName = originalNamePieces[0];
2856  std::string nodeNameIndex;
2857  for(unsigned int p = 1; p < originalNamePieces.size(); ++p)
2858  {
2859  nodeNameIndex = originalNodeIndices[i];
2860  if(fixedWidth > 1)
2861  {
2862  if(nodeNameIndex.size() > fixedWidth)
2863  {
2864  __SS__ << "Illegal original node name index '" << nodeNameIndex
2865  << "' - length is longer than fixed width requirement of " << fixedWidth << "!" << __E__;
2866  __SS_THROW__;
2867  }
2868 
2869  // 0 prepend as needed
2870  while(nodeNameIndex.size() < fixedWidth)
2871  nodeNameIndex = "0" + nodeNameIndex;
2872  } // end fixed width handling
2873 
2874  originalName += nodeNameIndex + originalNamePieces[p];
2875  }
2876  __COUTV__(originalName);
2877  originalRow =
2878  typeTable.tableView_->findRow(typeTable.tableView_->getColUID(), originalName, 0 /*offsetRow*/, true /*doNotThrow*/);
2879  __COUTV__(originalRow);
2880 
2881  // if have a new valid row, then delete last valid row
2882  if(originalRow != TableView::INVALID && lastOriginalRow != TableView::INVALID)
2883  {
2884  // before deleting, record all customizing values and maintain when saving
2885  originalMultinodeValues.emplace(std::make_pair(nodeName, std::map<unsigned int /*col*/, std::string /*value*/>()));
2886 
2887  __COUT__ << "Saving multinode value " << nodeName << "[" << lastOriginalRow
2888  << "][*] with row count = " << typeTable.tableView_->getNumberOfRows() << __E__;
2889 
2890  // save all link values
2891  for(unsigned int col = 0; col < typeTable.tableView_->getNumberOfColumns(); ++col)
2892  if(typeTable.tableView_->getColumnInfo(col).getName() == ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK ||
2893  typeTable.tableView_->getColumnInfo(col).getName() == ARTDAQTableBase::ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID)
2894  continue; // skip subsystem link
2895  else if(typeTable.tableView_->getColumnInfo(col).isChildLink() ||
2896  typeTable.tableView_->getColumnInfo(col).isChildLinkGroupID() ||
2897  typeTable.tableView_->getColumnInfo(col).isChildLinkUID())
2898  originalMultinodeValues.at(nodeName).emplace(
2899  std::make_pair(col, typeTable.tableView_->getDataView()[lastOriginalRow][col]));
2900 
2901  typeTable.tableView_->deleteRow(lastOriginalRow);
2902  if(originalRow > lastOriginalRow)
2903  --originalRow; // modify after delete
2904  }
2905 
2906  if(originalRow != TableView::INVALID)
2907  {
2908  lastOriginalRow = originalRow; // save last valid row for future deletion
2909  nodeName = originalName;
2910  }
2911 
2912  } // end loop through multi-node instances
2913 
2914  row = lastOriginalRow; // take last valid row to proceed
2915 
2916  __COUTV__(nodeName);
2917  __COUTV__(row);
2918 
2919  } // end handling of original multinode
2920  else
2921  {
2922  // attempt to find original 'single' node name
2923  row = typeTable.tableView_->findRow(typeTable.tableView_->getColUID(), nodePair.second[i], 0 /*offsetRow*/, true /*doNotThrow*/);
2924  __COUTV__(row);
2925 
2926  nodeName = nodePair.first; // take new node name
2927  }
2928 
2929  __COUTV__(nodeName);
2930  if(row == TableView::INVALID)
2931  {
2932  // create artdaq type instance record
2933  row = typeTable.tableView_->addRow(author, true /*incrementUniqueData*/, nodeName);
2934 
2935  //fill defaults properties/parameters here!
2936  if(nodeTypePair.first == processTypes_.READER)
2937  {
2938  __COUT__ << "Handling new " << processTypes_.READER << " defaults!" << __E__;
2939  }
2940  }
2941  else // set UID
2942  {
2943  typeTable.tableView_->setValueAsString(nodeName, row, typeTable.tableView_->getColUID());
2944  }
2945  __COUTV__(row);
2946 
2947  // remove from delete map
2948  deleteRecordMap[row] = false;
2949 
2950  __COUTV__(StringMacros::mapToString(processTypes_.mapToLinkGroupIDColumn_));
2951 
2952  // set GroupID
2953  typeTable.tableView_->setValueAsString(
2954  artdaqSupervisorTable.tableView_->getDataView()[artdaqSupervisorRow][artdaqSupervisorTable.tableView_->findCol(
2955  processTypes_.mapToLinkGroupIDColumn_.at(nodeTypePair.first))],
2956  row,
2957  typeTable.tableView_->findCol(processTypes_.mapToGroupIDColumn_.at(nodeTypePair.first)));
2958  }
2959  else if(i == 1) // status
2960  {
2961  // enable/disable the target row
2962  typeTable.tableView_->setValueAsString(nodePair.second[i], row, typeTable.tableView_->getColStatus());
2963  }
2964  else if(i == 2) // hostname
2965  {
2966  // set hostname
2967  hostname = nodePair.second[i];
2968  typeTable.tableView_->setValueAsString(hostname, row, typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME));
2969  }
2970  else if(i == 3) // subsystemName
2971  {
2972  // set subsystemName
2973  if(nodePair.second[i] != "" && nodePair.second[i] != TableViewColumnInfo::DATATYPE_STRING_DEFAULT)
2974  {
2975  // real subsystem?
2976  if(subsystemObjectMap.find(nodePair.second[i]) == subsystemObjectMap.end())
2977  {
2978  __SS__ << "Illegal subsystem '" << nodePair.second[i] << "' mismatch!" << __E__;
2979  __SS_THROW__;
2980  }
2981 
2982  typeTable.tableView_->setValueAsString(
2983  ARTDAQ_SUBSYSTEM_TABLE, row, typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
2984  typeTable.tableView_->setValueAsString(
2985  nodePair.second[i], row, typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK_UID));
2986  }
2987  else // no subsystem (i.e. default subsystem)
2988  {
2989  typeTable.tableView_->setValueAsString(
2990  TableViewColumnInfo::DATATYPE_LINK_DEFAULT, row, typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_SUBSYSTEM_LINK));
2991  }
2992  }
2993  else if(i == 4 || i == 5 || i == 6 || i == 7) //(nodeArrString),(nodeNameFixedWidth),(hostnameArrString),(hostnameFixedWidth)
2994  {
2995  // fill multi-node and array hostname info to empty
2996  // then handle after all parameters in hand.
2997 
2998  __COUT__ << "Handling printer syntax i=" << i << __E__;
2999 
3000  std::vector<std::string> printerSyntaxArr = StringMacros::getVectorFromString(nodePair.second[i], {','} /*delimiter*/);
3001 
3002  if(printerSyntaxArr.size() == 2) // consider if fixed value
3003  {
3004  if(printerSyntaxArr[0] == "nnfw") // then node name fixed width
3005  {
3006  sscanf(printerSyntaxArr[1].c_str(), "%u", &nodeNameFixedWidth);
3007  __COUTV__(nodeNameFixedWidth);
3008  continue;
3009  }
3010  else if(printerSyntaxArr[0] == "hnfw") // then hostname fixed width
3011  {
3012  sscanf(printerSyntaxArr[1].c_str(), "%u", &hostnameFixedWidth);
3013  __COUTV__(hostnameFixedWidth);
3014  continue;
3015  }
3016  }
3017 
3018  //unsigned int count = 0;
3019  for(auto& printerSyntaxValue : printerSyntaxArr)
3020  {
3021  __COUTV__(printerSyntaxValue);
3022 
3023  std::vector<std::string> printerSyntaxRange = StringMacros::getVectorFromString(printerSyntaxValue, {'-'} /*delimiter*/);
3024  if(printerSyntaxRange.size() == 0 || printerSyntaxRange.size() > 2)
3025  {
3026  __SS__ << "Illegal multi-node printer syntax string '" << printerSyntaxValue << "!'" << __E__;
3027  __SS_THROW__;
3028  }
3029  else if(printerSyntaxRange.size() == 1)
3030  {
3031  // unsigned int index;
3032  __COUTV__(printerSyntaxRange[0]);
3033  // sscanf(printerSyntaxRange[0].c_str(), "%u", &index);
3034  //__COUTV__(index);
3035 
3036  if(i == 4 /*nodeArrayString*/)
3037  nodeIndices.push_back(printerSyntaxRange[0]);
3038  else
3039  hostnameIndices.push_back(printerSyntaxRange[0]);
3040  }
3041  else // printerSyntaxRange.size() == 2
3042  {
3043  unsigned int lo, hi;
3044  sscanf(printerSyntaxRange[0].c_str(), "%u", &lo);
3045  sscanf(printerSyntaxRange[1].c_str(), "%u", &hi);
3046  if(hi < lo) // swap
3047  {
3048  lo = hi;
3049  sscanf(printerSyntaxRange[0].c_str(), "%u", &hi);
3050  }
3051  for(; lo <= hi; ++lo)
3052  {
3053  __COUTV__(lo);
3054  if(i == 4 /*nodeArrayString*/)
3055  nodeIndices.push_back(std::to_string(lo));
3056  else
3057  hostnameIndices.push_back(std::to_string(lo));
3058  }
3059  }
3060  }
3061  }
3062  else
3063  {
3064  __SS__ << "Unexpected parameter[" << i << " '" << nodePair.second[i] << "' for node " << nodePair.first << "!" << __E__;
3065  __SS_THROW__;
3066  }
3067  } // end node parameter loop
3068 
3069  __COUTV__(nodeIndices.size());
3070  __COUTV__(hostnameIndices.size());
3071 
3072  if(hostnameIndices.size()) // handle hostname array
3073  {
3074  if(hostnameIndices.size() != nodeIndices.size())
3075  {
3076  __SS__ << "Illegal associated hostname array has count " << hostnameIndices.size() << " which is not equal to the node count "
3077  << nodeIndices.size() << "!" << __E__;
3078  __SS_THROW__;
3079  }
3080  }
3081 
3082  if(nodeIndices.size()) // handle multi-node instances
3083  {
3084  unsigned int hostnameCol = typeTable.tableView_->findCol(ARTDAQ_TYPE_TABLE_HOSTNAME);
3085  // Steps:
3086  // first instance takes current row,
3087  // then copy for remaining instances
3088 
3089  std::vector<std::string> namePieces = StringMacros::getVectorFromString(nodePair.first, {'*'} /*delimiter*/);
3090  __COUTV__(StringMacros::vectorToString(namePieces));
3091 
3092  if(namePieces.size() < 2)
3093  {
3094  __SS__ << "Illegal multi-node name template - please use * to indicate where the multi-node index should be inserted!" << __E__;
3095  __SS_THROW__;
3096  }
3097 
3098  std::vector<std::string> hostnamePieces;
3099  if(hostnameIndices.size()) // handle hostname array
3100  {
3101  hostnamePieces = StringMacros::getVectorFromString(hostname, {'*'} /*delimiter*/);
3102  __COUTV__(StringMacros::vectorToString(hostnamePieces));
3103 
3104  if(hostnamePieces.size() < 2)
3105  {
3106  __SS__ << "Illegal hostname array template - please use * to indicate where the hostname index should be inserted!" << __E__;
3107  __SS_THROW__;
3108  }
3109  }
3110 
3111  bool isFirst = true;
3112  for(unsigned int i = 0; i < nodeIndices.size(); ++i)
3113  {
3114  std::string name = namePieces[0];
3115  std::string nodeNameIndex;
3116  for(unsigned int p = 1; p < namePieces.size(); ++p)
3117  {
3118  nodeNameIndex = nodeIndices[i];
3119  if(nodeNameFixedWidth > 1)
3120  {
3121  if(nodeNameIndex.size() > nodeNameFixedWidth)
3122  {
3123  __SS__ << "Illegal node name index '" << nodeNameIndex << "' - length is longer than fixed width requirement of "
3124  << nodeNameFixedWidth << "!" << __E__;
3125  __SS_THROW__;
3126  }
3127 
3128  // 0 prepend as needed
3129  while(nodeNameIndex.size() < nodeNameFixedWidth)
3130  nodeNameIndex = "0" + nodeNameIndex;
3131  } // end fixed width handling
3132 
3133  name += nodeNameIndex + namePieces[p];
3134  }
3135  __COUTV__(name);
3136 
3137  if(hostnamePieces.size())
3138  {
3139  hostname = hostnamePieces[0];
3140  std::string hostnameIndex;
3141  for(unsigned int p = 1; p < hostnamePieces.size(); ++p)
3142  {
3143  hostnameIndex = hostnameIndices[i];
3144  if(hostnameFixedWidth > 1)
3145  {
3146  if(hostnameIndex.size() > hostnameFixedWidth)
3147  {
3148  __SS__ << "Illegal hostname index '" << hostnameIndex << "' - length is longer than fixed width requirement of "
3149  << hostnameFixedWidth << "!" << __E__;
3150  __SS_THROW__;
3151  }
3152 
3153  // 0 prepend as needed
3154  while(hostnameIndex.size() < hostnameFixedWidth)
3155  hostnameIndex = "0" + hostnameIndex;
3156  } // end fixed width handling
3157 
3158  hostname += hostnameIndex + hostnamePieces[p];
3159  }
3160  __COUTV__(hostname);
3161  }
3162  // else use hostname from above
3163 
3164  if(isFirst) // take current row
3165  {
3166  typeTable.tableView_->setValueAsString(name, row, typeTable.tableView_->getColUID());
3167 
3168  typeTable.tableView_->setValueAsString(hostname, row, hostnameCol);
3169 
3170  // remove from delete map
3171  deleteRecordMap[row] = false;
3172  }
3173  else // copy row
3174  {
3175  unsigned int copyRow = typeTable.tableView_->copyRows(
3176  author, *(typeTable.tableView_), row, 1 /*srcRowsToCopy*/, -1 /*destOffsetRow*/, true /*generateUniqueDataColumns*/);
3177  typeTable.tableView_->setValueAsString(name, copyRow, typeTable.tableView_->getColUID());
3178  typeTable.tableView_->setValueAsString(hostname, copyRow, hostnameCol);
3179 
3180  // customize row if in original value map
3181  if(originalMultinodeValues.find(name) != originalMultinodeValues.end())
3182  {
3183  for(const auto& valuePair : originalMultinodeValues.at(name))
3184  {
3185  __COUT__ << "Customizing node: " << name << "[" << copyRow << "][" << valuePair.first << "] = " << valuePair.second
3186  << __E__;
3187  typeTable.tableView_->setValueAsString(valuePair.second, copyRow, valuePair.first);
3188  }
3189  }
3190 
3191  // remove from delete map
3192  deleteRecordMap[copyRow] = false;
3193  } // end copy and customize row handling
3194 
3195  isFirst = false;
3196  } // end multi-node loop
3197  } // end multi-node handling
3198  } // end node record loop
3199 
3200  { // delete record handling
3201  __COUT__ << "Deleting '" << nodeTypePair.first << "' records not specified..." << __E__;
3202 
3203  //unsigned int row;
3204  std::set<unsigned int> orderedRowSet; // need to delete in reverse order
3205  for(auto& deletePair : deleteRecordMap)
3206  {
3207  __COUTV__(deletePair.first);
3208  if(!deletePair.second)
3209  continue; // only delete if true
3210 
3211  __COUTV__(deletePair.first);
3212  orderedRowSet.emplace(deletePair.first);
3213  }
3214 
3215  // delete elements in reverse order
3216  for(std::set<unsigned int>::reverse_iterator rit = orderedRowSet.rbegin(); rit != orderedRowSet.rend(); rit++)
3217  typeTable.tableView_->deleteRow(*rit);
3218 
3219  } // end delete record handling
3220 
3221  {
3222  std::stringstream ss;
3223  typeTable.tableView_->print(ss);
3224  __COUT__ << ss.str();
3225  }
3226 
3227  typeTable.tableView_->init(); // verify new table (throws runtime_errors)
3228 
3229  } // end node type loop
3230 
3231  {
3232  std::stringstream ss;
3233  artdaqSupervisorTable.tableView_->print(ss);
3234  __COUT__ << ss.str();
3235  }
3236  {
3237  std::stringstream ss;
3238  artdaqSubsystemTable.tableView_->print(ss);
3239  __COUT__ << ss.str();
3240  }
3241 
3242  artdaqSupervisorTable.tableView_->init(); // verify new table (throws runtime_errors)
3243  artdaqSubsystemTable.tableView_->init(); // verify new table (throws runtime_errors)
3244  }
3245  catch(...)
3246  {
3247  __COUT__ << "Table errors while creating ARTDAQ nodes. Erasing all newly "
3248  "created table versions."
3249  << __E__;
3250  throw; // re-throw
3251  } // end catch
3252 
3253  __COUT__ << "Edits complete for artdaq nodes and subsystems.. now save and activate groups, and update aliases!" << __E__;
3254 
3255  TableGroupKey newConfigurationGroupKey;
3256  {
3257  std::string localAccumulatedWarnings;
3258  configGroupEdit.saveChanges(configGroupEdit.originalGroupName_,
3259  newConfigurationGroupKey,
3260  nullptr /*foundEquivalentGroupKey*/,
3261  true /*activateNewGroup*/,
3262  true /*updateGroupAliases*/,
3263  true /*updateTableAliases*/,
3264  nullptr /*newBackboneKey*/,
3265  nullptr /*foundEquivalentBackboneKey*/,
3266  &localAccumulatedWarnings);
3267  }
3268 
3269 } // end setAndActivateARTDAQSystem()
3270 
3271 //==============================================================================
3272 int ARTDAQTableBase::getSubsytemId(ConfigurationTree subsystemNode)
3273 {
3274  // using row forces a unique ID from 0 to rows-1
3275  // note: default no defined subsystem link to id=1; so add 2
3276 
3277  return subsystemNode.getNodeRow() + 2;
3278 } // end getSubsytemId()
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)