artdaq  v3_06_02
RootDAQOut_module.cc
1 #include "art/Framework/Core/ModuleMacros.h"
2 #include "art/Framework/Core/OutputModule.h"
3 #include "art/Framework/Core/RPManager.h"
4 #include "art/Framework/Core/ResultsProducer.h"
5 #include "art/Framework/IO/ClosingCriteria.h"
6 #include "art/Framework/IO/FileStatsCollector.h"
7 #include "art/Framework/IO/PostCloseFileRenamer.h"
8 #include "art/Framework/IO/Root/DropMetaData.h"
9 #include "art/Framework/IO/Root/RootFileBlock.h"
10 #include "art/Framework/IO/Root/detail/rootOutputConfigurationTools.h"
11 #include "art/Framework/IO/detail/logFileAction.h"
12 #include "art/Framework/IO/detail/validateFileNamePattern.h"
13 #include "art/Framework/Principal/Event.h"
14 #include "art/Framework/Principal/EventPrincipal.h"
15 #include "art/Framework/Principal/Results.h"
16 #include "art/Framework/Principal/ResultsPrincipal.h"
17 #include "art/Framework/Principal/Run.h"
18 #include "art/Framework/Principal/RunPrincipal.h"
19 #include "art/Framework/Principal/SubRun.h"
20 #include "art/Framework/Principal/SubRunPrincipal.h"
21 #include "art/Persistency/Provenance/ProductMetaData.h"
22 #include "art/Utilities/parent_path.h"
23 #include "art/Utilities/unique_filename.h"
24 #include "artdaq/ArtModules/RootDAQOutput-s85/RootDAQOutFile.h"
25 #include "artdaq/DAQdata/Globals.hh"
26 #include "canvas/Persistency/Provenance/FileFormatVersion.h"
27 #include "canvas/Persistency/Provenance/ProductTables.h"
28 #include "canvas/Utilities/Exception.h"
29 #include "fhiclcpp/ParameterSet.h"
30 #include "fhiclcpp/types/Atom.h"
31 #include "fhiclcpp/types/ConfigurationTable.h"
32 #include "fhiclcpp/types/OptionalAtom.h"
33 #include "fhiclcpp/types/OptionalSequence.h"
34 #include "fhiclcpp/types/Table.h"
35 #include "messagefacility/MessageLogger/MessageLogger.h"
36 #include "tracemf.h" // TLOG
37 #define TRACE_NAME (app_name + "_RootDAQOut").c_str()
38 
39 #include <iomanip>
40 #include <memory>
41 #include <sstream>
42 #include <string>
43 #include <utility>
44 
45 using std::string;
46 
47 namespace {
48 std::string const dev_null{"/dev/null"};
49 }
50 
51 namespace art {
52 class RootDAQOut;
53 class RootDAQOutFile;
54 } // namespace art
55 
56 class art::RootDAQOut final : public OutputModule
57 {
58 public:
59  static constexpr char const* default_tmpDir{"<parent-path-of-filename>"};
60 
61  struct Config
62  {
63  using Name = fhicl::Name;
64  using Comment = fhicl::Comment;
65  template<typename T>
66  using Atom = fhicl::Atom<T>;
67  template<typename T>
68  using OptionalAtom = fhicl::OptionalAtom<T>;
69 
70  fhicl::TableFragment<art::OutputModule::Config> omConfig;
71  Atom<std::string> catalog{Name("catalog"), ""};
72  OptionalAtom<bool> dropAllEvents{Name("dropAllEvents")};
73  Atom<bool> dropAllSubRuns{Name("dropAllSubRuns"), false};
74  OptionalAtom<bool> fastCloning{Name("fastCloning")};
75  Atom<std::string> tmpDir{Name("tmpDir"), default_tmpDir};
76  Atom<int> compressionLevel{Name("compressionLevel"), 7};
77  Atom<unsigned> freePercent{Name("freePercent"), 0};
78  Atom<unsigned> freeMB{Name("freeMB"), 0};
79  Atom<int64_t> saveMemoryObjectThreshold{Name("saveMemoryObjectThreshold"),
80  -1l};
81  Atom<int64_t> treeMaxVirtualSize{Name("treeMaxVirtualSize"), -1};
82  Atom<int> splitLevel{Name("splitLevel"), 99};
83  Atom<int> basketSize{Name("basketSize"), 16384};
84  Atom<bool> dropMetaDataForDroppedData{Name("dropMetaDataForDroppedData"),
85  false};
86  Atom<std::string> dropMetaData{Name("dropMetaData"), "NONE"};
87  Atom<bool> writeParameterSets{Name("writeParameterSets"), true};
88  fhicl::Table<ClosingCriteria::Config> fileProperties{Name("fileProperties")};
89  Atom<int> firstLoggerRank{Name("firstLoggerRank"), -1};
90 
92  {
93  fhicl::Atom<string> appName{fhicl::Name("appName")};
94  fhicl::Atom<string> newString{fhicl::Name("newString")};
95  };
97  {
98  fhicl::Atom<string> targetString{fhicl::Name("targetString")};
99  fhicl::Sequence<fhicl::Table<NewSubStringForApp>> replacementList{fhicl::Name("replacementList")};
100  };
101  fhicl::OptionalSequence<fhicl::Table<FileNameSubstitution>> fileNameSubstitutions{Name("fileNameSubstitutions")};
102  Atom<bool> enableLargeFileCatalogMetadata{
103  Name("enableLargeFileCatalogMetadata"),
104  true};
105 
106  Config()
107  {
108  // Both RootDAQOut module and OutputModule use the "fileName"
109  // FHiCL parameter. However, whereas in OutputModule the
110  // parameter has a default, for RootDAQOut the parameter should
111  // not. We therefore have to change the default flag setting
112  // for 'OutputModule::Config::fileName'.
113  using namespace fhicl::detail;
114  ParameterBase* adjustFilename{
115  const_cast<fhicl::Atom<std::string>*>(&omConfig().fileName)};
116  adjustFilename->set_par_style(fhicl::par_style::REQUIRED);
117  }
118 
120  {
121  std::set<std::string>
122  operator()() const
123  {
124  std::set<std::string> keys{
125  art::OutputModule::Config::KeysToIgnore::get()};
126  keys.insert("results");
127  return keys;
128  }
129  };
130  };
131 
132  using Parameters = fhicl::WrappedTable<Config, Config::KeysToIgnore>;
133 
134  explicit RootDAQOut(Parameters const&);
135 
136  void postSelectProducts() override;
137 
138  void beginJob() override;
139  void endJob() override;
140 
141  void event(EventPrincipal const&) override;
142 
143  void beginSubRun(SubRunPrincipal const&) override;
144  void endSubRun(SubRunPrincipal const&) override;
145 
146  void beginRun(RunPrincipal const&) override;
147  void endRun(RunPrincipal const&) override;
148 
149 private:
150  std::string fileNameAtOpen() const;
151  std::string fileNameAtClose(std::string const& currentFileName);
152  std::string const& lastClosedFileName() const override;
153  void openFile(FileBlock const&) override;
154  void respondToOpenInputFile(FileBlock const&) override;
155  void readResults(ResultsPrincipal const& resp) override;
156  void respondToCloseInputFile(FileBlock const&) override;
157  void incrementInputFileNumber() override;
158  Granularity fileGranularity() const override;
159  void write(EventPrincipal&) override;
160  void writeSubRun(SubRunPrincipal&) override;
161  void writeRun(RunPrincipal&) override;
162  void setSubRunAuxiliaryRangeSetID(RangeSet const&) override;
163  void setRunAuxiliaryRangeSetID(RangeSet const&) override;
164  bool isFileOpen() const override;
165  void setFileStatus(OutputFileStatus) override;
166  bool requestsToCloseFile() const override;
167  void doOpenFile();
168  void startEndFile() override;
169  void writeFileFormatVersion() override;
170  void writeFileIndex() override;
171  void writeEventHistory() override;
172  void writeProcessConfigurationRegistry() override;
173  void writeProcessHistoryRegistry() override;
174  void writeParameterSetRegistry() override;
175  void writeProductDescriptionRegistry() override;
176  void writeParentageRegistry() override;
177  void doWriteFileCatalogMetadata(
178  FileCatalogMetadata::collection_type const& md,
179  FileCatalogMetadata::collection_type const& ssmd) override;
180  void writeProductDependencies() override;
181  void finishEndFile() override;
182  void doRegisterProducts(MasterProductRegistry& mpr,
183  ProductDescriptions& producedProducts,
184  ModuleDescription const& md) override;
185  std::string modifyFilePattern(std::string const&, Config const&);
186 
187 private:
188  std::string const catalog_;
189  bool dropAllEvents_{false};
190  bool dropAllSubRuns_;
191  std::string const moduleLabel_;
192  bool enableLargeFileCatalogMetadata_{true};
193  int inputFileCount_{0};
194  std::unique_ptr<RootDAQOutFile> rootOutputFile_{nullptr};
195  FileStatsCollector fstats_;
196  PostCloseFileRenamer fRenamer_{fstats_};
197  std::string const filePattern_;
198  std::string tmpDir_;
199  std::string lastClosedFileName_{};
200 
201  // We keep this set of data members for the use of RootDAQOutFile.
202  int const compressionLevel_;
203  unsigned freePercent_;
204  unsigned freeMB_;
205  int64_t const saveMemoryObjectThreshold_;
206  int64_t const treeMaxVirtualSize_;
207  int const splitLevel_;
208  int const basketSize_;
209  DropMetaData dropMetaData_;
210  bool dropMetaDataForDroppedData_;
211 
212  // We keep this for the use of RootDAQOutFile and we also use it
213  // during file open to make some choices.
214  bool fastCloningEnabled_{true};
215 
216  // Set false only for cases where we are guaranteed never to need
217  // historical ParameterSet information in the downstream file
218  // (e.g. mixing).
219  bool writeParameterSets_;
220  ClosingCriteria fileProperties_;
221 
222  // ResultsProducer management.
223  ProductTable producedResultsProducts_{};
224  RPManager rpm_;
225 };
226 
227 art::RootDAQOut::RootDAQOut(Parameters const& config)
228  : OutputModule{config().omConfig, config.get_PSet()}
229  , catalog_{config().catalog()}
230  , dropAllSubRuns_{config().dropAllSubRuns()}
231  , moduleLabel_{config.get_PSet().get<string>("module_label")}
232  , enableLargeFileCatalogMetadata_{config().enableLargeFileCatalogMetadata()}
233  , fstats_{moduleLabel_, processName(), enableLargeFileCatalogMetadata_}
234  , filePattern_{modifyFilePattern(config().omConfig().fileName(), config())}
235  , tmpDir_{config().tmpDir() == default_tmpDir ? parent_path(filePattern_) : config().tmpDir()}
236  , compressionLevel_{config().compressionLevel()}
237  , freePercent_{config().freePercent()}
238  , freeMB_{config().freeMB()}
239  , saveMemoryObjectThreshold_{config().saveMemoryObjectThreshold()}
240  , treeMaxVirtualSize_{config().treeMaxVirtualSize()}
241  , splitLevel_{config().splitLevel()}
242  , basketSize_{config().basketSize()}
243  , dropMetaData_{config().dropMetaData()}
244  , dropMetaDataForDroppedData_{config().dropMetaDataForDroppedData()}
245  , writeParameterSets_{config().writeParameterSets()}
246  , fileProperties_{(
247  detail::validateFileNamePattern(
248  config.get_PSet().has_key(config().fileProperties.name()),
249  filePattern_), // comma operator!
250  config().fileProperties())}
251  , rpm_{config.get_PSet()}
252 {
253  bool const dropAllEventsSet{config().dropAllEvents(dropAllEvents_)};
254  dropAllEvents_ =
255  detail::shouldDropEvents(dropAllEventsSet, dropAllEvents_, dropAllSubRuns_);
256 
257  // N.B. Any time file switching is enabled at a boundary other than
258  // InputFile, fastCloningEnabled_ ***MUST*** be deactivated. This is
259  // to ensure that the Event tree from the InputFile is not
260  // accidentally cloned to the output file before the output
261  // module has seen the events that are going to be processed.
262  bool const fastCloningSet{config().fastCloning(fastCloningEnabled_)};
263  fastCloningEnabled_ = detail::shouldFastClone(
264  fastCloningSet, fastCloningEnabled_, wantAllEvents(), fileProperties_);
265  if (!writeParameterSets_)
266  {
267  mf::LogWarning("PROVENANCE")
268  << "Output module " << moduleLabel_
269  << " has parameter writeParameterSets set to false.\n"
270  << "Parameter set provenance will not be available in subsequent jobs.\n"
271  << "Check your experiment's policy on this issue to avoid future "
272  "problems\n"
273  << "with analysis reproducibility.\n";
274  }
275 }
276 
277 void art::RootDAQOut::openFile(FileBlock const& fb)
278 {
279  // Note: The file block here refers to the currently open input
280  // file, so we can find out about the available products by
281  // looping over the branches of the input file data trees.
282  if (!isFileOpen())
283  {
284  doOpenFile();
285  respondToOpenInputFile(fb);
286  }
287 }
288 
289 void art::RootDAQOut::postSelectProducts()
290 {
291  if (isFileOpen())
292  {
293  rootOutputFile_->selectProducts();
294  }
295 }
296 
297 void art::RootDAQOut::respondToOpenInputFile(FileBlock const& fb)
298 {
299  ++inputFileCount_;
300  if (!isFileOpen())
301  return;
302 
303  auto const* rfb = dynamic_cast<RootFileBlock const*>(&fb);
304 
305  bool fastCloneThisOne = fastCloningEnabled_ && rfb &&
306  (rfb->tree() != nullptr) &&
307  ((remainingEvents() < 0) ||
308  (remainingEvents() >= rfb->tree()->GetEntries()));
309  if (fastCloningEnabled_ && !fastCloneThisOne)
310  {
311  mf::LogWarning("FastCloning")
312  << "Fast cloning deactivated for this input file due to "
313  << "empty event tree and/or event limits.";
314  }
315  if (fastCloneThisOne && !rfb->fastClonable())
316  {
317  mf::LogWarning("FastCloning")
318  << "Fast cloning deactivated for this input file due to "
319  << "information in FileBlock.";
320  fastCloneThisOne = false;
321  }
322  rootOutputFile_->beginInputFile(rfb, fastCloneThisOne);
323  fstats_.recordInputFile(fb.fileName());
324 }
325 
326 void art::RootDAQOut::readResults(ResultsPrincipal const& resp)
327 {
328  rpm_.for_each_RPWorker([&resp](RPWorker& w) { w.rp().doReadResults(resp); });
329 }
330 
331 void art::RootDAQOut::respondToCloseInputFile(FileBlock const& fb)
332 {
333  if (isFileOpen())
334  {
335  rootOutputFile_->respondToCloseInputFile(fb);
336  }
337 }
338 
339 void art::RootDAQOut::write(EventPrincipal& ep)
340 {
341  TLOG(10) << __func__ << ": enter; dropAllEvents_=" << dropAllEvents_;
342  if (dropAllEvents_)
343  {
344  return;
345  }
346  if (hasNewlyDroppedBranch()[InEvent])
347  {
348  ep.addToProcessHistory();
349  }
350  rootOutputFile_->writeOne(ep);
351  fstats_.recordEvent(ep.id());
352  TLOG(9) << __func__ << ": return";
353 }
354 
355 void art::RootDAQOut::setSubRunAuxiliaryRangeSetID(RangeSet const& rs)
356 {
357  rootOutputFile_->setSubRunAuxiliaryRangeSetID(rs);
358 }
359 
360 void art::RootDAQOut::writeSubRun(SubRunPrincipal& sr)
361 {
362  if (dropAllSubRuns_)
363  {
364  return;
365  }
366  if (hasNewlyDroppedBranch()[InSubRun])
367  {
368  sr.addToProcessHistory();
369  }
370  rootOutputFile_->writeSubRun(sr);
371  fstats_.recordSubRun(sr.id());
372 }
373 
374 void art::RootDAQOut::setRunAuxiliaryRangeSetID(RangeSet const& rs)
375 {
376  rootOutputFile_->setRunAuxiliaryRangeSetID(rs);
377 }
378 
379 void art::RootDAQOut::writeRun(RunPrincipal& r)
380 {
381  if (hasNewlyDroppedBranch()[InRun])
382  {
383  r.addToProcessHistory();
384  }
385  rootOutputFile_->writeRun(r);
386  fstats_.recordRun(r.id());
387 }
388 
389 void art::RootDAQOut::startEndFile()
390 {
391  auto resp = std::make_unique<ResultsPrincipal>(
392  ResultsAuxiliary{}, description().processConfiguration(), nullptr);
393  resp->setProducedProducts(producedResultsProducts_);
394  if (ProductMetaData::instance().productProduced(InResults) ||
395  hasNewlyDroppedBranch()[InResults])
396  {
397  resp->addToProcessHistory();
398  }
399  rpm_.for_each_RPWorker(
400  [&resp](RPWorker& w) { w.rp().doWriteResults(*resp); });
401  rootOutputFile_->writeResults(*resp);
402 }
403 
404 void art::RootDAQOut::writeFileFormatVersion()
405 {
406  rootOutputFile_->writeFileFormatVersion();
407 }
408 
409 void art::RootDAQOut::writeFileIndex()
410 {
411  rootOutputFile_->writeFileIndex();
412 }
413 
414 void art::RootDAQOut::writeEventHistory()
415 {
416  rootOutputFile_->writeEventHistory();
417 }
418 
419 void art::RootDAQOut::writeProcessConfigurationRegistry()
420 {
421  rootOutputFile_->writeProcessConfigurationRegistry();
422 }
423 
424 void art::RootDAQOut::writeProcessHistoryRegistry()
425 {
426  rootOutputFile_->writeProcessHistoryRegistry();
427 }
428 
429 void art::RootDAQOut::writeParameterSetRegistry()
430 {
431  if (writeParameterSets_)
432  {
433  rootOutputFile_->writeParameterSetRegistry();
434  }
435 }
436 
437 void art::RootDAQOut::writeProductDescriptionRegistry()
438 {
439  rootOutputFile_->writeProductDescriptionRegistry();
440 }
441 
442 void art::RootDAQOut::writeParentageRegistry()
443 {
444  rootOutputFile_->writeParentageRegistry();
445 }
446 
447 void art::RootDAQOut::doWriteFileCatalogMetadata(
448  FileCatalogMetadata::collection_type const& md,
449  FileCatalogMetadata::collection_type const& ssmd)
450 {
451  rootOutputFile_->writeFileCatalogMetadata(fstats_, md, ssmd);
452 }
453 
454 void art::RootDAQOut::writeProductDependencies()
455 {
456  rootOutputFile_->writeProductDependencies();
457 }
458 
459 void art::RootDAQOut::finishEndFile()
460 {
461  std::string const currentFileName{rootOutputFile_->currentFileName()};
462  rootOutputFile_->writeTTrees();
463  rootOutputFile_.reset();
464  fstats_.recordFileClose();
465  lastClosedFileName_ =
466  fRenamer_.maybeRenameFile(currentFileName, filePattern_);
467  TLOG(TLVL_INFO) << __func__ << ": Closed output file \"" << lastClosedFileName_ << "\"";
468  rpm_.invoke(&ResultsProducer::doClear);
469 }
470 
471 void art::RootDAQOut::doRegisterProducts(MasterProductRegistry& mpr,
472  ProductDescriptions& producedProducts,
473  ModuleDescription const& md)
474 {
475  // Register Results products from ResultsProducers.
476  rpm_.for_each_RPWorker([&mpr, &producedProducts, &md](RPWorker& w) {
477  auto const& params = w.params();
478  w.setModuleDescription(
479  ModuleDescription{params.rpPSetID,
480  params.rpPluginType,
481  md.moduleLabel() + '#' + params.rpLabel,
482  md.processConfiguration(),
483  md.parentageEnabled(),
484  md.rangesEnabled(),
485  md.dbEnabled(),
486  ModuleDescription::invalidID()});
487  w.rp().registerProducts(mpr, producedProducts, w.moduleDescription());
488  });
489 
490  // Form product table for Results products. We do this here so we
491  // can appropriately set the product tables for the
492  // ResultsPrincipal.
493  producedResultsProducts_ = ProductTable{producedProducts, InResults};
494 }
495 
496 void art::RootDAQOut::setFileStatus(OutputFileStatus const ofs)
497 {
498  if (isFileOpen())
499  rootOutputFile_->setFileStatus(ofs);
500 }
501 
502 bool art::RootDAQOut::isFileOpen() const
503 {
504  return rootOutputFile_.get() != nullptr;
505 }
506 
507 void art::RootDAQOut::incrementInputFileNumber()
508 {
509  if (isFileOpen())
510  rootOutputFile_->incrementInputFileNumber();
511 }
512 
513 bool art::RootDAQOut::requestsToCloseFile() const
514 {
515  return isFileOpen() ? rootOutputFile_->requestsToCloseFile() : false;
516 }
517 
518 art::Granularity
519 art::RootDAQOut::fileGranularity() const
520 {
521  return fileProperties_.granularity();
522 }
523 
524 void art::RootDAQOut::doOpenFile()
525 {
526  if (inputFileCount_ == 0)
527  {
528  throw art::Exception(art::errors::LogicError)
529  << "Attempt to open output file before input file. "
530  << "Please report this to the core framework developers.\n";
531  }
532  rootOutputFile_ =
533  std::make_unique<RootDAQOutFile>(this,
534  fileNameAtOpen(),
535  fileProperties_,
536  compressionLevel_,
537  freePercent_,
538  freeMB_,
539  saveMemoryObjectThreshold_,
540  treeMaxVirtualSize_,
541  splitLevel_,
542  basketSize_,
543  dropMetaData_,
544  dropMetaDataForDroppedData_,
545  fastCloningEnabled_,
546  description().parentageEnabled(),
547  description().rangesEnabled(),
548  description().dbEnabled());
549  fstats_.recordFileOpen();
550  TLOG(TLVL_INFO) << __func__ << ": Opened output file with pattern \"" << filePattern_ << "\"";
551 }
552 
553 string
554 art::RootDAQOut::fileNameAtOpen() const
555 {
556  return filePattern_ == dev_null ? dev_null : unique_filename(tmpDir_ + "/RootOutput");
557 }
558 
559 string
560 art::RootDAQOut::fileNameAtClose(std::string const& currentFileName)
561 {
562  return filePattern_ == dev_null ? dev_null : fRenamer_.maybeRenameFile(currentFileName, filePattern_);
563 }
564 
565 string const&
566 art::RootDAQOut::lastClosedFileName() const
567 {
568  if (lastClosedFileName_.empty())
569  {
570  throw Exception(errors::LogicError, "RootDAQOut::currentFileName(): ")
571  << "called before meaningful.\n";
572  }
573  return lastClosedFileName_;
574 }
575 
576 void art::RootDAQOut::beginJob()
577 {
578  rpm_.invoke(&ResultsProducer::doBeginJob);
579 }
580 
581 void art::RootDAQOut::endJob()
582 {
583  rpm_.invoke(&ResultsProducer::doEndJob);
584  showMissingConsumes();
585 }
586 
587 void art::RootDAQOut::event(EventPrincipal const& ep)
588 {
589  rpm_.for_each_RPWorker([&ep](RPWorker& w) { w.rp().doEvent(ep); });
590 }
591 
592 void art::RootDAQOut::beginSubRun(art::SubRunPrincipal const& srp)
593 {
594  rpm_.for_each_RPWorker([&srp](RPWorker& w) {
595  SubRun const sr{srp, w.moduleDescription(), Consumer::non_module_context()};
596  w.rp().doBeginSubRun(srp);
597  });
598 }
599 
600 void art::RootDAQOut::endSubRun(art::SubRunPrincipal const& srp)
601 {
602  rpm_.for_each_RPWorker([&srp](RPWorker& w) { w.rp().doEndSubRun(srp); });
603 }
604 
605 void art::RootDAQOut::beginRun(art::RunPrincipal const& rp)
606 {
607  rpm_.for_each_RPWorker([&rp](RPWorker& w) { w.rp().doBeginRun(rp); });
608 }
609 
610 void art::RootDAQOut::endRun(art::RunPrincipal const& rp)
611 {
612  rpm_.for_each_RPWorker([&rp](RPWorker& w) { w.rp().doEndRun(rp); });
613 }
614 
615 std::string
616 art::RootDAQOut::modifyFilePattern(std::string const& inputPattern, Config const& config)
617 {
618  TLOG(TLVL_DEBUG) << __func__ << ": inputPattern=\"" << inputPattern << "\"";
619 
620  // fetch the firstLoggerRank and fileNameSubstitutions (if provided) for use in
621  // substituting keywords in the filename pattern
622  int firstLoggerRank = config.firstLoggerRank();
623  std::vector<Config::FileNameSubstitution> subs;
624  config.fileNameSubstitutions(subs);
625  TLOG(TLVL_TRACE) << __func__ << ": firstLoggerRank=" << firstLoggerRank
626  << ", numberOfSubstitutionsProvided=" << subs.size();
627 
628  // initialization
629  std::string modifiedPattern = inputPattern;
630  std::string searchString;
631  size_t targetLocation;
632  int zeroBasedRelativeRank = my_rank;
633  int oneBasedRelativeRank = my_rank + 1;
634  if (firstLoggerRank >= 0)
635  {
636  zeroBasedRelativeRank -= firstLoggerRank;
637  oneBasedRelativeRank -= firstLoggerRank;
638  }
639  TLOG(TLVL_TRACE) << __func__ << ": my_rank=" << my_rank << ", zeroBasedRelativeRank=" << zeroBasedRelativeRank
640  << ", oneBasedRelativeRank=" << oneBasedRelativeRank;
641 
642  // if the "ZeroBasedRelativeRank" keyword was specified in the filename pattern,
643  // perform the substitution
644  searchString = "${ZeroBasedRelativeRank}";
645  targetLocation = modifiedPattern.find(searchString);
646  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
647  while (targetLocation != std::string::npos)
648  {
649  std::ostringstream oss;
650  oss << zeroBasedRelativeRank;
651  modifiedPattern.replace(targetLocation, searchString.length(), oss.str());
652  targetLocation = modifiedPattern.find(searchString);
653  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
654  }
655 
656  // if the "OneBasedRelativeRank" keyword was specified in the filename pattern,
657  // perform the substitution
658  searchString = "${OneBasedRelativeRank}";
659  targetLocation = modifiedPattern.find(searchString);
660  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
661  while (targetLocation != std::string::npos)
662  {
663  std::ostringstream oss;
664  oss << oneBasedRelativeRank;
665  modifiedPattern.replace(targetLocation, searchString.length(), oss.str());
666  targetLocation = modifiedPattern.find(searchString);
667  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
668  }
669 
670  // if the "Rank" keyword was specified in the filename pattern,
671  // perform the substitution
672  searchString = "${Rank}";
673  targetLocation = modifiedPattern.find(searchString);
674  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
675  while (targetLocation != std::string::npos)
676  {
677  std::ostringstream oss;
678  oss << my_rank;
679  modifiedPattern.replace(targetLocation, searchString.length(), oss.str());
680  targetLocation = modifiedPattern.find(searchString);
681  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
682  }
683 
684  // if one or more free-form substitutions were provided, we'll do them here
685  for (uint32_t subIdx = 0; subIdx < subs.size(); ++subIdx)
686  {
687  // first look up the replacement string for this process's app_name
688  const std::string BLAH = "none_provided";
689  std::string newString = BLAH;
690  std::vector<Config::NewSubStringForApp> replacementList = subs[subIdx].replacementList();
691  for (uint32_t rdx = 0; rdx < replacementList.size(); ++rdx)
692  {
693  if (replacementList[rdx].appName() == artdaq::Globals::app_name_)
694  {
695  newString = replacementList[rdx].newString();
696  break;
697  }
698  }
699  TLOG(TLVL_TRACE) << __func__ << ": app_name=" << artdaq::Globals::app_name_ << ", newString=" << newString;
700  if (newString != BLAH)
701  {
702  // first, add the expected surrounding text, and search for that
703  searchString = "${" + subs[subIdx].targetString() + "}";
704  targetLocation = modifiedPattern.find(searchString);
705  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
706  while (targetLocation != std::string::npos)
707  {
708  modifiedPattern.replace(targetLocation, searchString.length(), newString);
709  targetLocation = modifiedPattern.find(searchString);
710  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
711  }
712 
713  // then, search for the provided string, verbatim, in case the user specified
714  // the enclosing text in the configuration document
715  searchString = subs[subIdx].targetString();
716  targetLocation = modifiedPattern.find(searchString);
717  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
718  while (targetLocation != std::string::npos)
719  {
720  modifiedPattern.replace(targetLocation, searchString.length(), newString);
721  targetLocation = modifiedPattern.find(searchString);
722  TLOG(TLVL_TRACE) << __func__ << ":" << __LINE__ << " searchString=" << searchString << ", targetLocation=" << targetLocation;
723  }
724  }
725  }
726 
727  TLOG(TLVL_DEBUG) << __func__ << ": modifiedPattern = \"" << modifiedPattern << "\"";
728  return modifiedPattern;
729 }
730 
731 DEFINE_ART_MODULE(art::RootDAQOut)
732 
733 // vim: set sw=2:
static std::string app_name_
The name of the current application, to be used in logging and metrics.
Definition: Globals.hh:38
Required configuration for art processes started by artdaq, with artdaq-specific defaults where appli...
Definition: ArtConfig.hh:116