From 86947912dcced13ca201b8a3e443066a7a8abce8 Mon Sep 17 00:00:00 2001 From: Eugene Lykov Date: Thu, 25 Dec 2025 13:00:42 +0000 Subject: [PATCH] Step 2 --- CMakeLists.txt | 3 + src/CMakeLists.txt | 66 ++-------- src/Config/CMakeLists.txt | 9 ++ src/Config/Client/CMakeLists.txt | 17 +++ src/Config/Client/Config.cpp | 19 +++ src/Config/Client/Config.h | 15 +++ src/Config/Client/NetworkConfig.h | 14 +++ src/Config/Common/CMakeLists.txt | 12 ++ src/Config/Common/IniParser.cpp | 58 +++++++++ src/Config/Common/IniParser.h | 17 +++ src/Config/Server/BarStyleConfig.h | 13 ++ src/Config/Server/CMakeLists.txt | 17 +++ src/Config/Server/Config.cpp | 39 ++++++ src/Config/Server/Config.h | 21 ++++ src/Config/Server/DisplayConfig.h | 12 ++ src/Config/Server/NetworkConfig.h | 14 +++ src/Config/Server/TextStyleConfig.h | 12 ++ src/Display/CMakeLists.txt | 15 +-- src/Display/Graphics/CMakeLists.txt | 16 ++- src/Display/Graphics/Framebuffer.cpp | 138 +++++++++++++-------- src/Display/Graphics/Framebuffer.h | 40 +++--- src/Display/Graphics/FramebufferRotation.h | 12 ++ src/Display/Graphics/Renderer.cpp | 40 +++--- src/Display/Graphics/Renderer.h | 10 +- src/Display/UI/Bar/Bar.cpp | 46 +++++-- src/Display/UI/Bar/Bar.h | 20 +-- src/Display/UI/Bar/CMakeLists.txt | 16 +-- src/Display/UI/Bar/Orientation.h | 10 ++ src/Display/UI/Bar/Style.h | 26 ++++ src/Display/UI/CMakeLists.txt | 20 +-- src/Display/UI/HostBlock/CMakeLists.txt | 19 +++ src/Display/UI/HostBlock/HostBlock.cpp | 93 ++++++++++++++ src/Display/UI/HostBlock/HostBlock.h | 58 +++++++++ src/Display/UI/Text/BitmapFont.cpp | 2 +- src/Display/UI/Text/BitmapFont.h | 2 +- src/Display/UI/Text/CMakeLists.txt | 17 ++- src/Helpers/CMakeLists.txt | 11 +- src/Main/CMakeLists.txt | 8 ++ src/Main/Client/CMakeLists.txt | 20 +++ src/{Client.cpp => Main/Client/main.cpp} | 11 +- src/Main/Server/CMakeLists.txt | 24 ++++ src/Main/Server/main.cpp | 29 +++++ src/Metrics/CMakeLists.txt | 4 +- src/Metrics/Collector.cpp | 32 +++-- src/Metrics/Collector.h | 6 +- src/Metrics/Cpu.h | 3 +- src/Metrics/Host.cpp | 15 ++- src/Model/CMakeLists.txt | 7 +- src/Network/CMakeLists.txt | 22 ++-- src/Network/{ => Client}/Agent.cpp | 24 ++-- src/Network/{ => Client}/Agent.h | 12 +- src/Network/Client/CMakeLists.txt | 16 +++ src/Network/{ => Client}/Client.cpp | 38 +++--- src/Network/{ => Client}/Client.h | 4 +- src/Network/Server/CMakeLists.txt | 16 +++ src/Network/{ => Server}/Server.cpp | 2 +- src/Network/{ => Server}/Server.h | 0 src/Server.cpp | 25 ---- 58 files changed, 959 insertions(+), 328 deletions(-) create mode 100644 src/Config/CMakeLists.txt create mode 100644 src/Config/Client/CMakeLists.txt create mode 100644 src/Config/Client/Config.cpp create mode 100644 src/Config/Client/Config.h create mode 100644 src/Config/Client/NetworkConfig.h create mode 100644 src/Config/Common/CMakeLists.txt create mode 100644 src/Config/Common/IniParser.cpp create mode 100644 src/Config/Common/IniParser.h create mode 100644 src/Config/Server/BarStyleConfig.h create mode 100644 src/Config/Server/CMakeLists.txt create mode 100644 src/Config/Server/Config.cpp create mode 100644 src/Config/Server/Config.h create mode 100644 src/Config/Server/DisplayConfig.h create mode 100644 src/Config/Server/NetworkConfig.h create mode 100644 src/Config/Server/TextStyleConfig.h create mode 100644 src/Display/Graphics/FramebufferRotation.h create mode 100644 src/Display/UI/Bar/Orientation.h create mode 100644 src/Display/UI/Bar/Style.h create mode 100644 src/Display/UI/HostBlock/CMakeLists.txt create mode 100644 src/Display/UI/HostBlock/HostBlock.cpp create mode 100644 src/Display/UI/HostBlock/HostBlock.h create mode 100644 src/Main/CMakeLists.txt create mode 100644 src/Main/Client/CMakeLists.txt rename src/{Client.cpp => Main/Client/main.cpp} (50%) create mode 100644 src/Main/Server/CMakeLists.txt create mode 100644 src/Main/Server/main.cpp rename src/Network/{ => Client}/Agent.cpp (54%) rename src/Network/{ => Client}/Agent.h (65%) create mode 100644 src/Network/Client/CMakeLists.txt rename src/Network/{ => Client}/Client.cpp (52%) rename src/Network/{ => Client}/Client.h (89%) create mode 100644 src/Network/Server/CMakeLists.txt rename src/Network/{ => Server}/Server.cpp (98%) rename src/Network/{ => Server}/Server.h (100%) delete mode 100644 src/Server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 288687b..e578019 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,4 +44,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(INCLUDE_BASE_DIR ${PROJECT_SOURCE_DIR}/src) +option(BUILD_SERVER "Build ${PROJECT_DIR} server" ON) +option(BUILD_CLIENT "Build ${PROJECT_DIR} client" ON) + add_subdirectory("src") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0ab278..c3eb77f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,58 +1,14 @@ # Добавляем все модули как поддиректории -add_subdirectory(Display) -add_subdirectory(Helpers) -add_subdirectory(Metrics) -add_subdirectory(Model) -add_subdirectory(Network) -set(TARGET_NAME_SERVER ${PROJECT_NAME}-Server) -message(STATUS "Configuring ${TARGET_NAME_SERVER}") +if (BUILD_SERVER OR BUILD_CLIENT) + if (BUILD_SERVER) + add_subdirectory(Display) + add_subdirectory(Model) + endif() + add_subdirectory(Helpers) + add_subdirectory(Config) + add_subdirectory(Metrics) + add_subdirectory(Network) -# Основной исполняемый файл -add_executable(${TARGET_NAME_SERVER} Server.cpp) - -# Линкуем зависимости -target_link_libraries(${TARGET_NAME_SERVER} - PRIVATE - Display - Helpers - Metrics - Model - Network -) - -# Указываем корень include для dashboard -target_include_directories(${TARGET_NAME_SERVER} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} -) - - -message(STATUS "") -message(STATUS "${TARGET_NAME_SERVER} summary") -message(STATUS "") - -set(TARGET_NAME_CLIENT ${PROJECT_NAME}-Client) -message(STATUS "Configuring ${TARGET_NAME_CLIENT}") - -# Основной исполняемый файл -add_executable(${TARGET_NAME_CLIENT} Client.cpp) - -# Линкуем зависимости -target_link_libraries(${TARGET_NAME_CLIENT} - PRIVATE - Helpers - Metrics - Network -) - -# Указываем корень include для dashboard -target_include_directories(${TARGET_NAME_CLIENT} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} -) - - -message(STATUS "") -message(STATUS "${TARGET_NAME_CLIENT} summary") -message(STATUS "") + add_subdirectory(Main) +endif() diff --git a/src/Config/CMakeLists.txt b/src/Config/CMakeLists.txt new file mode 100644 index 0000000..de60e6f --- /dev/null +++ b/src/Config/CMakeLists.txt @@ -0,0 +1,9 @@ +message(STATUS "·Configuring Config") + +add_subdirectory(Common) +if (BUILD_SERVER) + add_subdirectory(Server) +endif() +if (BUILD_CLIENT) + add_subdirectory(Client) +endif() \ No newline at end of file diff --git a/src/Config/Client/CMakeLists.txt b/src/Config/Client/CMakeLists.txt new file mode 100644 index 0000000..d08640d --- /dev/null +++ b/src/Config/Client/CMakeLists.txt @@ -0,0 +1,17 @@ +message(STATUS "··Configuring Client") + +add_library(Config.Client + Config.cpp +) + +add_library(Config::Client ALIAS Config.Client) + +target_link_libraries(Config.Client + PUBLIC + Config::Common +) + +target_include_directories(Config.Client + PUBLIC + ${INCLUDE_BASE_DIR} +) \ No newline at end of file diff --git a/src/Config/Client/Config.cpp b/src/Config/Client/Config.cpp new file mode 100644 index 0000000..99408e4 --- /dev/null +++ b/src/Config/Client/Config.cpp @@ -0,0 +1,19 @@ +#include "Config/Client/Config.h" +#include "Config/Common/IniParser.h" +#include + +namespace Config::Client +{ + Config Config::load(const std::string &path) + { + Config cfg; + IniParser ini; + ini.load(path); + + cfg.network.serverHost = ini.get("network", "server_host", "127.0.0.1"); + cfg.network.serverPort = std::stoi(ini.get("network", "server_port", "5005")); + cfg.network.intervalMs = std::chrono::milliseconds(std::stoi(ini.get("network", "interval_ms", "1000"))); + + return cfg; + } +} // namespace Config::Client diff --git a/src/Config/Client/Config.h b/src/Config/Client/Config.h new file mode 100644 index 0000000..44ddfe0 --- /dev/null +++ b/src/Config/Client/Config.h @@ -0,0 +1,15 @@ +#pragma once +#include +#include "Display/Graphics/Color.h" +#include "Config/Client/NetworkConfig.h" + +namespace Config::Client +{ + class Config + { + public: + static Config load(const std::string &path); + + NetworkConfig network; + }; +} // namespace Config::Client diff --git a/src/Config/Client/NetworkConfig.h b/src/Config/Client/NetworkConfig.h new file mode 100644 index 0000000..584309e --- /dev/null +++ b/src/Config/Client/NetworkConfig.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace Config::Client +{ + struct NetworkConfig + { + std::string serverHost = "127.0.0.1"; + int serverPort = 5005; + std::chrono::milliseconds intervalMs = std::chrono::milliseconds(1000); + }; +} // namespace Config::Client diff --git a/src/Config/Common/CMakeLists.txt b/src/Config/Common/CMakeLists.txt new file mode 100644 index 0000000..b0bd5ef --- /dev/null +++ b/src/Config/Common/CMakeLists.txt @@ -0,0 +1,12 @@ +message(STATUS "··Configuring Common") + +add_library(Config.Common + IniParser.cpp +) + +add_library(Config::Common ALIAS Config.Common) + +target_include_directories(Config.Common + PUBLIC + ${INCLUDE_BASE_DIR} +) \ No newline at end of file diff --git a/src/Config/Common/IniParser.cpp b/src/Config/Common/IniParser.cpp new file mode 100644 index 0000000..9fe308a --- /dev/null +++ b/src/Config/Common/IniParser.cpp @@ -0,0 +1,58 @@ +#include "Config/Common/IniParser.h" +#include +#include +#include + +static std::string trim(std::string s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) + { return !isspace(c); })); + s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) + { return !isspace(c); }) + .base(), + s.end()); + return s; +} + +bool IniParser::load(const std::string &path) +{ + std::ifstream f(path); + if (!f) + return false; + + std::string line, section; + while (std::getline(f, line)) + { + line = trim(line); + if (line.empty() || line[0] == '#' || line[0] == ';') + continue; + + if (line.front() == '[' && line.back() == ']') + { + section = line.substr(1, line.size() - 2); + continue; + } + + auto pos = line.find('='); + if (pos == std::string::npos) + continue; + + std::string key = trim(line.substr(0, pos)); + std::string val = trim(line.substr(pos + 1)); + data_[section][key] = val; + } + return true; +} + +std::string IniParser::get(const std::string §ion, + const std::string &key, + const std::string &def) const +{ + auto s = data_.find(section); + if (s == data_.end()) + return def; + auto k = s->second.find(key); + if (k == s->second.end()) + return def; + return k->second; +} \ No newline at end of file diff --git a/src/Config/Common/IniParser.h b/src/Config/Common/IniParser.h new file mode 100644 index 0000000..9684d17 --- /dev/null +++ b/src/Config/Common/IniParser.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +class IniParser +{ +public: + bool load(const std::string &path); + + std::string get(const std::string §ion, + const std::string &key, + const std::string &def = "") const; + +private: + using Section = std::unordered_map; + std::unordered_map data_; +}; \ No newline at end of file diff --git a/src/Config/Server/BarStyleConfig.h b/src/Config/Server/BarStyleConfig.h new file mode 100644 index 0000000..08a8c5f --- /dev/null +++ b/src/Config/Server/BarStyleConfig.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Display/Graphics/Color.h" + +namespace Config::Server +{ + struct BarStyleConfig + { + Display::Graphics::Color background; + Display::Graphics::Color fill; + }; + +} // namespace Config::Server diff --git a/src/Config/Server/CMakeLists.txt b/src/Config/Server/CMakeLists.txt new file mode 100644 index 0000000..66b0969 --- /dev/null +++ b/src/Config/Server/CMakeLists.txt @@ -0,0 +1,17 @@ +message(STATUS "··Configuring Server") + +add_library(Config.Server + Config.cpp +) + +add_library(Config::Server ALIAS Config.Server) + +target_link_libraries(Config.Server + PUBLIC + Config::Common +) + +target_include_directories(Config.Server + PUBLIC + ${INCLUDE_BASE_DIR} +) \ No newline at end of file diff --git a/src/Config/Server/Config.cpp b/src/Config/Server/Config.cpp new file mode 100644 index 0000000..b3514ff --- /dev/null +++ b/src/Config/Server/Config.cpp @@ -0,0 +1,39 @@ +#include "Config/Server/Config.h" +#include "Config/Common/IniParser.h" +#include + +namespace Config::Server +{ + static Display::Graphics::Color parseColor(const std::string &s) + { + int r, g, b; + char c; + std::stringstream ss(s); + ss >> r >> c >> g >> c >> b; + return {uint8_t(r), uint8_t(g), uint8_t(b)}; + } + + Config Config::load(const std::string &path) + { + Config cfg; + IniParser ini; + ini.load(path); + + cfg.display.rotation = std::stoi(ini.get("display", "rotation", "0")); + cfg.display.refreshMs = std::chrono::milliseconds(std::stoi(ini.get("display", "refresh_ms", "1000"))); + + cfg.network.listenPort = std::stoi(ini.get("network", "listen_port", "5005")); + cfg.network.intervalMs = std::chrono::milliseconds(std::stoi(ini.get("network", "interval_ms", "1000"))); + + cfg.textStyle.fontPath = ini.get("text", "font_path", + "/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf"); + cfg.textStyle.fontSize = std::stoi(ini.get("text", "font_size", "10")); + + cfg.barStyle.background = parseColor( + ini.get("bar", "background", "30,30,30")); + cfg.barStyle.fill = parseColor( + ini.get("bar", "fill", "0,180,0")); + + return cfg; + } +} // namespace Config::Server diff --git a/src/Config/Server/Config.h b/src/Config/Server/Config.h new file mode 100644 index 0000000..0ff9479 --- /dev/null +++ b/src/Config/Server/Config.h @@ -0,0 +1,21 @@ +#pragma once +#include + +#include "Config/Server/BarStyleConfig.h" +#include "Config/Server/DisplayConfig.h" +#include "Config/Server/NetworkConfig.h" +#include "Config/Server/TextStyleConfig.h" + +namespace Config::Server +{ + class Config + { + public: + static Config load(const std::string &path); + + DisplayConfig display; + NetworkConfig network; + TextStyleConfig textStyle; + BarStyleConfig barStyle; + }; +} // namespace Config::Server diff --git a/src/Config/Server/DisplayConfig.h b/src/Config/Server/DisplayConfig.h new file mode 100644 index 0000000..e738018 --- /dev/null +++ b/src/Config/Server/DisplayConfig.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Config::Server +{ + struct DisplayConfig + { + int rotation = 0; + std::chrono::milliseconds refreshMs = std::chrono::milliseconds(1000); + }; +} // namespace Config::Server diff --git a/src/Config/Server/NetworkConfig.h b/src/Config/Server/NetworkConfig.h new file mode 100644 index 0000000..05f5639 --- /dev/null +++ b/src/Config/Server/NetworkConfig.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace Config::Server +{ + struct NetworkConfig + { + std::string listenAddr = "0.0.0.0"; + int listenPort = 5005; + std::chrono::milliseconds intervalMs = std::chrono::milliseconds(1000); + }; +} // namespace Config::Server diff --git a/src/Config/Server/TextStyleConfig.h b/src/Config/Server/TextStyleConfig.h new file mode 100644 index 0000000..d6c4d9f --- /dev/null +++ b/src/Config/Server/TextStyleConfig.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace Config::Server +{ + struct TextStyleConfig + { + std::string fontPath; + int fontSize = 10; + }; +} // namespace Config::Server diff --git a/src/Display/CMakeLists.txt b/src/Display/CMakeLists.txt index e3a935e..f048661 100644 --- a/src/Display/CMakeLists.txt +++ b/src/Display/CMakeLists.txt @@ -1,17 +1,4 @@ -message(STATUS " Configuring Display") - -add_library(Display - INTERFACE -) +message(STATUS "·Configuring Display") add_subdirectory(Graphics) add_subdirectory(UI) - -target_link_libraries(Display - INTERFACE Graphics UI -) - -target_include_directories(Display - INTERFACE - ${INCLUDE_BASE_DIR} -) diff --git a/src/Display/Graphics/CMakeLists.txt b/src/Display/Graphics/CMakeLists.txt index e6369ef..c073f7f 100644 --- a/src/Display/Graphics/CMakeLists.txt +++ b/src/Display/Graphics/CMakeLists.txt @@ -1,15 +1,21 @@ -message(STATUS " Configuring Graphics") +message(STATUS "··Configuring Graphics") -add_library(Graphics +add_library(DisplayGraphics Framebuffer.cpp Renderer.cpp ) -target_link_libraries(Graphics - PRIVATE Model Helpers UI +add_library(Display::Graphics ALIAS DisplayGraphics) + +target_link_libraries(DisplayGraphics + PRIVATE + Model::All + Helpers::All + Display::UI::Text + Display::UI::HostBlock ) -target_include_directories(Graphics +target_include_directories(DisplayGraphics PUBLIC ${INCLUDE_BASE_DIR} ) diff --git a/src/Display/Graphics/Framebuffer.cpp b/src/Display/Graphics/Framebuffer.cpp index b1ef01d..ae34e4c 100644 --- a/src/Display/Graphics/Framebuffer.cpp +++ b/src/Display/Graphics/Framebuffer.cpp @@ -9,95 +9,129 @@ namespace Display::Graphics { - Framebuffer::Framebuffer(const char *device) + Framebuffer::Framebuffer(const char *device, FramebufferRotation rotation) : rotation(rotation) { - fd_ = open(device, O_RDWR); - if (fd_ < 0) - throw std::runtime_error("Cannot open framebuffer"); + fd = open(device, O_RDWR); + if (fd < 0) + throw std::runtime_error("Failed to open framebuffer"); - fb_var_screeninfo vinfo; - fb_fix_screeninfo finfo; + fb_var_screeninfo vinfo{}; + fb_fix_screeninfo finfo{}; + ioctl(fd, FBIOGET_VSCREENINFO, &vinfo); + ioctl(fd, FBIOGET_FSCREENINFO, &finfo); + physical.width = vinfo.xres; + physical.height = vinfo.yres; + line_length = finfo.line_length; + size = finfo.smem_len; - ioctl(fd_, FBIOGET_VSCREENINFO, &vinfo); - ioctl(fd_, FBIOGET_FSCREENINFO, &finfo); + if (rotation == FramebufferRotation::R90 || rotation == FramebufferRotation::R270) + { + logical.width = physical.height; + logical.height = physical.width; + } + else + { + logical.width = physical.width; + logical.height = physical.height; + } - width_ = vinfo.xres; - height_ = vinfo.yres; - line_len_ = finfo.line_length; - size_ = finfo.smem_len; + buffer.resize(physical.width * physical.height, 0); - fb_ = static_cast(mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); - if (fb_ == MAP_FAILED) - throw std::runtime_error("mmap failed"); + fb = static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + if (fb == MAP_FAILED) + throw std::runtime_error("Failed to mmap framebuffer"); } Framebuffer::~Framebuffer() { - munmap(fb_, size_); - close(fd_); + munmap(fb, size); + close(fd); } void Framebuffer::clear(uint16_t color) { - for (int y = 0; y < height_; ++y) + for (int y = 0; y < logical.height; ++y) { - uint16_t *row = reinterpret_cast(fb_ + y * line_len_); - for (int x = 0; x < width_; ++x) + uint16_t *row = reinterpret_cast(fb + y * line_length); + for (int x = 0; x < logical.width; ++x) row[x] = color; } } - void Framebuffer::fillRect(int x, int y, int w, int h, uint16_t color) + void Framebuffer::present() { - for (int j = 0; j < h; ++j) + for (int y = 0; y < logical.height; ++y) { - int py = y + j; - if (py < 0 || py >= height_) - continue; - - uint16_t *row = reinterpret_cast(fb_ + py * line_len_); - for (int i = 0; i < w; ++i) + for (int x = 0; x < logical.width; ++x) { - int px = x + i; - if (px < 0 || px >= width_) - continue; + int fx = 0; + int fy = 0; - row[px] = color; + switch (rotation) + { + case FramebufferRotation::R0: + fx = x; + fy = y; + break; + + case FramebufferRotation::R90: + fx = physical.width - 1 - y; + fy = x; + break; + + case FramebufferRotation::R180: + fx = physical.width - 1 - x; + fy = physical.height - 1 - y; + break; + + case FramebufferRotation::R270: + fx = y; + fy = physical.height - 1 - x; + break; + } + + uint16_t *row = + reinterpret_cast(fb + fy * line_length); + row[fx] = buffer[y * logical.width + x]; } } } - void Framebuffer::drawRect(int x, int y, int w, int h, const Color &c) + void Framebuffer::fillRect(int x, int y, int w, int h, const Color &color) { - // верх - fillRect(x, y, w, 1, c); - // низ - fillRect(x, y + h - 1, w, 1, c); - // лево - fillRect(x, y, 1, h, c); - // право - fillRect(x + w - 1, y, 1, h, c); + for (int j = 0; j < h; ++j) + for (int i = 0; i < w; ++i) + setPixel(x + i, y + j, color); } - void Framebuffer::setPixel(int x, int y, const Color &c) + void Framebuffer::drawRect(int x, int y, int w, int h, const Color &color) { - if (x < 0 || x >= width_ || y < 0 || y >= height_) - return; + // верх + fillRect(x, y, w, 1, color); + // низ + fillRect(x, y + h - 1, w, 1, color); + // лево + fillRect(x, y, 1, h, color); + // право + fillRect(x + w - 1, y, 1, h, color); + } - uint16_t *p = reinterpret_cast(fb_ + y * line_len_) + x; - *p = ((c.r & 0xF8) << 8) | ((c.g & 0xFC) << 3) | (c.b >> 3); + void Framebuffer::setPixel(int x, int y, const Color &color) + { + if (x < 0 || x >= logical.width || y < 0 || y >= logical.height) + return; + buffer[y * logical.width + x] = rgb565(color); } Color Framebuffer::getPixel(int x, int y) const { - if (x < 0 || x >= width_ || y < 0 || y >= height_) + if (x < 0 || x >= logical.width || y < 0 || y >= logical.height) return {0, 0, 0, 255}; - - uint16_t p = *(reinterpret_cast(fb_ + y * line_len_) + x); + uint16_t p = buffer[y * logical.width + x]; Color c; - c.r = (p >> 8) & 0xF8; - c.g = (p >> 3) & 0xFC; - c.b = (p << 3) & 0xF8; + c.r = ((p >> 11) & 0x1F) << 3; + c.g = ((p >> 5) & 0x3F) << 2; + c.b = (p & 0x1F) << 3; c.a = 255; return c; } diff --git a/src/Display/Graphics/Framebuffer.h b/src/Display/Graphics/Framebuffer.h index 61b0c43..c28be44 100644 --- a/src/Display/Graphics/Framebuffer.h +++ b/src/Display/Graphics/Framebuffer.h @@ -1,31 +1,43 @@ #pragma once -#include "Display/Graphics/Color.h" +#include #include -#include + +#include "Display/Graphics/Color.h" +#include "Display/Graphics/FramebufferRotation.h" namespace Display::Graphics { class Framebuffer { public: - Framebuffer(const char *device); + Framebuffer(const char *device, FramebufferRotation rotation); ~Framebuffer(); void clear(uint16_t color); - void fillRect(int x, int y, int w, int h, uint16_t color); - void drawRect(int x, int y, int w, int h, const Color &c); - void setPixel(int x, int y, const Color &c); + void present(); + void fillRect(int x, int y, int w, int h, const Color &color); + void drawRect(int x, int y, int w, int h, const Color &color); + void setPixel(int x, int y, const Color &color); Color getPixel(int x, int y) const; - int width() const { return width_; } - int height() const { return height_; } + int getWidth() const { return logical.width; } + int getHeight() const { return logical.height; } + FramebufferRotation getRotation() const { return rotation; } private: - int fd_; - uint8_t *fb_; - size_t size_; - int width_; - int height_; - int line_len_; + struct Dimensions + { + int width; + int height; + }; + FramebufferRotation rotation = FramebufferRotation::R0; + std::vector buffer; + + Dimensions physical; + Dimensions logical; + int fd; + uint8_t *fb; + size_t size; + int line_length; }; } // namespace Display::Graphics diff --git a/src/Display/Graphics/FramebufferRotation.h b/src/Display/Graphics/FramebufferRotation.h new file mode 100644 index 0000000..b911c6f --- /dev/null +++ b/src/Display/Graphics/FramebufferRotation.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Display::Graphics +{ + enum class FramebufferRotation + { + R0, + R90, + R180, + R270 + }; +} // namespace Display::Graphics \ No newline at end of file diff --git a/src/Display/Graphics/Renderer.cpp b/src/Display/Graphics/Renderer.cpp index fd9ca38..4f93f29 100644 --- a/src/Display/Graphics/Renderer.cpp +++ b/src/Display/Graphics/Renderer.cpp @@ -4,38 +4,40 @@ namespace Display::Graphics { Renderer::Renderer(Framebuffer &framebuffer, Model::HostRegistry ®istry) : framebuffer(framebuffer), - registry(registry), - barMem(200, 10) + registry(registry) { } void Renderer::render() { - // очистка экрана framebuffer.clear(Color{0, 0, 0}); - int y = 10; // начальная вертикальная позиция - int lineHeight = 24; // высота строки (подбираем под шрифт) + int blocksPerRow = + (SCREEN_WIDTH + BLOCK_GAP) / (Display::UI::HostBlock::BLOCK_WIDTH + BLOCK_GAP); + if (blocksPerRow < 1) + blocksPerRow = 1; + + int index = 0; for (auto &[host, m] : registry.snapshot()) { - // рисуем фон строки - framebuffer.fillRect(10, y - 18, 300, lineHeight, Color{0, 64, 0}); + int col = index % blocksPerRow; + int row = index / blocksPerRow; - // формируем строку - std::string line = host + " " + std::to_string(int(m.cpu.loads.at(0))) + - "% " + std::to_string(int(m.memory.used / 1024 / 1024)) + - "/" + std::to_string(int(m.memory.total / 1024 / 1024)) + - " " + std::to_string(int(m.disks.at(0).used / 1024 / 1024)) + - "/" + std::to_string(int(m.disks.at(0).total / 1024 / 1024)); + int x = START_X + col * (Display::UI::HostBlock::BLOCK_WIDTH + BLOCK_GAP); + int y = START_Y + row * (Display::UI::HostBlock::BLOCK_HEIGHT + BLOCK_GAP); - // выводим текст на экран - textRenderer.drawText(framebuffer, 12, y, line, Color{255, 255, 255}); + hostblock.draw( + framebuffer, + textRenderer, + x, + y, + host, + m); - float mem = (float)m.memory.used / (float)m.memory.total; - barMem.draw(framebuffer, 10, y + 35, mem); - - y += lineHeight; // переход на следующую строку + ++index; } + + framebuffer.present(); } } // namespace Display::Graphics diff --git a/src/Display/Graphics/Renderer.h b/src/Display/Graphics/Renderer.h index db9a88b..129b912 100644 --- a/src/Display/Graphics/Renderer.h +++ b/src/Display/Graphics/Renderer.h @@ -2,12 +2,18 @@ #include "Display/Graphics/Framebuffer.h" #include "Model/HostRegistry.h" #include "Display/UI/Text/Renderer.h" -#include "Display/UI/Bar/Bar.h" +#include "Display/UI/HostBlock/HostBlock.h" #include "Display/Graphics/Color.h" #include namespace Display::Graphics { + static constexpr int START_X = 0; + static constexpr int START_Y = 10; + static constexpr int BLOCK_GAP = 4; + + static constexpr int SCREEN_WIDTH = 240; + class Renderer { public: @@ -22,6 +28,6 @@ namespace Display::Graphics Framebuffer &framebuffer; Model::HostRegistry ®istry; Display::UI::Text::Renderer textRenderer; - Display::UI::Bar::Bar barMem; + Display::UI::HostBlock::HostBlock hostblock; }; } // namespace Display::Graphics diff --git a/src/Display/UI/Bar/Bar.cpp b/src/Display/UI/Bar/Bar.cpp index f0c40e3..7f368d1 100644 --- a/src/Display/UI/Bar/Bar.cpp +++ b/src/Display/UI/Bar/Bar.cpp @@ -3,8 +3,8 @@ namespace Display::UI::Bar { - Bar::Bar(int width, int height, Style style) - : width(width), height(height), style(style) + Bar::Bar(int width, int height, Orientation orientation, Style style) + : width(width), height(height), orientation(orientation), style(style) { } @@ -12,20 +12,50 @@ namespace Display::UI::Bar { value = std::clamp(value, 0.0f, 1.0f); - // фон + // 1. Рисуем фон framebuffer.fillRect(x, y, width, height, style.background); - // заполнение - int fillWidth = static_cast(width * value); - if (fillWidth > 0) + // 2. Рисуем заполнение + Display::Graphics::Color fillColor = valueToColor(value); + + if (orientation == Orientation::Horizontal) { - framebuffer.fillRect(x, y, fillWidth, height, style.fill); + int fillWidth = static_cast(width * value); + framebuffer.fillRect(x, y, fillWidth, height, fillColor); + } + else // Vertical + { + int fillHeight = static_cast(height * value); + framebuffer.fillRect(x, y + (height - fillHeight), width, fillHeight, fillColor); } - // рамка + // 3. Рисуем рамку if (style.drawBorder) { + // можно добавить метод drawRect в Framebuffer framebuffer.drawRect(x, y, width, height, style.border); } } + + Display::Graphics::Color Bar::valueToColor(float value) + { + value = std::clamp(value, 0.0f, 1.0f); + if (value <= 0.5f) + { + float t = value / 0.5f; + return Display::Graphics::Color{ + static_cast(t * 255), // R: 0 → 255 + 180, // G: остаётся 180 + 0 // B + }; + } + else + { + float t = (value - 0.5f) / 0.5f; + return Display::Graphics::Color{ + 255, // R: остаётся 255 + static_cast(180 * (1.0f - t)), // G: 180 → 0 + 0}; + } + } } // namespace Display::UI::Bar diff --git a/src/Display/UI/Bar/Bar.h b/src/Display/UI/Bar/Bar.h index b1796f6..ee28ece 100644 --- a/src/Display/UI/Bar/Bar.h +++ b/src/Display/UI/Bar/Bar.h @@ -1,26 +1,15 @@ #pragma once #include "Display/Graphics/Framebuffer.h" #include "Display/Graphics/Color.h" +#include "Display/UI/Bar/Orientation.h" +#include "Display/UI/Bar/Style.h" namespace Display::UI::Bar { class Bar { public: - struct Style - { - Display::Graphics::Color background; - Display::Graphics::Color fill; - Display::Graphics::Color border; - bool drawBorder; - - Style() - : background{30, 30, 30}, fill{0, 180, 0}, border{80, 80, 80}, drawBorder(true) - { - } - }; - - Bar(int width, int height, Style style = {}); + Bar(int width, int height, Orientation orientation = Orientation::Horizontal, Style style = {}); void draw(Display::Graphics::Framebuffer &framebuffer, int x, int y, @@ -29,6 +18,9 @@ namespace Display::UI::Bar private: int width; int height; + Orientation orientation; Style style; + + Display::Graphics::Color valueToColor(float value); }; } // namespace Display::UI::Bar diff --git a/src/Display/UI/Bar/CMakeLists.txt b/src/Display/UI/Bar/CMakeLists.txt index a8cb030..de18b33 100644 --- a/src/Display/UI/Bar/CMakeLists.txt +++ b/src/Display/UI/Bar/CMakeLists.txt @@ -1,15 +1,17 @@ -message(STATUS " Configuring Bar") +message(STATUS "···Configuring Bar") -add_library(Bar +add_library(DisplayUIBar Bar.cpp ) -target_link_libraries(Bar - PRIVATE Graphics +add_library(Display::UI::Bar ALIAS DisplayUIBar) + +target_link_libraries(DisplayUIBar + PUBLIC + Display::Graphics ) -# include-root общий -target_include_directories(Bar +target_include_directories(DisplayUIBar PUBLIC ${INCLUDE_BASE_DIR} -) \ No newline at end of file +) diff --git a/src/Display/UI/Bar/Orientation.h b/src/Display/UI/Bar/Orientation.h new file mode 100644 index 0000000..19926a7 --- /dev/null +++ b/src/Display/UI/Bar/Orientation.h @@ -0,0 +1,10 @@ +#pragma once + +namespace Display::UI::Bar +{ + enum class Orientation + { + Horizontal, + Vertical + }; +} // namespace Display::UI::Bar \ No newline at end of file diff --git a/src/Display/UI/Bar/Style.h b/src/Display/UI/Bar/Style.h new file mode 100644 index 0000000..643065c --- /dev/null +++ b/src/Display/UI/Bar/Style.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Display/Graphics/Color.h" + +namespace Display::UI::Bar +{ + struct Style + { + Display::Graphics::Color background; + Display::Graphics::Color fill; + Display::Graphics::Color border; + bool drawBorder; + + Style( + Display::Graphics::Color background = Display::Graphics::Color{30, 30, 30}, + Display::Graphics::Color fill = Display::Graphics::Color{0, 180, 0}, + Display::Graphics::Color border = Display::Graphics::Color{80, 80, 80}, + bool drawBorder = true) + : background(background), + fill(fill), + border(border), + drawBorder(true) + { + } + }; +} // namespace Display::UI::Bar diff --git a/src/Display/UI/CMakeLists.txt b/src/Display/UI/CMakeLists.txt index e694d96..9b68d2b 100644 --- a/src/Display/UI/CMakeLists.txt +++ b/src/Display/UI/CMakeLists.txt @@ -1,21 +1,5 @@ -message(STATUS " Configuring UI") - -add_library(UI - INTERFACE -) +message(STATUS "··Configuring UI") add_subdirectory(Bar) add_subdirectory(Text) - -target_link_libraries(UI - INTERFACE Bar Text -) - -target_include_directories(UI - INTERFACE - ${INCLUDE_BASE_DIR} -) - - - - +add_subdirectory(HostBlock) \ No newline at end of file diff --git a/src/Display/UI/HostBlock/CMakeLists.txt b/src/Display/UI/HostBlock/CMakeLists.txt new file mode 100644 index 0000000..c72079b --- /dev/null +++ b/src/Display/UI/HostBlock/CMakeLists.txt @@ -0,0 +1,19 @@ +message(STATUS "···Configuring HostBlock") + +add_library(DisplayUIHostBlock + HostBlock.cpp +) + +add_library(Display::UI::HostBlock ALIAS DisplayUIHostBlock) + +target_link_libraries(DisplayUIHostBlock + PUBLIC + Display::Graphics + Display::UI::Text + Display::UI::Bar +) + +target_include_directories(DisplayUIHostBlock + PUBLIC + ${INCLUDE_BASE_DIR} +) diff --git a/src/Display/UI/HostBlock/HostBlock.cpp b/src/Display/UI/HostBlock/HostBlock.cpp new file mode 100644 index 0000000..e733a1f --- /dev/null +++ b/src/Display/UI/HostBlock/HostBlock.cpp @@ -0,0 +1,93 @@ +#include "Display/UI/HostBlock/HostBlock.h" + +#include + +#include "Display/UI/Bar/Orientation.h" +#include "Display/UI/Bar/Style.h" +#include "Display/Graphics/Color.h" + +namespace Display::UI::HostBlock +{ + HostBlock::HostBlock() + : cpuBar( + CPU_BAR_WIDTH, + CPU_BAR_HEIGHT, + Display::UI::Bar::Orientation::Vertical, + Display::UI::Bar::Style{Display::Graphics::Color{40, 40, 40}, Display::Graphics::Color{0, 180, 0}, Display::Graphics::Color{80, 80, 80}, true}), + memBar( + BLOCK_WIDTH - PADDING * 2, + MEM_BAR_HEIGHT, + Display::UI::Bar::Orientation::Horizontal, + Display::UI::Bar::Style{Display::Graphics::Color{40, 40, 40}, Display::Graphics::Color{0, 120, 200}, Display::Graphics::Color{80, 80, 80}, true}) + { + } + + void HostBlock::draw( + Graphics::Framebuffer &fb, + UI::Text::Renderer &text, + int x, int y, + const std::string &hostname, + const Metrics::Host &metrics) + { + // ===== Block background ===== + fb.fillRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, BG_COLOR); + fb.drawRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, BORDER_COLOR); + + int cursorY = y + PADDING; + + // ===== Header ===== + fb.fillRect( + x + PADDING, + cursorY, + BLOCK_WIDTH - PADDING * 2, + HEADER_HEIGHT, + HEADER_BG); + + text.drawText( + fb, + x + PADDING + 2, + cursorY + HEADER_HEIGHT - 3, + hostname, + TEXT_COLOR); + + cursorY += HEADER_HEIGHT + SECTION_GAP; + + // ===== CPU bars ===== + int cpuCount = std::min(metrics.cpu.coreLoads.size(), + CPU_MAX_PER_ROW * CPU_ROWS); + + for (int i = 0; i < cpuCount; ++i) + { + int row = i / CPU_MAX_PER_ROW; + int col = i % CPU_MAX_PER_ROW; + + int bx = x + PADDING + + col * (CPU_BAR_WIDTH + CPU_BAR_GAP); + + int by = cursorY + + row * (CPU_BAR_HEIGHT + CPU_BAR_GAP); + + float value = std::clamp(metrics.cpu.coreLoads[i] / 100.0f, 0.0f, 1.0f); + + cpuBar.draw(fb, bx, by, value); + } + + cursorY += CPU_ROWS * CPU_BAR_HEIGHT + + (CPU_ROWS - 1) * CPU_BAR_GAP + + SECTION_GAP; + + // ===== Memory bar ===== + float memValue = 0.0f; + if (metrics.memory.total > 0) + { + memValue = float(metrics.memory.used) / + float(metrics.memory.total); + } + + memBar.draw( + fb, + x + PADDING, + cursorY, + std::clamp(memValue, 0.0f, 1.0f)); + } +} diff --git a/src/Display/UI/HostBlock/HostBlock.h b/src/Display/UI/HostBlock/HostBlock.h new file mode 100644 index 0000000..5e6a1c1 --- /dev/null +++ b/src/Display/UI/HostBlock/HostBlock.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include "Display/Graphics/Framebuffer.h" +#include "Display/Graphics/Color.h" +#include "Display/UI/Text/Renderer.h" +#include "Display/UI/Bar/Bar.h" +#include "Metrics/Host.h" + +namespace Display::UI::HostBlock +{ + // ===== Layout ===== + constexpr int BLOCK_WIDTH = 116; + constexpr int BLOCK_HEIGHT = 116; + constexpr int PADDING = 4; + + constexpr int HEADER_HEIGHT = 16; + constexpr int SECTION_GAP = 4; + + // ===== CPU bars ===== + constexpr int CPU_BAR_WIDTH = 10; + constexpr int CPU_BAR_GAP = 4; + constexpr int CPU_BAR_HEIGHT = 36; + // constexpr int CPU_BAR_HEIGHT = 10; + constexpr int CPU_MAX_PER_ROW = 8; + constexpr int CPU_ROWS = 2; + + // ===== Memory bar ===== + constexpr int MEM_BAR_HEIGHT = 8; + + // ===== Colors ===== + const Display::Graphics::Color BG_COLOR{20, 20, 20}; + const Display::Graphics::Color BORDER_COLOR{60, 60, 60}; + const Display::Graphics::Color TEXT_COLOR{220, 220, 220}; + const Display::Graphics::Color HEADER_BG{30, 30, 30}; + + class HostBlock + { + public: + HostBlock(); + + static constexpr int width() { return 116; } + static constexpr int height() { return 116; } + + void draw( + Graphics::Framebuffer &fb, + UI::Text::Renderer &text, + int x, int y, + const std::string &hostname, + const Metrics::Host &metrics); + + private: + UI::Bar::Bar cpuBar; + UI::Bar::Bar memBar; + }; +} diff --git a/src/Display/UI/Text/BitmapFont.cpp b/src/Display/UI/Text/BitmapFont.cpp index 069908e..5ec89ab 100644 --- a/src/Display/UI/Text/BitmapFont.cpp +++ b/src/Display/UI/Text/BitmapFont.cpp @@ -16,7 +16,7 @@ namespace Display::UI::Text {0x1E, 0x21, 0x21, 0x1F, 0x01, 0x02, 0x1C} // 9 }; - void BitmapFont::drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, uint16_t color) + void BitmapFont::drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, const Display::Graphics::Color &color) { if (d < 0 || d > 9) return; diff --git a/src/Display/UI/Text/BitmapFont.h b/src/Display/UI/Text/BitmapFont.h index 99432f0..19dc31d 100644 --- a/src/Display/UI/Text/BitmapFont.h +++ b/src/Display/UI/Text/BitmapFont.h @@ -8,6 +8,6 @@ namespace Display::UI::Text class BitmapFont { public: - void drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, uint16_t color); + void drawDigit(Display::Graphics::Framebuffer &fb, int x, int y, int d, const Display::Graphics::Color &color); }; } // namespace Display::UI::Text diff --git a/src/Display/UI/Text/CMakeLists.txt b/src/Display/UI/Text/CMakeLists.txt index cb741cb..9f7c49b 100644 --- a/src/Display/UI/Text/CMakeLists.txt +++ b/src/Display/UI/Text/CMakeLists.txt @@ -1,21 +1,26 @@ -message(STATUS " Configuring Text") +message(STATUS "···Configuring Text") -add_library(Text +add_library(DisplayUIText BitmapFont.cpp FontFace.cpp GlyphCache.cpp Renderer.cpp ) + +add_library(Display::UI::Text ALIAS DisplayUIText) + find_package(Freetype REQUIRED) -target_link_libraries(Text - PRIVATE Graphics ${FREETYPE_LIBRARIES} +target_link_libraries(DisplayUIText + PRIVATE + Display::Graphics + ${FREETYPE_LIBRARIES} ) # include-root общий -target_include_directories(Text +target_include_directories(DisplayUIText PUBLIC ${INCLUDE_BASE_DIR} ${FREETYPE_INCLUDE_DIRS} -) \ No newline at end of file +) diff --git a/src/Helpers/CMakeLists.txt b/src/Helpers/CMakeLists.txt index baf9e65..73b4cd9 100644 --- a/src/Helpers/CMakeLists.txt +++ b/src/Helpers/CMakeLists.txt @@ -1,17 +1,16 @@ -message(STATUS " Configuring Model") +message(STATUS "·Configuring Model") -# Генерация Version.h из Version.h.in configure_file( Version.h.in ${CMAKE_CURRENT_BINARY_DIR}/Version.h ) -# Создаём "интерфейсную библиотеку", чтобы передавать include dirs add_library(Helpers INTERFACE) -# Добавляем include директории: и исходники, и сгенерированные файлы +add_library(Helpers::All ALIAS Helpers) + target_include_directories(Helpers INTERFACE - ${INCLUDE_BASE_DIR} # Color.h - ${CMAKE_CURRENT_BINARY_DIR} # Version.h + ${INCLUDE_BASE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt new file mode 100644 index 0000000..1b0d7df --- /dev/null +++ b/src/Main/CMakeLists.txt @@ -0,0 +1,8 @@ +message(STATUS "·Configuring Main") + +if (BUILD_SERVER) + add_subdirectory(Server) +endif() +if (BUILD_CLIENT) + add_subdirectory(Client) +endif() diff --git a/src/Main/Client/CMakeLists.txt b/src/Main/Client/CMakeLists.txt new file mode 100644 index 0000000..c7bc097 --- /dev/null +++ b/src/Main/Client/CMakeLists.txt @@ -0,0 +1,20 @@ +set(TARGET_NAME_CLIENT ${PROJECT_NAME}-Client) +message(STATUS "··Configuring ${TARGET_NAME_CLIENT}") + +# Основной исполняемый файл +add_executable(${TARGET_NAME_CLIENT} main.cpp) + +# Линкуем зависимости +target_link_libraries(${TARGET_NAME_CLIENT} + PRIVATE + Helpers::All + Metrics::All + Network::Client + Config::Client +) + +# Указываем корень include для dashboard +target_include_directories(${TARGET_NAME_CLIENT} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/Client.cpp b/src/Main/Client/main.cpp similarity index 50% rename from src/Client.cpp rename to src/Main/Client/main.cpp index edbfdda..b67e19b 100644 --- a/src/Client.cpp +++ b/src/Main/Client/main.cpp @@ -2,15 +2,18 @@ #include #include -#include "Network/Client.h" -#include "Network/Agent.h" +#include "Network/Client/Client.h" +#include "Network/Client/Agent.h" #include "Metrics/Collector.h" +#include "Config/Client/Config.h" +#include "Config/Client/NetworkConfig.h" int main(int argc, char **argv) { - Network::Client client("172.24.5.54", 5005); + Config::Client::Config config = Config::Client::Config::load("client.ini"); + Network::Client client(config.network.serverHost, config.network.serverPort); Metrics::Collector collector; - Network::Agent agent(std::move(client), std::move(collector), std::chrono::milliseconds(5000)); + Network::Agent agent(std::move(client), std::move(collector), config.network.intervalMs); agent.start(); diff --git a/src/Main/Server/CMakeLists.txt b/src/Main/Server/CMakeLists.txt new file mode 100644 index 0000000..fe4d32e --- /dev/null +++ b/src/Main/Server/CMakeLists.txt @@ -0,0 +1,24 @@ +set(TARGET_NAME_SERVER ${PROJECT_NAME}-Server) +message(STATUS "··Configuring ${TARGET_NAME_SERVER}") + +# Основной исполняемый файл +add_executable(${TARGET_NAME_SERVER} main.cpp) + +# Линкуем зависимости +target_link_libraries(${TARGET_NAME_SERVER} + PRIVATE + Config::Server + Display::Graphics + Display::UI::Bar + Display::UI::Text + Helpers::All + Metrics::All + Model::All + Network::Server +) + +# Указываем корень include для dashboard +target_include_directories(${TARGET_NAME_SERVER} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) diff --git a/src/Main/Server/main.cpp b/src/Main/Server/main.cpp new file mode 100644 index 0000000..9a5e925 --- /dev/null +++ b/src/Main/Server/main.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "Display/Graphics/Framebuffer.h" +#include "Model/HostRegistry.h" +#include "Network/Server/Server.h" +#include "Display/Graphics/Renderer.h" +#include "Config/Server/Config.h" +#include "Config/Server/DisplayConfig.h" +#include "Config/Server/NetworkConfig.h" + +int main(int argc, char **argv) +{ + Config::Server::Config config = Config::Server::Config::load("server.ini"); + Model::HostRegistry registry; + Display::Graphics::Framebuffer fb("/dev/fb1", Display::Graphics::FramebufferRotation::R270); + Display::Graphics::Renderer renderer(fb, registry); + Network::Server server(config.network.listenPort, registry); + + server.start(); + + while (true) + { + renderer.render(); + std::this_thread::sleep_for(config.display.refreshMs); + } + return 0; +} \ No newline at end of file diff --git a/src/Metrics/CMakeLists.txt b/src/Metrics/CMakeLists.txt index 6cc89f5..728430c 100644 --- a/src/Metrics/CMakeLists.txt +++ b/src/Metrics/CMakeLists.txt @@ -1,10 +1,12 @@ -message(STATUS " Configuring Metrics") +message(STATUS "·Configuring Metrics") add_library(Metrics Collector.cpp Host.cpp ) +add_library(Metrics::All ALIAS Metrics) + target_include_directories(Metrics PUBLIC ${INCLUDE_BASE_DIR} diff --git a/src/Metrics/Collector.cpp b/src/Metrics/Collector.cpp index 44bfef3..717385b 100644 --- a/src/Metrics/Collector.cpp +++ b/src/Metrics/Collector.cpp @@ -19,15 +19,16 @@ namespace Metrics auto curCpu = readCpuTimes(); - if (prevCpu_.empty()) - prevCpu_ = curCpu; + if (prevCpu.second.empty()) + prevCpu = curCpu; - host.cpu.loads.resize(curCpu.size()); + host.cpu.coreLoads.resize(curCpu.second.size()); - for (size_t i = 0; i < curCpu.size(); ++i) - host.cpu.loads[i] = cpuLoad(prevCpu_[i], curCpu[i]); + host.cpu.totalLoad = cpuLoad(prevCpu.first, curCpu.first); + for (size_t i = 0; i < curCpu.second.size(); ++i) + host.cpu.coreLoads[i] = cpuLoad(prevCpu.second[i], curCpu.second[i]); - prevCpu_ = curCpu; + prevCpu = curCpu; host.memory = readMemory(); host.disks.push_back(readDisk()); @@ -38,24 +39,31 @@ namespace Metrics return host; } - std::vector Collector::readCpuTimes() + std::pair> Collector::readCpuTimes() { std::ifstream f("/proc/stat"); - std::vector out; + std::pair> out; std::string line; while (std::getline(f, line)) { - if (line.rfind("cpu", 0) != 0 || line == "cpu") - continue; + if (!line.starts_with("cpu")) + break; std::istringstream ss(line); std::string cpu; - Collector::CpuTimes t; + CpuTimes t; ss >> cpu >> t.user >> t.nice >> t.system >> t.idle >> t.iowait >> t.irq >> t.softirq >> t.steal; - out.push_back(t); + if (cpu == "cpu") + { + out.first = t; + } + else + { + out.second.push_back(t); + } } return out; diff --git a/src/Metrics/Collector.h b/src/Metrics/Collector.h index c719602..cb2c8d5 100644 --- a/src/Metrics/Collector.h +++ b/src/Metrics/Collector.h @@ -19,10 +19,10 @@ namespace Metrics softirq = 0, steal = 0; }; - std::vector prevCpu_; + std::pair> prevCpu; - std::vector readCpuTimes(); - float cpuLoad(const Collector::CpuTimes &prev, const Collector::CpuTimes &cur); + std::pair> readCpuTimes(); + float cpuLoad(const CpuTimes &prev, const CpuTimes &cur); Memory readMemory(); Disk readDisk(const char *path = "/"); void readLoad(float &l1, float &l5, float &l15); diff --git a/src/Metrics/Cpu.h b/src/Metrics/Cpu.h index ec36bb2..9ba1d1c 100644 --- a/src/Metrics/Cpu.h +++ b/src/Metrics/Cpu.h @@ -5,6 +5,7 @@ namespace Metrics { struct Cpu { - std::vector loads; // длина = num_cores + float totalLoad; + std::vector coreLoads; // длина = num_cores }; } // namespace Metrics \ No newline at end of file diff --git a/src/Metrics/Host.cpp b/src/Metrics/Host.cpp index 8a66519..9b6f5bd 100644 --- a/src/Metrics/Host.cpp +++ b/src/Metrics/Host.cpp @@ -35,8 +35,12 @@ namespace Metrics buffer.insert(buffer.end(), hostname.begin(), hostname.end()); // CPU - buffer.push_back(static_cast(cpu.loads.size())); - for (float f : cpu.loads) + uint32_t totalLoadNet = floatToNetwork(cpu.totalLoad); + buffer.insert(buffer.end(), + reinterpret_cast(&totalLoadNet), + reinterpret_cast(&totalLoadNet) + sizeof(totalLoadNet)); + buffer.push_back(static_cast(cpu.coreLoads.size())); + for (float f : cpu.coreLoads) { uint32_t net = floatToNetwork(f); buffer.insert(buffer.end(), @@ -93,14 +97,19 @@ namespace Metrics pos += hostLen; // CPU + uint32_t totolLoad; + std::memcpy(&totolLoad, &buffer[pos], 4); + pos += 4; + m.cpu.totalLoad = networkToFloat(totolLoad); uint8_t numCpu = buffer[pos++]; if (pos + numCpu * 4 > buffer.size()) throw std::runtime_error("Buffer too small for CPU"); + for (int i = 0; i < numCpu; ++i) { uint32_t tmp; std::memcpy(&tmp, &buffer[pos], 4); - m.cpu.loads.push_back(networkToFloat(tmp)); + m.cpu.coreLoads.push_back(networkToFloat(tmp)); pos += 4; } diff --git a/src/Model/CMakeLists.txt b/src/Model/CMakeLists.txt index fa503bb..97bf4f6 100644 --- a/src/Model/CMakeLists.txt +++ b/src/Model/CMakeLists.txt @@ -1,11 +1,14 @@ -message(STATUS " Configuring Model") +message(STATUS "·Configuring Model") add_library(Model HostRegistry.cpp ) +add_library(Model::All ALIAS Model) + target_link_libraries(Model - PRIVATE Metrics + PRIVATE + Metrics::All ) target_include_directories(Model diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt index 69b3a64..e45c0d5 100644 --- a/src/Network/CMakeLists.txt +++ b/src/Network/CMakeLists.txt @@ -1,16 +1,8 @@ -message(STATUS " Configuring Network") +message(STATUS "·Configuring Network") -add_library(Network - Agent.cpp - Client.cpp - Server.cpp -) - -target_link_libraries(Network - PRIVATE Model Metrics -) - -target_include_directories(Network - PUBLIC - ${INCLUDE_BASE_DIR} -) \ No newline at end of file +if (BUILD_SERVER) + add_subdirectory(Server) +endif() +if (BUILD_CLIENT) + add_subdirectory(Client) +endif() diff --git a/src/Network/Agent.cpp b/src/Network/Client/Agent.cpp similarity index 54% rename from src/Network/Agent.cpp rename to src/Network/Client/Agent.cpp index eca558a..7169201 100644 --- a/src/Network/Agent.cpp +++ b/src/Network/Client/Agent.cpp @@ -1,4 +1,4 @@ -#include "Network/Agent.h" +#include "Network/Client/Agent.h" #include namespace Network @@ -8,7 +8,7 @@ namespace Network Client client, Metrics::Collector collector, std::chrono::milliseconds interval) - : client_(std::move(client)), collector_(std::move(collector)), interval_(interval) + : client(std::move(client)), collector(std::move(collector)), interval(interval) { } @@ -19,34 +19,34 @@ namespace Network void Agent::start() { - if (running_) + if (running) return; - running_ = true; - worker_ = std::thread(&Agent::run, this); + running = true; + worker = std::thread(&Agent::run, this); } void Agent::stop() { - running_ = false; - if (worker_.joinable()) - worker_.join(); + running = false; + if (worker.joinable()) + worker.join(); } void Agent::run() { - while (running_) + while (running) { try { - auto metrics = collector_.collect(); - client_.sendMetrics(metrics); + auto metrics = collector.collect(); + client.sendMetrics(metrics); } catch (const std::exception &e) { std::cerr << "Agent error: " << e.what() << std::endl; } - std::this_thread::sleep_for(interval_); + std::this_thread::sleep_for(interval); } } diff --git a/src/Network/Agent.h b/src/Network/Client/Agent.h similarity index 65% rename from src/Network/Agent.h rename to src/Network/Client/Agent.h index c6e462b..b6b54e5 100644 --- a/src/Network/Agent.h +++ b/src/Network/Client/Agent.h @@ -1,6 +1,6 @@ #pragma once -#include "Network/Client.h" +#include "Network/Client/Client.h" #include "Metrics/Collector.h" #include @@ -26,12 +26,12 @@ namespace Network private: void run(); - Client client_; - Metrics::Collector collector_; - std::chrono::milliseconds interval_; + Client client; + Metrics::Collector collector; + std::chrono::milliseconds interval; - std::thread worker_; - std::atomic running_{false}; + std::thread worker; + std::atomic running{false}; }; } \ No newline at end of file diff --git a/src/Network/Client/CMakeLists.txt b/src/Network/Client/CMakeLists.txt new file mode 100644 index 0000000..245b8bc --- /dev/null +++ b/src/Network/Client/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(Network.Client + Agent.cpp + Client.cpp +) + +add_library(Network::Client ALIAS Network.Client) + +target_link_libraries(Network.Client + PRIVATE + Metrics::All +) + +target_include_directories(Network.Client + PUBLIC + ${INCLUDE_BASE_DIR} +) diff --git a/src/Network/Client.cpp b/src/Network/Client/Client.cpp similarity index 52% rename from src/Network/Client.cpp rename to src/Network/Client/Client.cpp index 9dc947f..d42c035 100644 --- a/src/Network/Client.cpp +++ b/src/Network/Client/Client.cpp @@ -1,4 +1,4 @@ -#include "Network/Client.h" +#include "Network/Client/Client.h" #include #include #include @@ -9,18 +9,18 @@ namespace Network Client::Client(const std::string &host, uint16_t port) { - sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd_ < 0) + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { perror("socket"); throw std::runtime_error("Cannot create UDP socket"); } - memset(&serverAddr_, 0, sizeof(serverAddr_)); - serverAddr_.sin_family = AF_INET; - serverAddr_.sin_port = htons(port); + memset(&serverAddr, 0, sizeof(serverAddr)); + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(port); - if (inet_pton(AF_INET, host.c_str(), &serverAddr_.sin_addr) <= 0) + if (inet_pton(AF_INET, host.c_str(), &serverAddr.sin_addr) <= 0) { throw std::runtime_error("Invalid server IP"); } @@ -28,27 +28,27 @@ namespace Network Client::~Client() { - if (sockfd_ >= 0) - close(sockfd_); + if (sockfd >= 0) + close(sockfd); } Client::Client(Client &&other) noexcept { - sockfd_ = other.sockfd_; - serverAddr_ = other.serverAddr_; - other.sockfd_ = -1; + sockfd = other.sockfd; + serverAddr = other.serverAddr; + other.sockfd = -1; } Client &Client::operator=(Client &&other) noexcept { if (this != &other) { - if (sockfd_ >= 0) - close(sockfd_); + if (sockfd >= 0) + close(sockfd); - sockfd_ = other.sockfd_; - serverAddr_ = other.serverAddr_; - other.sockfd_ = -1; + sockfd = other.sockfd; + serverAddr = other.serverAddr; + other.sockfd = -1; } return *this; } @@ -56,8 +56,8 @@ namespace Network bool Client::sendMetrics(const Metrics::Host &metrics) { auto buffer = metrics.serialize(); - ssize_t sent = sendto(sockfd_, buffer.data(), buffer.size(), 0, - (struct sockaddr *)&serverAddr_, sizeof(serverAddr_)); + ssize_t sent = sendto(sockfd, buffer.data(), buffer.size(), 0, + (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (sent < 0) { perror("sendto"); diff --git a/src/Network/Client.h b/src/Network/Client/Client.h similarity index 89% rename from src/Network/Client.h rename to src/Network/Client/Client.h index 10ac047..d55e054 100644 --- a/src/Network/Client.h +++ b/src/Network/Client/Client.h @@ -22,8 +22,8 @@ namespace Network bool sendMetrics(const Metrics::Host &metrics); private: - int sockfd_; - struct sockaddr_in serverAddr_; + int sockfd; + struct sockaddr_in serverAddr; }; } // namespace Network \ No newline at end of file diff --git a/src/Network/Server/CMakeLists.txt b/src/Network/Server/CMakeLists.txt new file mode 100644 index 0000000..9964bb5 --- /dev/null +++ b/src/Network/Server/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library(Network.Server + Server.cpp +) + +add_library(Network::Server ALIAS Network.Server) + +target_link_libraries(Network.Server + PRIVATE + Model::All + Metrics::All +) + +target_include_directories(Network.Server + PUBLIC + ${INCLUDE_BASE_DIR} +) \ No newline at end of file diff --git a/src/Network/Server.cpp b/src/Network/Server/Server.cpp similarity index 98% rename from src/Network/Server.cpp rename to src/Network/Server/Server.cpp index 9350ac5..c11f005 100644 --- a/src/Network/Server.cpp +++ b/src/Network/Server/Server.cpp @@ -1,4 +1,4 @@ -#include "Network/Server.h" +#include "Network/Server/Server.h" #include #include #include diff --git a/src/Network/Server.h b/src/Network/Server/Server.h similarity index 100% rename from src/Network/Server.h rename to src/Network/Server/Server.h diff --git a/src/Server.cpp b/src/Server.cpp deleted file mode 100644 index 9118e89..0000000 --- a/src/Server.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include - -#include "Display/Graphics/Framebuffer.h" -#include "Model/HostRegistry.h" -#include "Network/Server.h" -#include "Display/Graphics/Renderer.h" - -int main(int argc, char **argv) -{ - Model::HostRegistry registry; - Display::Graphics::Framebuffer fb("/dev/fb1"); - Display::Graphics::Renderer renderer(fb, registry); - Network::Server server(5005, registry); - - server.start(); - - while (true) - { - renderer.render(); - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - return 0; -} \ No newline at end of file