otsdaq  3.03.00
ARTDAQEventBuilderTable_table.cc
1 #include "otsdaq/Macros/TablePluginMacros.h"
2 #include "otsdaq/TablePlugins/ARTDAQEventBuilderTable.h"
3 
4 #include <fstream> // for std::ofstream
5 
6 using namespace ots;
7 
8 // clang-format off
9 
10 #define SLOWCONTROL_PV_FILE_PATH \
11  std::string( \
12  getenv("OTSDAQ_EPICS_DATA")? \
13  (std::string(getenv("OTSDAQ_EPICS_DATA")) + "/" + __ENV__("MU2E_OWNER") + "_otsdaq_artdaqEventBuilder-ai.dbg"): \
14  (EPICS_CONFIG_PATH + "/_otsdaq_artdaqEventBuilder-ai.dbg") )
15 
16 // clang-format on
17 
18 //==============================================================================
19 ARTDAQEventBuilderTable::ARTDAQEventBuilderTable(void)
20  : TableBase("ARTDAQEventBuilderTable")
21  , ARTDAQTableBase("ARTDAQEventBuilderTable")
22  , SlowControlsTableBase("ARTDAQEventBuilderTable")
23 {
25  // WARNING: the names used in C++ MUST match the Table INFO //
27  __COUT__ << "ARTDAQEventBuilderTable Constructed." << __E__;
28 } // end constructor()
29 
30 //==============================================================================
31 ARTDAQEventBuilderTable::~ARTDAQEventBuilderTable(void) {}
32 
33 //==============================================================================
35 {
36  lastConfigManager_ = configManager;
37 
38  // use isFirstAppInContext to only run once per context, for example to avoid
39  // generating files on local disk multiple times.
40  isFirstAppInContext_ = configManager->isOwnerFirstAppInContext();
41 
42  //__COUTV__(isFirstAppInContext);
44  return;
45 
46  //if artdaq supervisor is disabled, skip fcl handling
47  if(!ARTDAQTableBase::isARTDAQEnabled(configManager))
48  {
49  __COUT_INFO__ << "ARTDAQ Supervisor is disabled, so skipping fcl handling."
50  << __E__;
51  return;
52  }
53 
54  // make directory just in case
55  mkdir((ARTDAQTableBase::ARTDAQ_FCL_PATH).c_str(), 0755);
56 
57  __COUTS__(3) << "*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*" << __E__;
58  __COUTS__(3) << configManager->__SELF_NODE__ << __E__;
59 
60  // handle fcl file generation, wherever the level of this table
61 
62  auto builders = lastConfigManager_->getNode(ARTDAQTableBase::getTableName())
63  .getChildren(
64  /*default filterMap*/ std::map<std::string /*relative-path*/,
65  std::string /*value*/>(),
66  /*default byPriority*/ false,
67  /*TRUE! onlyStatusTrue*/ true);
68 
69  std::string lastBuilderFcl[2],
70  flattenedLastFclParts
71  [2]; //same handling as otsdaq/otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.cc:1986
72  for(auto& builder : builders)
73  {
74  const std::string& builderUID = builder.first;
75  __COUTV__(builderUID);
76 
77  std::string returnFcl, processName;
78  bool needToFlatten = true;
79  bool captureAsLastFcl =
80  builders.size() && //init to true if multiple builders left to handle
81  (&builder != &builders.back());
82  outputDataReceiverFHICL(builder.second,
83  ARTDAQAppType::EventBuilder,
84  DEFAULT_MAX_FRAGMENT_SIZE,
85  DEFAULT_ROUTING_TIMEOUT_MS,
86  DEFAULT_ROUTING_RETRY_COUNT,
87  captureAsLastFcl ? &returnFcl : nullptr);
88 
89  //Speed-up Philosophy:
90  // flattenFHICL is expensive, so try to identify multinodes with fcl that only differ by process_name,
91  // i.e., ignore starting comments and process name, then compare fcl.
92  // Note: not much gain for any other node types but Event Builders, which tend to only differ by process_name in their fcl
93 
94  auto cmi =
95  returnFcl.find("# otsdaq-ARTDAQ builder UID:"); //find starting comments
96  if(cmi != std::string::npos)
97  cmi = returnFcl.find('\n', cmi);
98  if(cmi != std::string::npos)
99  {
100  size_t pnj = std::string::npos;
101  auto pni = returnFcl.find("\tprocess_name: ", cmi); //find process name
102  if(pni != std::string::npos)
103  {
104  pni += std::string("\tprocess_name: ").size(); //move past field name
105  pnj = returnFcl.find('\n', pni);
106  }
107  if(pnj != std::string::npos)
108  {
109  processName = returnFcl.substr(pni, pnj - pni);
110  __COUT__ << "Found process name = " << processName << __E__;
111 
112  bool sameFirst = false;
113  //check before process name (ignoring comments)
114  std::string newPiece = returnFcl.substr(cmi, pni - cmi);
115  if(flattenedLastFclParts[0].size() && lastBuilderFcl[0].size() &&
116  lastBuilderFcl[0] == newPiece)
117  {
118  __COUT__ << "Same first fcl" << __E__;
119  sameFirst = true;
120  }
121  else if(TTEST(20))
122  {
123  __COUTVS__(20, lastBuilderFcl[0]);
124  __COUTVS__(20, newPiece);
125  for(size_t i = 0, j = 0;
126  i < lastBuilderFcl[0].size() && j < newPiece.size();
127  ++i, ++j)
128  {
129  if(lastBuilderFcl[0][i] != newPiece[j])
130  {
131  __COUTVS__(20, i);
132  __COUTVS__(20, j);
133  __COUTVS__(20, lastBuilderFcl[0].substr(i, 30));
134  __COUTVS__(20, newPiece.substr(j, 30));
135  break;
136  }
137  }
138  }
139  if(captureAsLastFcl) //if more, save piece
140  lastBuilderFcl[0] = newPiece;
141 
142  //check after process name
143  newPiece = returnFcl.substr(pnj);
144  if(lastBuilderFcl[0].size() && lastBuilderFcl[1] == newPiece)
145  {
146  __COUT__ << "Same second fcl" << __E__;
147  if(sameFirst) //found opportunity for shortcut-to-flatten!
148  {
149  std::chrono::steady_clock::time_point startClock =
150  std::chrono::steady_clock::now();
151  __COUT__ << "Found fcl match! Reuse for " << builderUID << __E__;
152  captureAsLastFcl =
153  false; //do not overwrite current last fcl now!
154  needToFlatten = false;
155 
156  //do rapid flatten here
157  std::string outFile =
158  getFlatFHICLFilename(ARTDAQAppType::EventBuilder, builderUID);
159  __COUTVS__(3, outFile);
160  std::ofstream ofs{outFile};
161  if(!ofs)
162  {
163  __SS__ << "Failed to open fhicl output file '" << outFile
164  << "!'" << __E__;
165  __SS_THROW__;
166  }
167  ofs << flattenedLastFclParts[0] << "process_name: \""
168  << processName << "\"" << flattenedLastFclParts[1];
169  __COUTT__ << builderUID << " Flatten Clock time = "
170  << artdaq::TimeUtils::GetElapsedTime(startClock)
171  << __E__;
172  continue; //done with shortcut-to-flatten
173  } //end shortcut-to-flatten handling
174  }
175  if(captureAsLastFcl) //if interesting for more, save piece
176  lastBuilderFcl[1] = newPiece;
177  }
178  }
179 
180  if(needToFlatten)
181  ARTDAQTableBase::flattenFHICL(ARTDAQAppType::EventBuilder,
182  builderUID,
183  captureAsLastFcl ? &returnFcl : nullptr);
184  else
185  __COUT__ << "Skipping full flatten for " << builderUID << __E__;
186 
187  //save parts without process name
188  __COUTV__(captureAsLastFcl);
189  if(captureAsLastFcl)
190  {
191  size_t pnj = std::string::npos;
192  auto pni = returnFcl.find("process_name:"); //find process name
193  if(pni != std::string::npos)
194  {
195  //enforce white space before process name
196  if(pni && (returnFcl[pni - 1] == ' ' || returnFcl[pni - 1] == '\n' ||
197  returnFcl[pni - 1] == '\t'))
198  pnj = returnFcl.find('\n', pni);
199  }
200  if(pnj != std::string::npos)
201  {
202  __COUT__
203  << "Found flattened '" //Note: returnFcl.substr(pni, pnj - pni) includes "process_name:"
204  << returnFcl.substr(pni, pnj - pni) << "' at pos " << pni << " of "
205  << returnFcl.size() << __E__;
206  flattenedLastFclParts[0] = returnFcl.substr(0, pni);
207  flattenedLastFclParts[1] = returnFcl.substr(pnj);
208  }
209  else
210  {
211  __COUT_WARN__ << "Failed to capture fcl for " << processName << "!"
212  << __E__;
213  }
214  }
215  } //end builder fcl handling loop
216  __COUTS__(3) << "*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*" << __E__;
217 } // end init()
218 
219 //==============================================================================
220 unsigned int ARTDAQEventBuilderTable::slowControlsHandlerConfig(
221  std::stringstream& out,
222  ConfigurationManager* configManager,
223  std::vector<std::pair<std::string /*channelName*/, std::vector<std::string>>>*
224  channelList /*= 0*/
225 ) const
226 {
228  // generate xdaq run parameter file
229 
230  std::string tabStr = "";
231  std::string commentStr = "";
232 
233  // loop through ARTDAQ EventBuilder records starting at ARTDAQSupervisorTable
234  std::vector<std::pair<std::string, ConfigurationTree>> artdaqRecords =
235  configManager->getNode("ARTDAQSupervisorTable").getChildren();
236 
237  unsigned int numberOfEventBuiderMetricParameters = 0;
238 
239  for(auto& artdaqPair : artdaqRecords) // start main artdaq record loop
240  {
241  if(artdaqPair.second.getNode(colARTDAQSupervisor_.colLinkToEventBuilders_)
242  .isDisconnected())
243  continue;
244 
245  std::vector<std::pair<std::string, ConfigurationTree>> eventBuilderRecords =
246  artdaqPair.second.getNode(colARTDAQSupervisor_.colLinkToEventBuilders_)
247  .getChildren();
248 
249  for(auto& eventBuilderPair :
250  eventBuilderRecords) // start main eventBuilder record loop
251  {
252  if(!eventBuilderPair.second.status())
253  continue;
254 
255  try
256  {
257  if(eventBuilderPair.second.getNode("daqLink").isDisconnected())
258  continue;
259 
260  auto daqLink = eventBuilderPair.second.getNode("daqLink");
261 
262  if(daqLink.getNode("daqMetricsLink").isDisconnected())
263  continue;
264 
265  auto daqMetricsLinks = daqLink.getNode("daqMetricsLink").getChildren();
266  for(auto& daqMetricsLink :
267  daqMetricsLinks) // start daqMetricsLinks record loop
268  {
269  if(!daqMetricsLink.second.status())
270  continue;
271 
272  if(daqMetricsLink.second.getNode("metricParametersLink")
273  .isDisconnected())
274  continue;
275 
276  // ConfigurationTree slowControlsLink = configManager->getNode("ARTDAQMetricAlarmThresholdsTable");
277  ConfigurationTree slowControlsLink =
278  eventBuilderPair.second.getNode("MetricAlarmThresholdsLink");
279 
280  auto metricParametersLinks =
281  daqMetricsLink.second.getNode("metricParametersLink")
282  .getChildren();
283  for(auto& metricParametersLink :
284  metricParametersLinks) // start daq MetricParametersLinks record loop
285  {
286  if(!metricParametersLink.second.status())
287  continue;
288 
289  std::string subsystem =
290  metricParametersLink.second.getNode("metricParameterValue")
291  .getValueWithDefault<std::string>(std::string("TDAQ_") +
292  __ENV__("MU2E_OWNER"));
293  if(subsystem.find("Mu2e:") != std::string::npos)
294  subsystem = subsystem.replace(subsystem.find("Mu2e:"), 5, "");
295  while(subsystem.find("\"") != std::string::npos)
296  subsystem = subsystem.replace(subsystem.find("\""), 1, "");
297 
298  numberOfEventBuiderMetricParameters =
299  slowControlsHandler(out,
300  tabStr,
301  commentStr,
302  subsystem,
303  eventBuilderPair.first,
304  slowControlsLink,
305  channelList);
306 
307  __COUT__ << "EventBuilder '" << eventBuilderPair.first
308  << "' number of metrics for slow controls: "
309  << numberOfEventBuiderMetricParameters << __E__;
310  }
311  }
312  }
313  catch(const std::runtime_error& e)
314  {
315  __COUT_ERR__ << "Ignoring EventBuilder error: " << e.what() << __E__;
316  }
317  }
318  }
319 
320  return numberOfEventBuiderMetricParameters;
321 } // end slowControlsHandlerConfig()
322 
323 //==============================================================================
326 {
327  return SLOWCONTROL_PV_FILE_PATH;
328 }
329 
330 DEFINE_OTS_TABLE(ARTDAQEventBuilderTable)
virtual std::string setFilePath() const override
return out file path
void init(ConfigurationManager *configManager) override
Methods.
<virtual so future plugins can inherit from multiple table base classes
static void outputDataReceiverFHICL(const ConfigurationTree &receiverNode, ARTDAQAppType appType, size_t maxFragmentSizeBytes=DEFAULT_MAX_FRAGMENT_SIZE, size_t routingTimeoutMs=DEFAULT_ROUTING_TIMEOUT_MS, size_t routingRetryCount=DEFAULT_ROUTING_RETRY_COUNT, std::string *returnFcl=nullptr)
static bool isARTDAQEnabled(const ConfigurationManager *cfgMgr)
isARTDAQEnabled
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
"root/parent/parent/"
ConfigurationTree getNode(const std::string &nodeName, bool doNotThrowOnBrokenUIDLinks=false) const
navigating between nodes
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
<virtual so future plugins can inherit from multiple table base classes
bool isFirstAppInContext_
for managing if PV list has changed
const std::string & getTableName(void) const
Getters.
Definition: TableBase.cc:814