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