From cb2eca9c4ae7faa7cf764d82141b801e1d8299ce Mon Sep 17 00:00:00 2001 From: Dennis Klein Date: Fri, 18 Jun 2021 15:13:54 +0200 Subject: [PATCH] feat(top): Add new fairmq-top tool --- .gitignore | 1 + CMakeLists.txt | 5 + cmake/FairMQDependencies.cmake | 7 ++ cmake/FairMQProjectSettings.cmake | 4 +- cmake/FairMQSummary.cmake | 12 +- cmake/Findimtui.cmake | 58 +++++++++ fairmq/sdk/CMakeLists.txt | 33 +++-- fairmq/sdk/top/README.md | 19 +++ fairmq/sdk/top/Tool.h | 200 ++++++++++++++++++++++++++++++ fairmq/sdk/top/TopTable.h | 200 ++++++++++++++++++++++++++++++ fairmq/sdk/top/runTool.cxx | 39 ++++++ 11 files changed, 566 insertions(+), 12 deletions(-) create mode 100644 cmake/Findimtui.cmake create mode 100644 fairmq/sdk/top/README.md create mode 100644 fairmq/sdk/top/Tool.h create mode 100644 fairmq/sdk/top/TopTable.h create mode 100644 fairmq/sdk/top/runTool.cxx diff --git a/.gitignore b/.gitignore index f0a60098..4fee9af6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ install .vscode /compile_commands.json .cache +imgui.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c12cf12..7547a37c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,8 @@ fairmq_build_option(USE_EXTERNAL_GTEST "Do not use bundled GTest. Not recommend DEFAULT OFF) fairmq_build_option(FAIRMQ_DEBUG_MODE "Compile in debug mode (may decrease performance)." DEFAULT OFF) +fairmq_build_option(BUILD_SDK_TOP_TOOL "Build the fairmq-top tool." + DEFAULT OFF REQUIRES "BUILD_SDK") ################################################################################ @@ -116,6 +118,9 @@ endif() if(BUILD_TIDY_TOOL) list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool) endif() +if(BUILD_SDK_TOP_TOOL) + list(APPEND PROJECT_PACKAGE_COMPONENTS sdk_top_tool) +endif() ################################################################################ diff --git a/cmake/FairMQDependencies.cmake b/cmake/FairMQDependencies.cmake index bcab5c7a..c7f1363f 100644 --- a/cmake/FairMQDependencies.cmake +++ b/cmake/FairMQDependencies.cmake @@ -77,9 +77,16 @@ if(BUILD_TIDY_TOOL) find_package2(PRIVATE LLVM REQUIRED) find_package2(PRIVATE Clang REQUIRED) set(Clang_VERSION ${LLVM_VERSION}) +endif() + +if(BUILD_TIDY_TOOL OR BUILD_SDK_TOP_TOOL) find_package2(PRIVATE CLI11 REQUIRED) endif() +if(BUILD_SDK_TOP_TOOL) + find_package2(PRIVATE imtui REQUIRED) +endif() + find_package2_implicit_dependencies() # Always call last after latest find_package2 if(PROJECT_PACKAGE_DEPENDENCIES) diff --git a/cmake/FairMQProjectSettings.cmake b/cmake/FairMQProjectSettings.cmake index c264f2fe..2390566d 100644 --- a/cmake/FairMQProjectSettings.cmake +++ b/cmake/FairMQProjectSettings.cmake @@ -106,7 +106,9 @@ if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") endif() list(JOIN _sanitizers "," _sanitizers) -set(_sanitizers "-fsanitize=${_sanitizers}") +if(_sanitizers) + set(_sanitizers "-fsanitize=${_sanitizers}") +endif() # Configure build types set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" "RelWithDebInfo") diff --git a/cmake/FairMQSummary.cmake b/cmake/FairMQSummary.cmake index 2eb4d8e9..09246a49 100644 --- a/cmake/FairMQSummary.cmake +++ b/cmake/FairMQSummary.cmake @@ -70,11 +70,17 @@ macro(fairmq_summary_components) endif() message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}") if(BUILD_TIDY_TOOL) - set(sdk_tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DBUILD_TIDY_TOOL=OFF${CR})") + set(tidy_summary "${BGreen}YES${CR} (disable with ${BMagenta}-DBUILD_TIDY_TOOL=OFF${CR})") else() - set(sdk_tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TIDY_TOOL=ON${CR})") + set(tidy_summary "${BRed} NO${CR} (default, enable with ${BMagenta}-DBUILD_TIDY_TOOL=ON${CR})") endif() - message(STATUS " ${BWhite}tidy_tool${CR} ${sdk_tidy_summary}") + message(STATUS " ${BWhite}tidy_tool${CR} ${tidy_summary}") + if(BUILD_SDK_TOP_TOOL) + set(sdk_top_summary "${BGreen}YES${CR} EXPERIMENTAL (disable with ${BMagenta}-DBUILD_SDK_TOP_TOOL=OFF${CR})") + else() + set(sdk_top_summary "${BRed} NO${CR} EXPERIMENTAL (default, enable with ${BMagenta}-DBUILD_SDK_TOP_TOOL=ON${CR})") + endif() + message(STATUS " ${BWhite}sdk_top_tool${CR} ${sdk_top_summary}") endmacro() macro(fairmq_summary_static_analysis) diff --git a/cmake/Findimtui.cmake b/cmake/Findimtui.cmake new file mode 100644 index 00000000..1aa28b0a --- /dev/null +++ b/cmake/Findimtui.cmake @@ -0,0 +1,58 @@ +################################################################################ +# Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# # +# This software is distributed under the terms of the # +# GNU Lesser General Public Licence (LGPL) version 3, # +# copied verbatim in the file "LICENSE" # +################################################################################ + +set(pkg imtui) +find_path(${pkg}_INCLUDE_DIR + NAMES ${pkg}.h + PATH_SUFFIXES include/${pkg} + DOC "imtui include directory" +) +get_filename_component(${pkg}_INCLUDE_DIR "${${pkg}_INCLUDE_DIR}/.." ABSOLUTE) + +find_library(${pkg}_LIBRARY + NAMES lib${pkg}.so + PATH_SUFFIXES lib lib64 + DOC "Path to libimtui.a" +) + +find_library(${pkg}_ncurses_LIBRARY + NAMES lib${pkg}-ncurses.so + PATH_SUFFIXES lib lib64 + DOC "Path to libimtui-ncurses.a" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(${pkg} REQUIRED_VARS + ${pkg}_INCLUDE_DIR + ${pkg}_LIBRARY + ${pkg}_ncurses_LIBRARY +) + +get_filename_component(${pkg}_PREFIX "${${pkg}_INCLUDE_DIR}/.." ABSOLUTE) + +if(${pkg}_FOUND AND NOT TARGET ${pkg}::${pkg}-ncurses) + add_library(${pkg}::${pkg}-ncurses SHARED IMPORTED) + set_target_properties(${pkg}::${pkg}-ncurses PROPERTIES + IMPORTED_LOCATION ${${pkg}_ncurses_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${${pkg}_INCLUDE_DIR} + ) +endif() + +if(${pkg}_FOUND AND NOT TARGET ${pkg}::${pkg}) + add_library(${pkg}::${pkg} SHARED IMPORTED) + set_target_properties(${pkg}::${pkg} PROPERTIES + IMPORTED_LOCATION ${${pkg}_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${${pkg}_INCLUDE_DIR} + ) +endif() + +mark_as_advanced( + ${pkg}_INCLUDE_DIR + ${pkg}_LIBRARY + ${pkg}_ncurses_LIBRARY +) diff --git a/fairmq/sdk/CMakeLists.txt b/fairmq/sdk/CMakeLists.txt index 79b1c267..811b833b 100644 --- a/fairmq/sdk/CMakeLists.txt +++ b/fairmq/sdk/CMakeLists.txt @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # +# Copyright (C) 2019-2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH # # # # This software is distributed under the terms of the # # GNU Lesser General Public Licence (LGPL) version 3, # @@ -84,18 +84,35 @@ endif() ############### # executables # ############### -add_executable(fairmq-dds-command-ui ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx) -target_link_libraries(fairmq-dds-command-ui - FairMQ - Commands - SDK - StateMachine -) +set(target fairmq-dds-command-ui) +add_executable(${target} ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx) +target_link_libraries(${target} PRIVATE FairMQ Commands SDK StateMachine) +target_compile_features(${target} PRIVATE cxx_std_17) +if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY) + fairmq_target_tidy(TARGET ${target}) +endif() + +if(BUILD_SDK_TOP_TOOL) + set(fairmq_top "fairmq-top") + add_executable(${fairmq_top} + top/runTool.cxx + top/Tool.h + ) + target_link_libraries(${fairmq_top} PRIVATE + SDK asio::asio CLI11::CLI11 imtui::imtui imtui::imtui-ncurses) + target_compile_features(${fairmq_top} PRIVATE cxx_std_17) + if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY) + fairmq_target_tidy(TARGET ${fairmq_top}) + endif() +else() + set(fairmq_top) +endif() install( TARGETS SDK fairmq-dds-command-ui + ${fairmq_top} EXPORT ${PROJECT_EXPORT_SET} RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR} diff --git a/fairmq/sdk/top/README.md b/fairmq/sdk/top/README.md new file mode 100644 index 00000000..17ffc976 --- /dev/null +++ b/fairmq/sdk/top/README.md @@ -0,0 +1,19 @@ +### `fairmq-top` + +`htop`-like TUI to monitor/control a running FairMQ topology. + +EXPERIMENTAL - Currently mainly used as internal development tool. + +Enable building by passing `-DBUILD_FAIRMQ=ON -DBUILD_SDK_COMMANDS=ON -DBUILD_SDK=ON -DBUILD_DDS_PLUGIN=ON -DBUILD_SDK_TOP_TOOL=ON`. + +--- + +How to build [imtui](https://github.com/ggerganov/imtui) (requires ncurses devel package installed): + +``` +git clone https://github.com/ggerganov/imtui +cmake -S imtui -B -DIMTUI_INSTALL_IMGUI_HEADERS=ON -DIMTUI_SUPPORT_NCURSES=ON -DCMAKE_INSTALL_PREFIX= +cmake --build --target install +``` + +and then pass `-Dimtui_ROOT=` to the FairMQ configure above. diff --git a/fairmq/sdk/top/Tool.h b/fairmq/sdk/top/Tool.h new file mode 100644 index 00000000..6988229e --- /dev/null +++ b/fairmq/sdk/top/Tool.h @@ -0,0 +1,200 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#ifndef FAIR_MQ_SDK_TOP_TOOL_H +#define FAIR_MQ_SDK_TOP_TOOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fair::mq::sdk::top { + +using namespace std::chrono_literals; + +struct Tool +{ + static constexpr auto DefaultFrameDrawInterval = 200ms; // 5Hz + static constexpr auto DefaultInputPollInterval = 10ms; // 100Hz + static constexpr auto DefaultDataPollInterval = 333ms; // 3Hz + + Tool(DDSSession::Id session_id) + : fScreen(Tool::MakeScreen()) + , fSignals(fIoCtx, SIGINT, SIGTERM, SIGSEGV) + , fFrameDrawInterval(DefaultFrameDrawInterval) + , fFrameDrawTimer(fIoCtx) + , fInputPollInterval(DefaultInputPollInterval) + , fInputPollTimer(fIoCtx) + , fDdsSessionId(std::move(session_id)) + , fDataPollInterval(DefaultInputPollInterval) + , fDataPollTimer(fIoCtx) + { + fSignals.async_wait([&](std::error_code const&, int) { + fIoCtx.stop(); + Tool::ShutdownImTui(); + }); + } + Tool(Tool const&) = delete; + Tool& operator=(Tool const&) = delete; + Tool(Tool&&) = delete; + Tool& operator=(Tool&&) = delete; + ~Tool() { Tool::ShutdownImTui(); } + + auto DrawFrame() -> void + { + fFrameDrawTimer.expires_from_now(fFrameDrawInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + ImTui_ImplNcurses_NewFrame(); + ImTui_ImplText_NewFrame(); + ImGui::NewFrame(); + + ImGui::SetNextWindowPos({0.0f, 0.0f}); + ImGui::SetNextWindowSize(ImGui::GetContentRegionAvail()); + ImGui::Begin( + tools::ToString("fairmq-top v", FAIRMQ_VERSION, " session: ", fDdsSessionId, " active topo: ", fDdsTopo->GetName()).c_str(), + nullptr, + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); + ImGui::TextUnformatted(tools::ToString(this).c_str()); + // auto tasks(fDdsTopo->GetTasks()); + // for (auto const& task : tasks) { + // ImGui::TextUnformatted(tools::ToString(task.GetId(), task.GetCollectionId()).c_str()); + // } + // auto const agents(fDdsSession->RequestAgentInfo()); + // for (auto const& agent : agents) { + // ImGui::TextUnformatted(tools::ToString(agent).c_str()); + // } + // for (auto const& dev : fFmqTopoState) { + // ImGui::TextUnformatted( + // tools::ToString(dev.state, " ", fDdsTasks.at(dev.taskId)).c_str()); + // } + ImGui::End(); + + ImGui::Render(); + ImTui_ImplText_RenderDrawData(ImGui::GetDrawData(), &fScreen); + ImTui_ImplNcurses_DrawScreen(); + fFrameDrawTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->DrawFrame(); + } + }); + } + + auto PollInput() -> void + { + fInputPollTimer.expires_from_now(fInputPollInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + + if (ImGui::IsKeyPressed('q', false)) { + asio::post([&]() { fIoCtx.stop(); }); + } + + fInputPollTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->PollInput(); + } + }); + } + + auto PollData() -> void + { + fDataPollTimer.expires_from_now(fDataPollInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + + fFmqTopoState = fFmqTopo->GetCurrentState(); + + fDataPollTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->PollData(); + } + }); + } + + auto LoadSession(DDSTopology::Path const& topo) -> void + { + if (fDdsSessionId.empty()) { + fDdsSession = std::make_unique(getMostRecentRunningDDSSession(fDdsEnv)); + fDdsSessionId = fDdsSession->GetId(); + } else { + fDdsSession = std::make_unique(fDdsSessionId); + } + fDdsTopo = std::make_unique(topo, fDdsEnv); + fFmqTopo = std::make_unique(fIoCtx.get_executor(), *fDdsTopo, *fDdsSession); + + this->PollData(); + } + + auto Run(DDSTopology::Path const& topo) -> int + { + asio::post([&]() { this->PollInput(); }); + asio::post([&]() { this->LoadSession(topo); }); + asio::post([&]() { this->DrawFrame(); }); + try { + fIoCtx.run(); + } catch (...) { + Tool::ShutdownImTui(); + throw; + } + return 0; + } + + private: + asio::io_context fIoCtx; + ImTui::TScreen& fScreen; + asio::signal_set fSignals; + std::chrono::milliseconds fFrameDrawInterval; + asio::steady_timer fFrameDrawTimer; + std::chrono::milliseconds fInputPollInterval; + asio::steady_timer fInputPollTimer; + DDSSession::Id fDdsSessionId; + + // TODO refactor ff members into separate class + DDSEnvironment fDdsEnv; + std::unique_ptr fDdsSession; + std::unique_ptr fDdsTopo; + std::unique_ptr fFmqTopo; + std::chrono::milliseconds fDataPollInterval; + asio::steady_timer fDataPollTimer; + TopologyState fFmqTopoState; + std::map fDdsTasks; + + static auto MakeScreen() -> ImTui::TScreen& + { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + auto& screen(*ImTui_ImplNcurses_Init(true)); + ImTui_ImplText_Init(); + return screen; + } + + static auto ShutdownImTui() -> void + { + ImTui_ImplText_Shutdown(); + ImTui_ImplNcurses_Shutdown(); + } +}; + +} // namespace fair::mq::sdk::top + +#endif /* FAIR_MQ_SDK_TOP_TOOL_H */ diff --git a/fairmq/sdk/top/TopTable.h b/fairmq/sdk/top/TopTable.h new file mode 100644 index 00000000..6988229e --- /dev/null +++ b/fairmq/sdk/top/TopTable.h @@ -0,0 +1,200 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#ifndef FAIR_MQ_SDK_TOP_TOOL_H +#define FAIR_MQ_SDK_TOP_TOOL_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fair::mq::sdk::top { + +using namespace std::chrono_literals; + +struct Tool +{ + static constexpr auto DefaultFrameDrawInterval = 200ms; // 5Hz + static constexpr auto DefaultInputPollInterval = 10ms; // 100Hz + static constexpr auto DefaultDataPollInterval = 333ms; // 3Hz + + Tool(DDSSession::Id session_id) + : fScreen(Tool::MakeScreen()) + , fSignals(fIoCtx, SIGINT, SIGTERM, SIGSEGV) + , fFrameDrawInterval(DefaultFrameDrawInterval) + , fFrameDrawTimer(fIoCtx) + , fInputPollInterval(DefaultInputPollInterval) + , fInputPollTimer(fIoCtx) + , fDdsSessionId(std::move(session_id)) + , fDataPollInterval(DefaultInputPollInterval) + , fDataPollTimer(fIoCtx) + { + fSignals.async_wait([&](std::error_code const&, int) { + fIoCtx.stop(); + Tool::ShutdownImTui(); + }); + } + Tool(Tool const&) = delete; + Tool& operator=(Tool const&) = delete; + Tool(Tool&&) = delete; + Tool& operator=(Tool&&) = delete; + ~Tool() { Tool::ShutdownImTui(); } + + auto DrawFrame() -> void + { + fFrameDrawTimer.expires_from_now(fFrameDrawInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + ImTui_ImplNcurses_NewFrame(); + ImTui_ImplText_NewFrame(); + ImGui::NewFrame(); + + ImGui::SetNextWindowPos({0.0f, 0.0f}); + ImGui::SetNextWindowSize(ImGui::GetContentRegionAvail()); + ImGui::Begin( + tools::ToString("fairmq-top v", FAIRMQ_VERSION, " session: ", fDdsSessionId, " active topo: ", fDdsTopo->GetName()).c_str(), + nullptr, + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); + ImGui::TextUnformatted(tools::ToString(this).c_str()); + // auto tasks(fDdsTopo->GetTasks()); + // for (auto const& task : tasks) { + // ImGui::TextUnformatted(tools::ToString(task.GetId(), task.GetCollectionId()).c_str()); + // } + // auto const agents(fDdsSession->RequestAgentInfo()); + // for (auto const& agent : agents) { + // ImGui::TextUnformatted(tools::ToString(agent).c_str()); + // } + // for (auto const& dev : fFmqTopoState) { + // ImGui::TextUnformatted( + // tools::ToString(dev.state, " ", fDdsTasks.at(dev.taskId)).c_str()); + // } + ImGui::End(); + + ImGui::Render(); + ImTui_ImplText_RenderDrawData(ImGui::GetDrawData(), &fScreen); + ImTui_ImplNcurses_DrawScreen(); + fFrameDrawTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->DrawFrame(); + } + }); + } + + auto PollInput() -> void + { + fInputPollTimer.expires_from_now(fInputPollInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + + if (ImGui::IsKeyPressed('q', false)) { + asio::post([&]() { fIoCtx.stop(); }); + } + + fInputPollTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->PollInput(); + } + }); + } + + auto PollData() -> void + { + fDataPollTimer.expires_from_now(fDataPollInterval); // this will drift a bit (depending + // on outstanding work in fIoCtx) + + fFmqTopoState = fFmqTopo->GetCurrentState(); + + fDataPollTimer.async_wait([&](std::error_code const& e) { + if (!e) { + this->PollData(); + } + }); + } + + auto LoadSession(DDSTopology::Path const& topo) -> void + { + if (fDdsSessionId.empty()) { + fDdsSession = std::make_unique(getMostRecentRunningDDSSession(fDdsEnv)); + fDdsSessionId = fDdsSession->GetId(); + } else { + fDdsSession = std::make_unique(fDdsSessionId); + } + fDdsTopo = std::make_unique(topo, fDdsEnv); + fFmqTopo = std::make_unique(fIoCtx.get_executor(), *fDdsTopo, *fDdsSession); + + this->PollData(); + } + + auto Run(DDSTopology::Path const& topo) -> int + { + asio::post([&]() { this->PollInput(); }); + asio::post([&]() { this->LoadSession(topo); }); + asio::post([&]() { this->DrawFrame(); }); + try { + fIoCtx.run(); + } catch (...) { + Tool::ShutdownImTui(); + throw; + } + return 0; + } + + private: + asio::io_context fIoCtx; + ImTui::TScreen& fScreen; + asio::signal_set fSignals; + std::chrono::milliseconds fFrameDrawInterval; + asio::steady_timer fFrameDrawTimer; + std::chrono::milliseconds fInputPollInterval; + asio::steady_timer fInputPollTimer; + DDSSession::Id fDdsSessionId; + + // TODO refactor ff members into separate class + DDSEnvironment fDdsEnv; + std::unique_ptr fDdsSession; + std::unique_ptr fDdsTopo; + std::unique_ptr fFmqTopo; + std::chrono::milliseconds fDataPollInterval; + asio::steady_timer fDataPollTimer; + TopologyState fFmqTopoState; + std::map fDdsTasks; + + static auto MakeScreen() -> ImTui::TScreen& + { + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + auto& screen(*ImTui_ImplNcurses_Init(true)); + ImTui_ImplText_Init(); + return screen; + } + + static auto ShutdownImTui() -> void + { + ImTui_ImplText_Shutdown(); + ImTui_ImplNcurses_Shutdown(); + } +}; + +} // namespace fair::mq::sdk::top + +#endif /* FAIR_MQ_SDK_TOP_TOOL_H */ diff --git a/fairmq/sdk/top/runTool.cxx b/fairmq/sdk/top/runTool.cxx new file mode 100644 index 00000000..9c45d4d3 --- /dev/null +++ b/fairmq/sdk/top/runTool.cxx @@ -0,0 +1,39 @@ +/******************************************************************************** + * Copyright (C) 2021 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#include +#include +#include +#include +#include + +auto main(int argc, char** argv) -> int +{ + CLI::App app("htop-like TUI to monitor/control a running FairMQ topology\n", "fairmq-top"); + + auto session(app.add_option("-s,--session", "DDS session id") + ->default_val(fair::mq::sdk::DDSSession::Id()) + ->envname("DDS_SESSION_ID")); + auto topo(app.add_option("-t,--topology", "DDS topology file")->check(CLI::ExistingFile)); + auto version(app.add_flag("-v,--version", "Print version")->excludes(session)->excludes(topo)); + + try { + app.parse(argc, argv); + } catch (CLI::ParseError const& e) { + return app.exit(e); + } + + if (version->as()) { + std::cout << FAIRMQ_GIT_VERSION << '\n'; + return EXIT_SUCCESS; + } + + fair::mq::sdk::top::Tool tool(session->as()); + + return tool.Run(topo->as()); +}