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