mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-13 08:41:16 +00:00
feat(top): Add new fairmq-top tool
This commit is contained in:
parent
bc91799e56
commit
cb2eca9c4a
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,3 +4,4 @@ install
|
||||||
.vscode
|
.vscode
|
||||||
/compile_commands.json
|
/compile_commands.json
|
||||||
.cache
|
.cache
|
||||||
|
imgui.ini
|
||||||
|
|
|
@ -47,6 +47,8 @@ fairmq_build_option(USE_EXTERNAL_GTEST "Do not use bundled GTest. Not recommend
|
||||||
DEFAULT OFF)
|
DEFAULT OFF)
|
||||||
fairmq_build_option(FAIRMQ_DEBUG_MODE "Compile in debug mode (may decrease performance)."
|
fairmq_build_option(FAIRMQ_DEBUG_MODE "Compile in debug mode (may decrease performance)."
|
||||||
DEFAULT OFF)
|
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)
|
if(BUILD_TIDY_TOOL)
|
||||||
list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool)
|
list(APPEND PROJECT_PACKAGE_COMPONENTS tidy_tool)
|
||||||
endif()
|
endif()
|
||||||
|
if(BUILD_SDK_TOP_TOOL)
|
||||||
|
list(APPEND PROJECT_PACKAGE_COMPONENTS sdk_top_tool)
|
||||||
|
endif()
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,9 +77,16 @@ if(BUILD_TIDY_TOOL)
|
||||||
find_package2(PRIVATE LLVM REQUIRED)
|
find_package2(PRIVATE LLVM REQUIRED)
|
||||||
find_package2(PRIVATE Clang REQUIRED)
|
find_package2(PRIVATE Clang REQUIRED)
|
||||||
set(Clang_VERSION ${LLVM_VERSION})
|
set(Clang_VERSION ${LLVM_VERSION})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_TIDY_TOOL OR BUILD_SDK_TOP_TOOL)
|
||||||
find_package2(PRIVATE CLI11 REQUIRED)
|
find_package2(PRIVATE CLI11 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_SDK_TOP_TOOL)
|
||||||
|
find_package2(PRIVATE imtui REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
find_package2_implicit_dependencies() # Always call last after latest find_package2
|
find_package2_implicit_dependencies() # Always call last after latest find_package2
|
||||||
|
|
||||||
if(PROJECT_PACKAGE_DEPENDENCIES)
|
if(PROJECT_PACKAGE_DEPENDENCIES)
|
||||||
|
|
|
@ -106,7 +106,9 @@ if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(JOIN _sanitizers "," _sanitizers)
|
list(JOIN _sanitizers "," _sanitizers)
|
||||||
|
if(_sanitizers)
|
||||||
set(_sanitizers "-fsanitize=${_sanitizers}")
|
set(_sanitizers "-fsanitize=${_sanitizers}")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Configure build types
|
# Configure build types
|
||||||
set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" "RelWithDebInfo")
|
set(CMAKE_CONFIGURATION_TYPES "Debug" "Release" "RelWithDebInfo")
|
||||||
|
|
|
@ -70,11 +70,17 @@ macro(fairmq_summary_components)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}")
|
message(STATUS " ${BWhite}sdk_commands${CR} ${sdk_commands_summary}")
|
||||||
if(BUILD_TIDY_TOOL)
|
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()
|
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()
|
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()
|
endmacro()
|
||||||
|
|
||||||
macro(fairmq_summary_static_analysis)
|
macro(fairmq_summary_static_analysis)
|
||||||
|
|
58
cmake/Findimtui.cmake
Normal file
58
cmake/Findimtui.cmake
Normal file
|
@ -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
|
||||||
|
)
|
|
@ -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 #
|
# This software is distributed under the terms of the #
|
||||||
# GNU Lesser General Public Licence (LGPL) version 3, #
|
# GNU Lesser General Public Licence (LGPL) version 3, #
|
||||||
|
@ -84,18 +84,35 @@ endif()
|
||||||
###############
|
###############
|
||||||
# executables #
|
# executables #
|
||||||
###############
|
###############
|
||||||
add_executable(fairmq-dds-command-ui ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx)
|
set(target fairmq-dds-command-ui)
|
||||||
target_link_libraries(fairmq-dds-command-ui
|
add_executable(${target} ${CMAKE_CURRENT_SOURCE_DIR}/runDDSCommandUI.cxx)
|
||||||
FairMQ
|
target_link_libraries(${target} PRIVATE FairMQ Commands SDK StateMachine)
|
||||||
Commands
|
target_compile_features(${target} PRIVATE cxx_std_17)
|
||||||
SDK
|
if(BUILD_TIDY_TOOL AND RUN_FAIRMQ_TIDY)
|
||||||
StateMachine
|
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(
|
install(
|
||||||
TARGETS
|
TARGETS
|
||||||
SDK
|
SDK
|
||||||
fairmq-dds-command-ui
|
fairmq-dds-command-ui
|
||||||
|
${fairmq_top}
|
||||||
|
|
||||||
EXPORT ${PROJECT_EXPORT_SET}
|
EXPORT ${PROJECT_EXPORT_SET}
|
||||||
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
RUNTIME DESTINATION ${PROJECT_INSTALL_BINDIR}
|
||||||
|
|
19
fairmq/sdk/top/README.md
Normal file
19
fairmq/sdk/top/README.md
Normal file
|
@ -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 <builddir> -DIMTUI_INSTALL_IMGUI_HEADERS=ON -DIMTUI_SUPPORT_NCURSES=ON -DCMAKE_INSTALL_PREFIX=<installdir>
|
||||||
|
cmake --build <builddir> --target install
|
||||||
|
```
|
||||||
|
|
||||||
|
and then pass `-Dimtui_ROOT=<installdir>` to the FairMQ configure above.
|
200
fairmq/sdk/top/Tool.h
Normal file
200
fairmq/sdk/top/Tool.h
Normal file
|
@ -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 <asio/io_context.hpp>
|
||||||
|
#include <asio/post.hpp>
|
||||||
|
#include <asio/signal_set.hpp>
|
||||||
|
#include <asio/steady_timer.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <fairmq/Tools.h>
|
||||||
|
#include <fairmq/Version.h>
|
||||||
|
#include <fairmq/sdk/DDSAgent.h>
|
||||||
|
#include <fairmq/sdk/DDSEnvironment.h>
|
||||||
|
#include <fairmq/sdk/DDSSession.h>
|
||||||
|
#include <fairmq/sdk/DDSTask.h>
|
||||||
|
#include <fairmq/sdk/DDSTopology.h>
|
||||||
|
#include <fairmq/sdk/Topology.h>
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
#include <imtui/imtui-impl-ncurses.h>
|
||||||
|
#include <imtui/imtui.h>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
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<DDSSession>(getMostRecentRunningDDSSession(fDdsEnv));
|
||||||
|
fDdsSessionId = fDdsSession->GetId();
|
||||||
|
} else {
|
||||||
|
fDdsSession = std::make_unique<DDSSession>(fDdsSessionId);
|
||||||
|
}
|
||||||
|
fDdsTopo = std::make_unique<DDSTopology>(topo, fDdsEnv);
|
||||||
|
fFmqTopo = std::make_unique<Topology>(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<DDSSession> fDdsSession;
|
||||||
|
std::unique_ptr<DDSTopology> fDdsTopo;
|
||||||
|
std::unique_ptr<Topology> fFmqTopo;
|
||||||
|
std::chrono::milliseconds fDataPollInterval;
|
||||||
|
asio::steady_timer fDataPollTimer;
|
||||||
|
TopologyState fFmqTopoState;
|
||||||
|
std::map<DDSTask::Id, DDSCollection::Id> 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 */
|
200
fairmq/sdk/top/TopTable.h
Normal file
200
fairmq/sdk/top/TopTable.h
Normal file
|
@ -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 <asio/io_context.hpp>
|
||||||
|
#include <asio/post.hpp>
|
||||||
|
#include <asio/signal_set.hpp>
|
||||||
|
#include <asio/steady_timer.hpp>
|
||||||
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <fairmq/Tools.h>
|
||||||
|
#include <fairmq/Version.h>
|
||||||
|
#include <fairmq/sdk/DDSAgent.h>
|
||||||
|
#include <fairmq/sdk/DDSEnvironment.h>
|
||||||
|
#include <fairmq/sdk/DDSSession.h>
|
||||||
|
#include <fairmq/sdk/DDSTask.h>
|
||||||
|
#include <fairmq/sdk/DDSTopology.h>
|
||||||
|
#include <fairmq/sdk/Topology.h>
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
#include <imtui/imtui-impl-ncurses.h>
|
||||||
|
#include <imtui/imtui.h>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
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<DDSSession>(getMostRecentRunningDDSSession(fDdsEnv));
|
||||||
|
fDdsSessionId = fDdsSession->GetId();
|
||||||
|
} else {
|
||||||
|
fDdsSession = std::make_unique<DDSSession>(fDdsSessionId);
|
||||||
|
}
|
||||||
|
fDdsTopo = std::make_unique<DDSTopology>(topo, fDdsEnv);
|
||||||
|
fFmqTopo = std::make_unique<Topology>(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<DDSSession> fDdsSession;
|
||||||
|
std::unique_ptr<DDSTopology> fDdsTopo;
|
||||||
|
std::unique_ptr<Topology> fFmqTopo;
|
||||||
|
std::chrono::milliseconds fDataPollInterval;
|
||||||
|
asio::steady_timer fDataPollTimer;
|
||||||
|
TopologyState fFmqTopoState;
|
||||||
|
std::map<DDSTask::Id, DDSCollection::Id> 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 */
|
39
fairmq/sdk/top/runTool.cxx
Normal file
39
fairmq/sdk/top/runTool.cxx
Normal file
|
@ -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 <CLI/App.hpp>
|
||||||
|
#include <CLI/Config.hpp>
|
||||||
|
#include <CLI/Formatter.hpp>
|
||||||
|
#include <fairmq/Version.h>
|
||||||
|
#include <fairmq/sdk/top/Tool.h>
|
||||||
|
|
||||||
|
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<bool>()) {
|
||||||
|
std::cout << FAIRMQ_GIT_VERSION << '\n';
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
fair::mq::sdk::top::Tool tool(session->as<fair::mq::sdk::DDSSession::Id>());
|
||||||
|
|
||||||
|
return tool.Run(topo->as<fair::mq::sdk::DDSTopology::Path>());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user