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