diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index e7591a0bc18983727527dc6072beb76de5bc11c3..809483cc879147c3cfc0cca9fb0f407d1780a56a 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -111,6 +111,9 @@ set(SRCS global/Reco.cxx qa/DigiEventQa.cxx qa/Histo1D.cxx + qa/CanvasConfig.cxx + qa/PadConfig.cxx + qa/QaData.cxx ca/TrackingChain.cxx ca/qa/CaQaBuilder.cxx ca/qa/CaQaConfig.cxx diff --git a/algo/ca/TrackingChain.cxx b/algo/ca/TrackingChain.cxx index e115b6316cb7e3e28c35aad582534cc6bc96465f..e5533656fdbecb74698764942bb6441e194c270b 100644 --- a/algo/ca/TrackingChain.cxx +++ b/algo/ca/TrackingChain.cxx @@ -34,24 +34,7 @@ using cbm::algo::ca::constants::clrs::GNb; // grin bald text // --------------------------------------------------------------------------------------------------------------------- // -TrackingChain::TrackingChain(std::shared_ptr<HistogramSender> histoSender) : fpSender(histoSender) -{ - // ------ Initialize the histogram sender - if (fpSender) { - auto histCfgs = fQaBuilder.GetConfig().GetHistogramConfigs(); - auto canvCfgs = fQaBuilder.GetConfig().GetCanvasConfigs(); - fpSender->PrepareAndSendMsg(std::pair<uint32_t, uint32_t>(histCfgs.size(), canvCfgs.size()), - zmq::send_flags::sndmore); - for (const auto& cfg : histCfgs) { - fpSender->PrepareAndSendMsg(cfg, zmq::send_flags::sndmore); - } - for (const auto& cfg : canvCfgs) { - fpSender->PrepareAndSendMsg(cfg, zmq::send_flags::sndmore); - } - // Histograms serialization and emission to close multi-part message - fpSender->PrepareAndSendMsg(std::vector<Histo1D>{}, zmq::send_flags::none); - } -} +TrackingChain::TrackingChain(std::shared_ptr<HistogramSender> histoSender) : fQaBuilder(ca::qa::Builder(histoSender)) {} // --------------------------------------------------------------------------------------------------------------------- // @@ -73,7 +56,10 @@ void TrackingChain::Init() fCaFramework.ReceiveParameters(std::move(parameters)); // ------ Initialize QA builder - fQaBuilder.RegisterParameters(&fCaFramework.GetParameters()); + if (fQaBuilder.IsSenderDefined()) { + fQaBuilder.RegisterParameters(&fCaFramework.GetParameters()); + fQaBuilder.Init(); + } } // --------------------------------------------------------------------------------------------------------------------- @@ -158,14 +144,11 @@ TrackingChain::Output_t TrackingChain::PrepareOutput() output.monitorData = fCaMonitorData; // QA - if (fpSender) { + if (fQaBuilder.IsSenderDefined()) { fQaBuilder.RegisterInputData(&fCaFramework.GetInputData()); fQaBuilder.RegisterTracks(&output.tracks); fQaBuilder.RegisterRecoHitIndices(&fCaFramework.fRecoHits); - auto qaData = fQaBuilder.Build(); - L_(info) << "TrackingChain: " << qaData.ToString(); - fpSender->PrepareAndSendMsg(qaData.VectorOfH1(), zmq::send_flags::none); - L_(info) << "TrackingChain: Published " << qaData.VectorOfH1().size() << " 1D-histograms"; + fQaBuilder.Build(); } diff --git a/algo/ca/TrackingChain.h b/algo/ca/TrackingChain.h index 03d5cc430aa396cb86b78ef50ebdf04735102f6d..782a6c67a88617c255858be345f92baf6a24acf9 100644 --- a/algo/ca/TrackingChain.h +++ b/algo/ca/TrackingChain.h @@ -95,9 +95,6 @@ namespace cbm::algo template<ca::EDetectorID DetID> void ReadHits(PartitionedSpan<const ca::HitTypes_t::at<DetID>> hits); - /// \brief - - // ************************* // ** Framework variables @@ -105,8 +102,7 @@ namespace cbm::algo ca::TrackingMonitorData fCaMonitorData{}; ///< CA monitor data object ca::Framework fCaFramework{}; ///< CA framework instance ca::DataManager fCaDataManager{}; ///< CA data manager - ca::qa::Builder fQaBuilder{ca::qa::Config()}; ///< CA QA builder - std::shared_ptr<HistogramSender> fpSender; ///< Histogram sender + ca::qa::Builder fQaBuilder; ///< CA QA builder // ************************ // ** Auxilary variables diff --git a/algo/ca/qa/CaQaBuilder.cxx b/algo/ca/qa/CaQaBuilder.cxx index 19e210fa8159886b6e43d62abca7325b6d0a9078..a319fd510f9e7eee27296542b8a1c0ee7d3d1196 100644 --- a/algo/ca/qa/CaQaBuilder.cxx +++ b/algo/ca/qa/CaQaBuilder.cxx @@ -14,33 +14,114 @@ #include "CaParameters.h" #include "CaTrack.h" +#include <fmt/format.h> + +using cbm::algo::CanvasConfig; +using cbm::algo::Histo1D; +using cbm::algo::PadConfig; +using cbm::algo::QaData; using cbm::algo::ca::constants::math::Pi; using cbm::algo::ca::qa::Builder; -using cbm::algo::ca::qa::Config; -using cbm::algo::ca::qa::Data; -using cbm::algo::ca::qa::H1Key; + // --------------------------------------------------------------------------------------------------------------------- // -Data Builder::Build() +void Builder::Init() { - Data res(fConfig); + if (!fpSender.get()) { + return; + } + + // ---- Init histograms + // TODO: Provide definition from config + constexpr int nParPads = 4; + std::array<std::string, knTrkParPoints> vsPointName = {"first", "last"}; + for (int i = 0; i < knTrkParPoints; ++i) { + { + auto sName = fmt::format("track_{}_theta", vsPointName[i]); + auto sTitl = fmt::format("#theta at {} hit; #theta", vsPointName[i]); + fvphTrkTheta[i] = fQaData.MakeObj<Histo1D>(62, 0., 90., sName, sTitl); + } + { + auto sName = fmt::format("track_{}_phi", vsPointName[i]); + auto sTitl = fmt::format("#phi at {} hit; #phi", vsPointName[i]); + fvphTrkPhi[i] = fQaData.MakeObj<Histo1D>(62, -180., 180., sName, sTitl); + } + { + auto sName = fmt::format("track_{}_chi2_ndf", vsPointName[i]); + auto sTitl = fmt::format("#chi^{{2}}/NDF at {} hit; #chi^{{2}}/NDF", vsPointName[i]); + fvphTrkChi2Ndf[i] = fQaData.MakeObj<Histo1D>(100, 0., 20., sName, sTitl); + } + { + auto sName = fmt::format("track_{}_hit_station", vsPointName[i]); + auto sTitl = fmt::format("Station of {} hit;ID_{{sta}}", vsPointName[i]); + fvphHitSta[i] = fQaData.MakeObj<Histo1D>(100, -.5, 10.5, sName, sTitl); + } + } + fphTrkNofHits = fQaData.MakeObj<Histo1D>(11, -0.5, 10.5, "n_hits", "Number of hits;N_{hit}"); + + // ---- Init canvases + { + // Track parameters at first/last track hit + for (int i = 0; i < knTrkParPoints; ++i) { + auto sCanvName = fmt::format("ca_trk_{}_hit", vsPointName[i]); + auto sCanvTitl = fmt::format("Track parameters at {} hit", vsPointName[i]); + auto canv = CanvasConfig(sCanvName, sCanvTitl); + std::vector<PadConfig> pads; + pads.reserve(nParPads); + for (int iPad = 0; iPad < nParPads; ++iPad) { + auto& pad = pads.emplace_back(); + pad.SetLog(false, true); + pad.SetGrid(true, true); + } + pads[0].RegisterHistogram(fvphTrkTheta[i], "hist"); + pads[1].RegisterHistogram(fvphTrkPhi[i], "hist"); + pads[2].RegisterHistogram(fvphTrkChi2Ndf[i], "hist"); + pads[3].RegisterHistogram(fvphHitSta[i], "hist"); + for (auto& pad : pads) { + canv.AddPadConfig(pad); + } + fQaData.AddCanvasConfig(canv); + } + + // Number of hits in track + { + auto canv = CanvasConfig("ca_nhits_in_trk", "Number of hit in track"); + auto pad = PadConfig(); + pad.SetGrid(true, true); + pad.SetLog(false, true); + pad.RegisterHistogram(fphTrkNofHits, "hist"); + canv.AddPadConfig(pad); + fQaData.AddCanvasConfig(canv); + } + } + + fQaData.Init(fpSender); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void Builder::Build() +{ + if (!fpSender.get()) { + return; + } if (!fpParameters) { LOG(error) << "cbm::algo::ca::qa::Builder::Build(): parameters object is undefined"; - return res; + return; } if (!fpInputData) { LOG(error) << "cbm::algo::ca::qa::Builder::Build(): input data object is undefined"; - return res; + return; } if (!fpvTracks) { LOG(error) << "cbm::algo::ca::qa::Builder::Build(): tracks vector is undefined"; - return res; + return; } if (!fpvRecoHits) { LOG(error) << "cbm::algo::ca::qa::Builder::Build(): reco hit indices vector is undefined"; - return res; + return; } // Fill track histograms @@ -51,16 +132,20 @@ Data Builder::Build() int iFstHit = (*fpvRecoHits)[trkFirstHit]; int iLstHit = (*fpvRecoHits)[trkFirstHit + nHits - 1]; - //res.H1(H1Key::TrackFirstTx).Add(track.fParFirst.GetTx()); - //res.H1(H1Key::TrackFirstTy).Add(track.fParFirst.GetTy()); - res.H1(H1Key::FstTrkTheta).Add(track.fParFirst.GetTheta() * 180. / Pi); - res.H1(H1Key::FstTrkPhi).Add(track.fParFirst.GetPhi() * 180. / Pi); - res.H1(H1Key::FstChi2Ndf).Add(track.fParFirst.GetChiSq() / track.fParFirst.GetNdf()); - res.H1(H1Key::FstHitSta).Add(fpInputData->GetHit(iFstHit).Station()); - res.H1(H1Key::LstHitSta).Add(fpInputData->GetHit(iLstHit).Station()); - res.H1(H1Key::TrkNofHits).Add(nHits); + // Distributions in different track points + for (int ip = 0; ip < knTrkParPoints; ++ip) { + int iHit = (ip == 0 ? iFstHit : iLstHit); + const auto& trkPar = (ip == 0 ? track.fParFirst : track.fParLast); + const auto& hit = fpInputData->GetHit(iHit); + fvphTrkTheta[ip]->Add(trkPar.GetTheta() * 180. / Pi); + fvphTrkPhi[ip]->Add(trkPar.GetPhi() * 180. / Pi); + fvphTrkChi2Ndf[ip]->Add(trkPar.GetChiSq() / trkPar.GetNdf()); + fvphHitSta[ip]->Add(hit.Station()); + } + // Other distributions + fphTrkNofHits->Add(nHits); trkFirstHit += nHits; } - return res; + fQaData.Send(fpSender); } diff --git a/algo/ca/qa/CaQaBuilder.h b/algo/ca/qa/CaQaBuilder.h index c2fb40ebe40c2a818518f40318434138015f3257..f9a6748f953c37066da641ef9a6dd5b031de7cb1 100644 --- a/algo/ca/qa/CaQaBuilder.h +++ b/algo/ca/qa/CaQaBuilder.h @@ -11,15 +11,19 @@ #include "CaHit.h" // for HitIndex_t #include "CaQaBuilder.h" -#include "CaQaData.h" #include "CaVector.h" +#include "QaData.h" -namespace cbm::algo::ca +namespace cbm::algo { - template<typename DataT> - class Parameters; - class InputData; - class Track; + class Histo1D; + namespace ca + { + template<typename DataT> + class Parameters; + class InputData; + class Track; + } // namespace ca } // namespace cbm::algo::ca namespace cbm::algo::ca::qa @@ -33,11 +37,12 @@ namespace cbm::algo::ca::qa using HitIndexV_t = ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>>; /// \brief Default destructor - Builder() = delete; + /// \param pSender Pointer to the histogram sender + Builder(std::shared_ptr<HistogramSender> pSender) : fpSender(pSender){}; /// \brief Constructor from the configuration object /// \param config QA configuration object - Builder(const Config& config) : fConfig(config) {} + Builder() = default; /// \brief Copy constructor Builder(const Builder&) = delete; @@ -55,10 +60,13 @@ namespace cbm::algo::ca::qa Builder& operator=(Builder&&) = delete; /// \brief QA execution function - Data Build(); + void Build(); - /// \brief Gets QA config - const Config& GetConfig() const { return fConfig; } + /// \brief Initializes the QA + void Init(); + + /// \brief Checks, if the histogram sender is defined + bool IsSenderDefined() const { return static_cast<bool>(fpSender.get()); } /// \brief Registers tracking input data /// \note Call per TS @@ -76,11 +84,23 @@ namespace cbm::algo::ca::qa /// \note Call per run void RegisterParameters(const Parameters<fvec>* pParameters) { fpParameters = pParameters; } + private: - Config fConfig; ///< QA configuration - const Parameters<fvec>* fpParameters = nullptr; ///< Pointer to tracking parameters - const Vector<Track>* fpvTracks = nullptr; ///< Pointer to tracks vector - const InputData* fpInputData = nullptr; ///< Pointer to input data - const Vector<HitIndex_t>* fpvRecoHits = nullptr; ///< Pointer to reco hit indices + QaData fQaData{"CaQa"}; ///< QA data + + std::shared_ptr<HistogramSender> fpSender = nullptr; ///< Histogram sender + const Parameters<fvec>* fpParameters = nullptr; ///< Pointer to tracking parameters + const Vector<Track>* fpvTracks = nullptr; ///< Pointer to tracks vector + const InputData* fpInputData = nullptr; ///< Pointer to input data + const Vector<HitIndex_t>* fpvRecoHits = nullptr; ///< Pointer to reco hit indices + + // ----- List of the histograms ------------------------------------------------------------------------------------ + static constexpr int knTrkParPoints = 2; ///< Number of track points to build par distributions + + std::array<Histo1D*, knTrkParPoints> fvphTrkTheta = {{0}}; ///< hist: theta at first/last hit + std::array<Histo1D*, knTrkParPoints> fvphTrkPhi = {{0}}; ///< hist: phi at first/last hit + std::array<Histo1D*, knTrkParPoints> fvphTrkChi2Ndf = {{0}}; ///< hist: chi2/NDF at first/last hit + std::array<Histo1D*, knTrkParPoints> fvphHitSta = {{0}}; ///< hist: station of first/last hit + Histo1D* fphTrkNofHits = nullptr; ///< hist: number of hits in track }; } // namespace cbm::algo::ca::qa diff --git a/algo/qa/CanvasConfig.cxx b/algo/qa/CanvasConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..449eba144c4b56f87d38740ed3151748f9f71a0b --- /dev/null +++ b/algo/qa/CanvasConfig.cxx @@ -0,0 +1,56 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file CanvasConfig.cxx +/// \date 12.02.2024 +/// \brief A class representing a canvas in the message for the Histogram server (implementation) +/// \author S.Zharko <s.zharko@gsi.de> + +#include "CanvasConfig.h" + +#include <sstream> + +using cbm::algo::CanvasConfig; +using cbm::algo::PadConfig; + +// --------------------------------------------------------------------------------------------------------------------- +// +CanvasConfig::CanvasConfig(std::string_view name, std::string_view title, int nPadsX, int nPadsY) + : fsName(name) + , fsTitle(title) + , fNofPadsX(nPadsX) + , fNofPadsY(nPadsY) +{ +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void CanvasConfig::AddPadConfig(const PadConfig& pad) +{ + fvsPadConfigs.push_back(pad.ToString()); + + // Re-calculate number of pads + if (fNofPadsX * fNofPadsY < static_cast<int>(fvsPadConfigs.size())) { + if (fNofPadsX > fNofPadsY) { + ++fNofPadsY; + } + else { + ++fNofPadsX; + } + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +std::string CanvasConfig::ToString() const +{ + // TODO: What to return (throw), if 0 pads defined? + + std::stringstream cfg; + cfg << fsName << ';' << fsTitle << ';' << fNofPadsX << ';' << fNofPadsY; + for (const auto& padCfg : fvsPadConfigs) { + cfg << ';' << padCfg; + } + return cfg.str(); +} diff --git a/algo/qa/CanvasConfig.h b/algo/qa/CanvasConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..0efe885c1c57a8192520dafa3790c338130c7286 --- /dev/null +++ b/algo/qa/CanvasConfig.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file CanvasConfig.h +/// \date 12.02.2024 +/// \brief A class representing a canvas in the message for the Histogram server +/// \author S.Zharko <s.zharko@gsi.de> + +#pragma once + +#include "PadConfig.h" + +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace cbm::algo +{ + /// \class CanvasConfig + /// \brief A canvas configuration for the histogram server + /// + /// The class represents a configuration of the canvas, which can be converted to the initialization + /// message for the histogram server. + class CanvasConfig { + public: + /// \brief Constructor + /// \param name Name of the canvas + /// \param title Title of the canvas + /// \param nPadsX Number of pads along x-axis + /// \param nPadsY Number of pads along y-axis + CanvasConfig(std::string_view name, std::string_view title, int nPadsX = 1, int nPadsY = 1); + + /// \brief Copy constructor + CanvasConfig(const CanvasConfig&) = default; + + /// \brief Move constructor + CanvasConfig(CanvasConfig&&) = default; + + /// \brief Copy assignment operator + CanvasConfig& operator=(const CanvasConfig&) = default; + + /// \brief Move assignment operator + CanvasConfig& operator=(CanvasConfig&&) = default; + + /// \brief Destructor + ~CanvasConfig() = default; + + /// \brief Adds a pad to the canvas + void AddPadConfig(const PadConfig& pad); + + /// \brief Returns message config + std::string ToString() const; + + private: + std::string fsName; ///< Name of the canvas + std::string fsTitle; ///< Name of the pad + std::vector<std::string> fvsPadConfigs; ///< Vector of pad config messages + int fNofPadsX = 1; ///< Number of pads along the x-axis + int fNofPadsY = 1; ///< Number of pads along the y-axis + }; +} // namespace cbm::algo diff --git a/algo/qa/PadConfig.cxx b/algo/qa/PadConfig.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5743b665a6a6c63e741944642b61a2c1dbb2425b --- /dev/null +++ b/algo/qa/PadConfig.cxx @@ -0,0 +1,48 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file Pad.cxx +/// \date 12.02.2024 +/// \brief A class representing a Pad in the message for the Histogram server (implementation) +/// \author S.Zharko <s.zharko@gsi.de> + +#include "PadConfig.h" + +#include <sstream> + +using cbm::algo::PadConfig; + +// --------------------------------------------------------------------------------------------------------------------- +// +void PadConfig::SetGrid(bool gridX, bool gridY) +{ + fbGridX = gridX; + fbGridY = gridY; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void PadConfig::SetLog(bool logX, bool logY, bool logZ) +{ + fbLogX = logX; + fbLogY = logY; + fbLogZ = logZ; +} + +// --------------------------------------------------------------------------------------------------------------------- +// +std::string PadConfig::ToString() const +{ + std::stringstream cfg; + cfg << fbGridX << ',' << fbGridY << ',' << fbLogX << ',' << fbLogY << ',' << fbLogZ; + if (fvObjectList.empty()) { + cfg << ','; + } + else { + for (const auto& [name, opt] : fvObjectList) { + cfg << ",(" << name << ',' << opt << ')'; + } + } + return cfg.str(); +} diff --git a/algo/qa/PadConfig.h b/algo/qa/PadConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..d95c1cdc1d7387d2aa0097616494ab052ef88bef --- /dev/null +++ b/algo/qa/PadConfig.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file PadConfig.h +/// \date 12.02.2024 +/// \brief A class representing a pad config in the message for the Histogram server +/// \author S.Zharko <s.zharko@gsi.de> + +#pragma once + +#include "Histo1D.h" + +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +namespace cbm::algo +{ + /// \class PadConfig + /// \brief A pad configuration for the histogram server + /// + /// The class represents a configuration of the pad, which can be converted in the part of the + /// canvas initialization message for the histogram server. + class PadConfig { + public: + /// \brief Constructor + PadConfig() = default; + + /// \brief Copy constructor + PadConfig(const PadConfig&) = default; + + /// \brief Move constructor + PadConfig(PadConfig&&) = default; + + /// \brief Copy assignment operator + PadConfig& operator=(const PadConfig&) = default; + + /// \brief Move assignment operator + PadConfig& operator=(PadConfig&&) = default; + + /// \brief Destructor + ~PadConfig() = default; + + /// \brief Set grid flags + /// \param gridX Flag for x-axis + /// \param gridY Flag for y-axis + void SetGrid(bool gridX, bool gridY = false); + + /// \brief Sets logarithm axis + /// \param logX Logarithm flag for x-axis + /// \param logY Logarithm flag for y-axis + /// \param logZ Logarithm flag for z-axis + void SetLog(bool logX, bool logY = false, bool logZ = false); + + /// \brief Registers an object in the pad + /// \param name Name of the object + /// \param opt Draw options for the object + void RegisterObject(std::string_view name, std::string_view opt) + { + fvObjectList.emplace_back(std::make_pair(name, opt)); + } + + /// \brief Registers a histogram in the pad + /// \param hist Histogram object + /// \param opt Draw options for the histogram + void RegisterHistogram(const Histo1D* hist, std::string_view opt) { RegisterObject(hist->Name(), opt); } + + /// \brief Returns message config + std::string ToString() const; + + private: + bool fbGridX = false; ///< Grid flag for x-axis + bool fbGridY = false; ///< Grid flag for y-axis + bool fbLogX = false; ///< Log flag for x-axis + bool fbLogY = false; ///< Log flag for y-axis + bool fbLogZ = false; ///< Log flag for z-axis + + std::vector<std::pair<std::string, std::string>> fvObjectList; ///< List of objects on the pad + }; + + +} // namespace cbm::algo diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx new file mode 100644 index 0000000000000000000000000000000000000000..955c1ba27c67966dde4db0f439b8079325f45a38 --- /dev/null +++ b/algo/qa/QaData.cxx @@ -0,0 +1,64 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file QaData.cxx +/// \date 12.02.2024 +/// \brief A unified data-structure to handle QA objects for the online reconstruction (implementation) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "QaData.h" + +using cbm::algo::QaData; + +// --------------------------------------------------------------------------------------------------------------------- +// +void QaData::Init(std::shared_ptr<HistogramSender> histSender) +{ + if (histSender.get()) { + // Forming a histogram config message + std::vector<std::pair<std::string, std::string>> vHistCfgs; + vHistCfgs.reserve(std::distance(fvH1.begin(), fvH1.end())); + for (const auto& hist : fvH1) { + vHistCfgs.emplace_back(hist.Name(), fsName); + } + + // Forming a canvas config message + std::vector<std::pair<std::string, std::string>> vCanvCfgs; + vCanvCfgs.reserve(fvsCanvCfgs.size()); + for (const auto& canv : fvsCanvCfgs) { + vCanvCfgs.emplace_back(std::make_pair(canv.substr(0, canv.find_first_of(';')), canv)); + } + + histSender->PrepareAndSendMsg(std::pair<uint32_t, uint32_t>(vHistCfgs.size(), vCanvCfgs.size()), + zmq::send_flags::sndmore); + for (const auto& cfg : vHistCfgs) { + histSender->PrepareAndSendMsg(cfg, zmq::send_flags::sndmore); + } + for (const auto& cfg : vCanvCfgs) { + histSender->PrepareAndSendMsg(cfg, zmq::send_flags::sndmore); + } + // Histograms serialization and emission to close multi-part message + histSender->PrepareAndSendMsg(std::vector<Histo1D>{}, zmq::send_flags::none); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void QaData::Reset() +{ + // TODO: clear the vectors of other object types here as well + for (auto& hist : fvH1) { + hist.Clear(); + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void QaData::Send(std::shared_ptr<HistogramSender> histoSender) +{ + std::vector<Histo1D> vHisto1D{std::begin(fvH1), std::end(fvH1)}; + histoSender->PrepareAndSendMsg(vHisto1D, zmq::send_flags::none); + L_(info) << fsName << ": Published " << vHisto1D.size() << " 1D-histograms"; + this->Reset(); +} diff --git a/algo/qa/QaData.h b/algo/qa/QaData.h new file mode 100644 index 0000000000000000000000000000000000000000..263614458c37962cd41f83eef542f109b4945113 --- /dev/null +++ b/algo/qa/QaData.h @@ -0,0 +1,87 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file QaData.cxx +/// \date 12.02.2024 +/// \brief A unified data-structure to handle QA objects for the online reconstruction +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "CanvasConfig.h" +#include "Histo1D.h" +#include "HistogramSender.h" + +#include <forward_list> +#include <log.hpp> +#include <memory> +#include <string_view> +#include <type_traits> +#include <vector> + +namespace cbm::algo +{ + /// \class QaData + /// \brief Class to handle QA-objects in the online reconstruction + class QaData { + public: + /// \brief Constructor + /// \param name Name of the QA module (appears as the directory name in the output) + QaData(std::string_view name) : fsName(name) {} + + /// \brief Copy constructor + QaData(const QaData&) = default; + + /// \brief Move constructor + QaData(QaData&&) = default; + + /// \brief Copy assignment operator + QaData& operator=(const QaData&) = default; + + /// \brief Move assignment operator + QaData& operator=(QaData&&) = default; + + /// \brief Destructor + ~QaData() = default; + + /// \brief Adds a canvas to the canvas config list + /// \param canvas A CanvasConfig object + void AddCanvasConfig(const CanvasConfig& canvas) { fvsCanvCfgs.push_back(canvas.ToString()); } + + /// \brief Creates a QA-object and returns the pointer to it + template<class Obj, typename... Args> + Obj* MakeObj(Args... args); + + /// \brief Gets module name + std::string_view GetName() const { return fsName; } + + /// \brief Sends QA initialization information to the HistogramSender + /// \param histoSender A pointer to the histogram sender + void Init(std::shared_ptr<HistogramSender> histoSender); + + /// \brief Resets the histograms + void Reset(); + + /// \brief Sends QA data to the HistogramSender + /// \param histoSender A pointer to the histogram sender + /// \note Calls this->Reset() after sending the message to the histogram server + void Send(std::shared_ptr<HistogramSender> histoSender); + + private: + std::string fsName; ///< Name of the QA module (used as a directory name) + std::forward_list<Histo1D> fvH1 = {}; ///< List of 1D-histograms + std::vector<std::string> fvsCanvCfgs = {}; ///< Vector of canvas configs + }; + + // ------------------------------------------------------------------------------------------------------------------- + // + template<class Obj, typename... Args> + Obj* QaData::MakeObj(Args... args) + { + if constexpr (std::is_same_v<Obj, cbm::algo::Histo1D>) { + return &(fvH1.emplace_front(args...)); + } + return nullptr; + } +} // namespace cbm::algo