From 51870329caaa2b83b3ecb7874fbd5c994644583c Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Mon, 10 Feb 2025 14:44:09 +0100
Subject: [PATCH] online qa: task header and QA-manager in the online binary

---
 algo/CMakeLists.txt    |  1 +
 algo/global/Reco.cxx   | 19 +++++++++++--
 algo/global/Reco.h     |  4 +++
 algo/qa/QaData.cxx     | 28 +++++++++++++-------
 algo/qa/QaData.h       | 14 ++++++----
 algo/qa/QaManager.h    |  4 +--
 algo/qa/QaTaskHeader.h | 60 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 111 insertions(+), 19 deletions(-)
 create mode 100644 algo/qa/QaTaskHeader.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index 5776eeb2aa..09ee075af5 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -150,6 +150,7 @@ set(SRCS
   qa/PadConfig.cxx
   qa/QaData.cxx
   qa/RecoGeneralQa.cxx
+  qa/QaManager.cxx
   qa/unpack/StsDigiQa.cxx
   ca/TrackingSetup.cxx
   ca/TrackingChain.cxx
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index 6ca23944bd..28acffb8b3 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -21,6 +21,7 @@
 #include "compat/OpenMP.h"
 #include "evbuild/Config.h"
 #include "much/Unpack.h"
+#include "qa/QaManager.h"
 #include "rich/Unpack.h"
 #include "sts/ChannelMaskSet.h"
 #include "sts/HitfinderChain.h"
@@ -111,8 +112,13 @@ void Reco::Init(const Options& opts)
   ParFiles parFiles(opts.RunId());
   L_(info) << "Using parameter files for setup " << parFiles.setup;
 
-  // General QA
+  // QA instantiation
   if (fSender != nullptr) {
+    // QA manager
+    fQaManager = std::make_unique<qa::Manager>(fSender);
+    fQaManager->SetContext(&fContext);
+
+    // General QA
     fGeneralQa = std::make_unique<qa::RecoGeneralQa>(fRunStartTimeNs, fSender);
   }
 
@@ -222,6 +228,11 @@ void Reco::Init(const Options& opts)
     fTracking->Init();
   }
 
+  // Initialize the QA manager
+  if (fQaManager != nullptr) {
+    fQaManager->Init();
+  }
+
   fInitialized = true;
 
   L_(debug) << "CBM Reco finished initialization";
@@ -379,9 +390,13 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.trdHits = std::move(recoData.trdHits);
     }
 
-    // General QA
+    // QA
     if (fSender != nullptr) {
       (*fGeneralQa)(ts);
+
+      // Send all the histograms, collected through the timeslice
+      fQaManager->SetTimesliceId(ts.index());
+      fQaManager->SendHistograms();
     }
   }
   PrintTimings(procMon.time);
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index a3fa9c2411..8e26d35271 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -89,6 +89,7 @@ namespace cbm::algo
   namespace qa
   {
     class RecoGeneralQa;
+    class Manager;
   }
 }  // namespace cbm::algo
 
@@ -173,6 +174,9 @@ namespace cbm::algo
     // Tracking
     std::unique_ptr<TrackingChain> fTracking;
 
+    // QA
+    std::unique_ptr<qa::Manager> fQaManager;
+
     static double FilterNan(double x) { return std::isnan(x) || std::isinf(x) ? 0. : x; }
 
     void Validate(const Options& opts);
diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx
index 5abee21fe5..520ed4e522 100644
--- a/algo/qa/QaData.cxx
+++ b/algo/qa/QaData.cxx
@@ -18,7 +18,18 @@ using cbm::algo::qa::Data;
 //
 void Data::Init(std::shared_ptr<HistogramSender> histSender)
 try {
-  if (histSender.get()) {
+  size_t nHistograms = 0;
+  nHistograms += fNofH1;
+  nHistograms += fNofH2;
+  nHistograms += fNofP1;
+  nHistograms += fNofP2;
+  fbNotEmpty = static_cast<bool>(nHistograms);
+  if (!fbNotEmpty) {
+    L_(warn) << "no histograms were provided to a qa::Data instance (running in an idle mode)";
+  }
+
+  if (histSender.get() && fbNotEmpty) {
+
     // Check, if the tasks list was initialized properly: at least one task must be initialized
     if (fvTaskProperties.empty()) {
       std::stringstream msg;
@@ -35,12 +46,7 @@ try {
 
     // Forming a histogram config message
     std::vector<std::pair<std::string, std::string>> vHistCfgs;
-    size_t nHistograms = 0;
     // NOTE: Important to keep the order of filling the histograms: 1D -> 2D -> ..
-    nHistograms += fNofH1;
-    nHistograms += fNofH2;
-    nHistograms += fNofP1;
-    nHistograms += fNofP2;
     vHistCfgs.reserve(nHistograms);
 
     for (const auto& task : fvTaskProperties) {
@@ -103,8 +109,10 @@ void Data::RegisterNewTask(std::string_view name)
 //
 void Data::Send(std::shared_ptr<HistogramSender> histoSender)
 {
-  histoSender->PrepareAndSendMsg(fHistograms, zmq::send_flags::none);
-  L_(info) << fsTaskNames << ": Published " << fNofH1 << " 1D- and " << fNofH2 << " 2D-histograms, " << fNofP1
-           << " 1D- and " << fNofP2 << " 2D-profiles";
-  this->Reset();
+  if (histoSender.get() && fbNotEmpty) {
+    histoSender->PrepareAndSendMsg(fHistograms, zmq::send_flags::none);
+    L_(info) << fsTaskNames << ": Published " << fNofH1 << " 1D- and " << fNofH2 << " 2D-histograms, " << fNofP1
+             << " 1D- and " << fNofP2 << " 2D-profiles";
+    this->Reset();
+  }
 }
diff --git a/algo/qa/QaData.h b/algo/qa/QaData.h
index 9c47474401..8c66c445a9 100644
--- a/algo/qa/QaData.h
+++ b/algo/qa/QaData.h
@@ -58,7 +58,10 @@ namespace cbm::algo::qa
     /// \param histoSender  A pointer to the histogram sender
     void Init(std::shared_ptr<HistogramSender> histoSender);
 
-    /// \brief Creates a QA-object and returns the pointer to it
+    /// \brief  Creates a QA-object and returns the pointer to it
+    /// \tparam Obj      A type of the histogram (H1D, H2D, Prof1D, Prof2D)
+    /// \tparam Args...  A signature of the histogram constructor
+    /// \param  args     Parameters, passed to a histogram constructor
     template<class Obj, typename... Args>
     Obj* MakeObj(Args... args);
 
@@ -84,10 +87,11 @@ namespace cbm::algo::qa
     std::vector<qa::TaskProperties> fvTaskProperties;  ///< A vector to store properties for multiple QA-tasks
     std::vector<std::string> fvsCanvCfgs = {};         ///< Vector of canvas configs
 
-    uint32_t fNofH1{0};  ///< Number of 1D-histograms
-    uint32_t fNofH2{0};  ///< Number of 2D-histograms
-    uint32_t fNofP1{0};  ///< Number of 1D-profiles
-    uint32_t fNofP2{0};  ///< Number of 2D-profiles
+    uint32_t fNofH1{0};     ///< Number of 1D-histograms
+    uint32_t fNofH2{0};     ///< Number of 2D-histograms
+    uint32_t fNofP1{0};     ///< Number of 1D-profiles
+    uint32_t fNofP2{0};     ///< Number of 2D-profiles
+    bool fbNotEmpty{true};  ///< false: if no histograms were provided, do not perform initialization and sending
   };
 
   // -------------------------------------------------------------------------------------------------------------------
diff --git a/algo/qa/QaManager.h b/algo/qa/QaManager.h
index 4a291800c5..ab7e397c5f 100644
--- a/algo/qa/QaManager.h
+++ b/algo/qa/QaManager.h
@@ -30,7 +30,7 @@ namespace cbm::algo::qa
     Manager(Manager&&) = delete;
 
     /// \brief Destructor
-    ~Manager() = delete;
+    ~Manager() = default;
 
     /// \brief Copy assignment operator
     Manager& operator=(const Manager&) = delete;
@@ -39,7 +39,7 @@ namespace cbm::algo::qa
     Manager& operator=(Manager&&) = delete;
 
     /// \brief Gets an instance of QA data
-    std::shared_ptr<Data> GetData() { return fpData; }
+    std::shared_ptr<Data> GetData() const { return fpData; }
 
     /// \brief Initializes the instance and sends the histogram and canvas configuration to the server
     void Init();
diff --git a/algo/qa/QaTaskHeader.h b/algo/qa/QaTaskHeader.h
new file mode 100644
index 0000000000..4038cd0d3e
--- /dev/null
+++ b/algo/qa/QaTaskHeader.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   QaTaskHeader.h
+/// \date   10.02.2025
+/// \brief  A header for a particular QA task, must be inherited by a given QA task
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#pragma once
+
+#include "qa/QaManager.h"
+
+#include <memory>
+
+namespace cbm::algo::qa
+{
+  /// \class TaskHeader
+  /// \brief An interface to the qa::Manager
+  /// \note  Must be inherited by a QA task
+  class TaskHeader {
+   public:
+    /// \brief Constructor
+    /// \param pManager a QA-manager
+    TaskHeader(const std::unique_ptr<Manager>& pManager) : fpData(pManager->GetData()) {}
+
+    /// \brief Copy constructor
+    TaskHeader(const TaskHeader&) = delete;
+
+    /// \brief Move constructor
+    TaskHeader(TaskHeader&&) = delete;
+
+    /// \brief Destructor
+    ~TaskHeader() = default;
+
+    /// \brief Copy assignment operator
+    TaskHeader& operator=(const TaskHeader&) = delete;
+
+    /// \brief Move assignment operator
+    TaskHeader& operator=(TaskHeader&&) = delete;
+
+   protected:
+    /// \brief Adds a canvas configuration
+    /// \param canvas  A CanvasConfig object
+    void AddCanvasConfig(const CanvasConfig& canvas) { fpData->AddCanvasConfig(canvas); }
+
+    /// \brief  Creates a QA-object and returns the pointer to it
+    /// \tparam Obj      A type of the histogram (H1D, H2D, Prof1D, Prof2D)
+    /// \tparam Args...  A signature of the histogram constructor
+    /// \param  args     Parameters, passed to a histogram constructor
+    template<class Obj, typename... Args>
+    Obj* MakeObj(Args... args)
+    {
+      fpData->MakeObj<Obj>(args...);
+    }
+
+   private:
+    std::shared_ptr<Data> fpData{nullptr};  ///< An instance of the QA data (shared between different tasks)
+  };
+}  // namespace cbm::algo::qa
-- 
GitLab