From 8d2df157a76e7ad9191d8475f6fd9c2bbeb8cfdb Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer <weiglhofer@fias.uni-frankfurt.de> Date: Thu, 16 Nov 2023 15:57:06 +0000 Subject: [PATCH] Add archive explorer to display online archives. --- algo/global/StorableRecoResults.h | 4 + services/CMakeLists.txt | 1 + services/archive_explorer/CMakeLists.txt | 53 +++++++ services/archive_explorer/app/Application.cxx | 31 ++++ services/archive_explorer/app/Application.h | 30 ++++ services/archive_explorer/app/Options.cxx | 53 +++++++ services/archive_explorer/app/Options.h | 20 +++ services/archive_explorer/app/main.cxx | 20 +++ services/archive_explorer/lib/LinkDef.h | 8 + services/archive_explorer/lib/Server.cxx | 149 ++++++++++++++++++ services/archive_explorer/lib/Server.h | 71 +++++++++ 11 files changed, 440 insertions(+) create mode 100644 services/archive_explorer/CMakeLists.txt create mode 100644 services/archive_explorer/app/Application.cxx create mode 100644 services/archive_explorer/app/Application.h create mode 100644 services/archive_explorer/app/Options.cxx create mode 100644 services/archive_explorer/app/Options.h create mode 100644 services/archive_explorer/app/main.cxx create mode 100644 services/archive_explorer/lib/LinkDef.h create mode 100644 services/archive_explorer/lib/Server.cxx create mode 100644 services/archive_explorer/lib/Server.h diff --git a/algo/global/StorableRecoResults.h b/algo/global/StorableRecoResults.h index dcf4d80dde..87d99fc8b3 100644 --- a/algo/global/StorableRecoResults.h +++ b/algo/global/StorableRecoResults.h @@ -6,7 +6,10 @@ #include "CbmDigiEvent.h" +#include "tof/Hit.h" + #include <boost/serialization/access.hpp> +#include <boost/serialization/utility.hpp> #include <boost/serialization/vector.hpp> #include <cstdint> @@ -14,6 +17,7 @@ #include "PartitionedVector.h" #include "ca/core/data/CaTrack.h" #include "ca/core/utils/CaVector.h" +#include "sts/Hit.h" namespace cbm::algo { diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt index e34c0786ba..7ccb2f7d10 100644 --- a/services/CMakeLists.txt +++ b/services/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(archive_explorer) add_subdirectory(histserv) diff --git a/services/archive_explorer/CMakeLists.txt b/services/archive_explorer/CMakeLists.txt new file mode 100644 index 0000000000..7631ccca32 --- /dev/null +++ b/services/archive_explorer/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# Application +# +set(APP cbm-archive-explorer) + +set(APP_SRCS + app/Application.cxx + app/Options.cxx + app/main.cxx +) + +add_executable(${APP} ${APP_SRCS}) + +target_link_libraries(${APP} + Algo + Boost::program_options + external::fles_logging + fmt::fmt +) + +# +# libCbmArchiveExplorer +# + +set(LIBRARY_NAME CbmArchiveExplorer) +set(LINKDEF lib/LinkDef.h) + +set(INCLUDE_DIRECTORIES + lib +) + +set(SRCS + lib/Server.cxx +) + +set(HEADERS + lib/Server.h +) + +set(PUBLIC_DEPENDENCIES + Algo + external::fles_logging + fmt::fmt + ROOT::Core + ROOT::Gpad + ROOT::Hist + ROOT::RIO + ROOT::RHTTP +) + +generate_cbm_library() + +target_link_libraries(${APP} CbmArchiveExplorer) diff --git a/services/archive_explorer/app/Application.cxx b/services/archive_explorer/app/Application.cxx new file mode 100644 index 0000000000..c3cc8e17e2 --- /dev/null +++ b/services/archive_explorer/app/Application.cxx @@ -0,0 +1,31 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#include "Application.h" + +#include <log.hpp> + +#include "RecoResultsInputArchive.h" +#include "Server.h" + +using namespace cbm::explore; +using namespace cbm::algo; + +Application::Application(const Options& options) : fOpts(options) +{ + L_(info) << "Starting application with port " << fOpts.Port(); + + L_(info) << "Opening archive " << fOpts.Input(); + + auto archive = std::make_shared<RecoResultsInputArchive>(fOpts.Input().string()); + + fServer = std::make_unique<Server>(fOpts.Port(), archive); +} + +Application::~Application() {} + +int Application::Run() +{ + fServer->Run(); + return 0; +} diff --git a/services/archive_explorer/app/Application.h b/services/archive_explorer/app/Application.h new file mode 100644 index 0000000000..49b418f221 --- /dev/null +++ b/services/archive_explorer/app/Application.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#pragma once + +#include <memory> + +#include "Options.h" + +namespace cbm::explore +{ + + class Server; + + class Application { + + public: + Application(const Options& options); + + ~Application(); + + int Run(); + + private: + Options fOpts; + + std::unique_ptr<Server> fServer; + }; + +} // namespace cbm::explore diff --git a/services/archive_explorer/app/Options.cxx b/services/archive_explorer/app/Options.cxx new file mode 100644 index 0000000000..9520cc81bd --- /dev/null +++ b/services/archive_explorer/app/Options.cxx @@ -0,0 +1,53 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#include "Options.h" + +#include <boost/program_options.hpp> + +#include <iostream> + +namespace po = boost::program_options; + +Options::Options(int argc, char** argv) +{ + po::options_description options("Options"); + + // clang-format off + options.add_options() + ("input,i", po::value(&fInput)->value_name("<input>")->required(), + "input archive") + ("port,p", po::value(&fPort)->value_name("<port>")->default_value(8080), + "port to listen on") + ("help,h", + "produce help message") + ; + // clang-format on + + po::variables_map vm; + po::command_line_parser parser {argc, argv}; + parser.options(options); + try { + auto result = parser.run(); + po::store(result, vm); + } + catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + std::cerr << "Use '-h' to display all valid options." << std::endl; + std::exit(EXIT_FAILURE); + } + + if (vm.count("help") > 0) { + std::cout << options << std::endl; + std::exit(EXIT_SUCCESS); + } + + try { + po::notify(vm); + } + catch (const po::required_option& e) { + std::cerr << "Error: " << e.what() << std::endl; + std::cerr << "Use '-h' to display all valid options." << std::endl; + std::exit(EXIT_FAILURE); + } +} diff --git a/services/archive_explorer/app/Options.h b/services/archive_explorer/app/Options.h new file mode 100644 index 0000000000..9ec407b889 --- /dev/null +++ b/services/archive_explorer/app/Options.h @@ -0,0 +1,20 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#pragma once + +#include <compat/Filesystem.h> + +class Options { +public: + Options(int argc, char** argv); + + int Port() const { return fPort; } + + cbm::algo::fs::path Input() const { return fInput; } + + +private: + int fPort; + std::string fInput; +}; diff --git a/services/archive_explorer/app/main.cxx b/services/archive_explorer/app/main.cxx new file mode 100644 index 0000000000..e0a206f2d3 --- /dev/null +++ b/services/archive_explorer/app/main.cxx @@ -0,0 +1,20 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ + +#include <log.hpp> + +#include "Application.h" +#include "Options.h" + +using namespace cbm::explore; + +int main(int argc, char** argv) +{ + + Options options(argc, argv); + + Application app(options); + + return app.Run(); +} diff --git a/services/archive_explorer/lib/LinkDef.h b/services/archive_explorer/lib/LinkDef.h new file mode 100644 index 0000000000..1e8c4b81af --- /dev/null +++ b/services/archive_explorer/lib/LinkDef.h @@ -0,0 +1,8 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class cbm::explore::Server + ; diff --git a/services/archive_explorer/lib/Server.cxx b/services/archive_explorer/lib/Server.cxx new file mode 100644 index 0000000000..6ddb0950a2 --- /dev/null +++ b/services/archive_explorer/lib/Server.cxx @@ -0,0 +1,149 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#include "Server.h" + +#include <TH1.h> +#include <THttpServer.h> +#include <TSystem.h> + +#include <log.hpp> + +#include <fmt/format.h> + +using namespace cbm::explore; + +ClassImp(Server); + +Server::Server(int port, std::shared_ptr<algo::RecoResultsInputArchive> archive) + : TNamed("server", "server") + , fArchive(archive) +{ + L_(info) << "Starting Server with port " << port; + + fServer = new THttpServer(fmt::format("http:{}", port).c_str()); + fServer->SetTimer(0, true); + + // FIXME: Why doesn't root use the local jsroot by default??? + fServer->SetJSROOT("https://jsroot.gsi.de/7.1.0/"); +} + +Server::~Server() {} + +int Server::Run() +{ + // register this object in http server + fServer->Register("/", this); + fServer->Hide("/server"); + + // enable monitoring and + // specify items to draw when page is opened + // fServer->SetItemField("/", "_monitoring", "5000"); + + // Specify layout. TODO: does not work? + fServer->SetItemField("/", "_layout", "grid3x3"); + + fServer->RegisterCommand("/NextTS", "/server/->RequestNextTS()"); + fServer->SetItemField("/NextTS", "_title", "Next Timeslice"); + + CreateHistos(); + NextTS(); + + L_(info) << "Starting event loop"; + while (true) { + + int nRequests = 0; + do { + nRequests = fServer->ProcessRequests(); + if (nRequests > 0) L_(info) << "Processed " << nRequests << " requests"; + + // sleep minimal time + // TODO: This is terrible. And the sleep should not be in this loop. + // But ROOTJS sometimes sends commands multiple times, + // And this is the only way, i was able to catch duplicate commands + gSystem->Sleep(SleepPerTick_ms); + } while (nRequests > 0); + + if (fRequestNextTS) { + NextTS(); + fRequestNextTS = false; + } + } + + return 0; +} + +void Server::NextTS() +{ + L_(info) << "Fetching next Timeslice..."; + + auto recoResults = fArchive->get(); + + if (fArchive->eos()) { + L_(info) << "End of archive reached"; + return; + } + + ResetHistos(); + + auto& stsHits = recoResults->StsHits(); + for (size_t p = 0; p < stsHits.NPartitions(); p++) { + auto [sensor, address] = stsHits.Partition(p); + L_(info) << "Filling sensor " << address << " with " << sensor.size() << " hits"; + for (auto& hit : sensor) { + fHStsHitsX->Fill(hit.X()); + fHStsHitsY->Fill(hit.Y()); + fHStsHitsZ->Fill(hit.Z()); + fHStsHitsTime->Fill(hit.Time()); + fHStsHitsDx->Fill(hit.Dx()); + fHStsHitsDy->Fill(hit.Dy()); + fHStsHitsDz->Fill(hit.fDz); + fHStsHitsDxy->Fill(hit.fDxy); + fHStsHitsTimeError->Fill(hit.TimeError()); + fHStsHitsDu->Fill(hit.fDu); + fHStsHitsDv->Fill(hit.fDv); + } + } + + L_(info) << "Finished Timeslice"; +} + +void Server::ResetHistos() +{ + for (auto* histo : fHistos) { + histo->Reset(); + } +} + +void Server::CreateHistos() +{ + CreateFolder("/sts", "STS"); + CreateFolder("/sts/hits", "Hits"); + CreateHisto(fHStsHitsX, "/sts/hits", "hStsHitsX", "Sts Hits X", 100, -10, 10); + CreateHisto(fHStsHitsY, "/sts/hits", "hStsHitsY", "Sts Hits Y", 100, -10, 10); + CreateHisto(fHStsHitsZ, "/sts/hits", "hStsHitsZ", "Sts Hits Z", 100, -10, 10); + CreateHisto(fHStsHitsTime, "/sts/hits", "hStsHitsTime", "Sts Hits Time", 1000, 0, 128000000); + CreateHisto(fHStsHitsDx, "/sts/hits", "hStsHitsDx", "Sts Hits Dx", 100, 0, 0.1); + CreateHisto(fHStsHitsDy, "/sts/hits", "hStsHitsDy", "Sts Hits Dy", 100, 0, 0.1); + CreateHisto(fHStsHitsDz, "/sts/hits", "hStsHitsDz", "Sts Hits Dz", 100, 0, 0.1); + CreateHisto(fHStsHitsDxy, "/sts/hits", "hStsHitsDxy", "Sts Hits Dxy", 100, 0, 0.1); + CreateHisto(fHStsHitsTimeError, "/sts/hits", "hStsHitsTimeError", "Sts Hits Time Error", 100, 0, 0.1); + CreateHisto(fHStsHitsDu, "/sts/hits", "hStsHitsDu", "Sts Hits Du", 100, 0, 0.1); + CreateHisto(fHStsHitsDv, "/sts/hits", "hStsHitsDv", "Sts Hits Dv", 100, 0, 0.1); +} + +template<typename Histo_t> +void Server::CreateHisto(Histo_t*& histo, const char* folder, const char* name, const char* title, int nbins, + double xlow, double xmax) +{ + histo = new Histo_t(name, title, nbins, xlow, xmax); + histo->SetDirectory(nullptr); + fServer->Register(folder, histo); + fHistos.push_back(histo); +} + +void Server::CreateFolder(const char* path, const char* name) +{ + fServer->CreateItem(path, name); + fServer->SetItemField(path, "_kind", "Folder"); +} diff --git a/services/archive_explorer/lib/Server.h b/services/archive_explorer/lib/Server.h new file mode 100644 index 0000000000..d20c7e3e8a --- /dev/null +++ b/services/archive_explorer/lib/Server.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2023 FIAS Frankfurt Institute for Advanced Studies, Frankfurt / Main + SPDX-License-Identifier: GPL-3.0-only + Authors: Felix Weiglhofer [committer] */ +#pragma once + +#include <Rtypes.h> +#include <TNamed.h> + +#include <memory> + +#include "RecoResultsInputArchive.h" + +class THttpServer; +class TH1; +class TH1F; +class TH1I; + +namespace cbm::explore +{ + + class Server : public TNamed { + + public: + Server(int port, std::shared_ptr<algo::RecoResultsInputArchive> archive); + + virtual ~Server(); + + int Run(); + + // Server commands + void RequestNextTS() { fRequestNextTS = true; } + + private: + static constexpr int SleepPerTick_ms = 50; + + // Internal state + THttpServer* fServer = nullptr; //! + std::shared_ptr<algo::RecoResultsInputArchive> fArchive; //! + std::vector<TH1*> fHistos; //! + + // Server commands + bool fRequestNextTS = false; + + // Histograms + TH1F* fHStsHitsX = nullptr; + TH1F* fHStsHitsY = nullptr; + TH1F* fHStsHitsZ = nullptr; + TH1I* fHStsHitsTime = nullptr; + TH1F* fHStsHitsDx = nullptr; + TH1F* fHStsHitsDy = nullptr; + TH1F* fHStsHitsDz = nullptr; + TH1F* fHStsHitsDxy = nullptr; + TH1F* fHStsHitsTimeError = nullptr; + TH1F* fHStsHitsDu = nullptr; + TH1F* fHStsHitsDv = nullptr; + + void NextTS(); + void ResetHistos(); + + void CreateHistos(); + + template<typename Histo_t> + void CreateHisto(Histo_t*& histo, const char* folder, const char* name, const char* title, int nbins, double xlow, + double xmax); + + void CreateFolder(const char* path, const char* name); + + ClassDef(Server, 1); + }; + +} // namespace cbm::explore -- GitLab