diff --git a/src/config/server/Config.cpp b/src/config/server/Config.cpp index fd39955..cdd9c8e 100644 --- a/src/config/server/Config.cpp +++ b/src/config/server/Config.cpp @@ -74,8 +74,12 @@ namespace config::server cfg.style.hostblock.disks.font.size = std::stoi(ini.get("style", "hostblock.disks.font.size", "5")); cfg.style.hostblock.disks.font.padding = std::stoi(ini.get("style", "hostblock.disks.font.padding", "8")); - cfg.style.hostblock.networks.height = std::stoi(ini.get("style", "hostblock.networks.height", "11")); - cfg.style.hostblock.networks.gap = std::stoi(ini.get("style", "hostblock.networks.gap", "2")); + cfg.style.hostblock.networks.width = std::stoi(ini.get("style", "hostblock.networks.width", "8")); + cfg.style.hostblock.networks.height = std::stoi(ini.get("style", "hostblock.networks.height", "16")); + cfg.style.hostblock.networks.gap_h = std::stoi(ini.get("style", "hostblock.networks.gap_h", "4")); + cfg.style.hostblock.networks.gap_v = std::stoi(ini.get("style", "hostblock.networks.gap_v", "4")); + cfg.style.hostblock.networks.max_per_row = std::stoi(ini.get("style", "hostblock.networks.max_per_row", "8")); + cfg.style.hostblock.networks.rows = std::stoi(ini.get("style", "hostblock.networks.rows", "2")); cfg.style.hostblock.networks.font.name = ini.get("style", "hostblock.networks.font.name", "PixelFive-Regular"); cfg.style.hostblock.networks.font.size = std::stoi(ini.get("style", "hostblock.networks.font.size", "5")); cfg.style.hostblock.networks.font.padding = std::stoi(ini.get("style", "hostblock.networks.font.padding", "8")); diff --git a/src/config/server/StyleConfig.h b/src/config/server/StyleConfig.h index 7d26dd2..5f7ad43 100644 --- a/src/config/server/StyleConfig.h +++ b/src/config/server/StyleConfig.h @@ -63,7 +63,24 @@ namespace config::server int size = 5; int padding = 8; } font; - } memory, disks, networks; + } memory, disks; + + struct + { + int width = 8; + int height = 16; + int gap_h = 4; + int gap_v = 4; + int max_per_row = 4; + int rows = 2; + struct + { + std::string name = "PixelFive-Regular"; + int size = 5; + int padding = 8; + } font; + } networks; + } hostblock; }; } // namespace config::server diff --git a/src/display/ui/bar/Bar.cpp b/src/display/ui/bar/Bar.cpp index 5abd85d..9f84a60 100644 --- a/src/display/ui/bar/Bar.cpp +++ b/src/display/ui/bar/Bar.cpp @@ -1,4 +1,5 @@ #include "display/ui/bar/Bar.h" +#include "display/ui/theme/Theme.h" #include namespace display::ui::bar @@ -8,49 +9,90 @@ namespace display::ui::bar { } - void Bar::draw(display::graphics::Framebuffer &framebuffer, int x, int y, float value, float overlay_value, bool active) + void Bar::draw(display::graphics::Framebuffer &framebuffer, int x, int y, float value, float overlay_value, bool active, bool mirror) { value = std::clamp(value, 0.0f, 1.0f); + overlay_value = std::clamp(overlay_value, 0.0f, 1.0f); - // 1. Рисуем фон + // 1. Фон framebuffer.fillRect(x, y, width, height, style.background); - // 2. Рисуем заполнение - display::graphics::Color fillColor = active ? valueToColor(value) : display::graphics::Color{0x66, 0x66, 0x66}; + // 2. Цвет заполнения + display::graphics::Color fillColor = + active ? valueToColor(value) : display::ui::theme::bar::OFFLINE; + // 3. Основное заполнение if (orientation == Orientation::Horizontal) { int fillWidth = static_cast(width * value); - framebuffer.fillRect(x, y, fillWidth, height, fillColor); + + int fillX = mirror + ? x + (width - fillWidth) + : x; + + framebuffer.fillRect(fillX, y, fillWidth, height, fillColor); } else // Vertical { int fillHeight = static_cast(height * value); - framebuffer.fillRect(x, y + (height - fillHeight), width, fillHeight, fillColor); + + int fillY = mirror + ? y + : y + (height - fillHeight); + + framebuffer.fillRect(x, fillY, width, fillHeight, fillColor); } - // 3. Рисуем рамку + // 4. Рамка if (style.drawBorder) { - // можно добавить метод drawRect в Framebuffer framebuffer.drawRect(x, y, width, height, style.border); } - if (overlay_value) + // 5. Overlay (диагональные линии) + if (overlay_value > 0.0f) { if (orientation == Orientation::Horizontal) { int fillWidth = static_cast(width * value); int overlayWidth = static_cast(width * overlay_value); - int overlayX = x + (fillWidth - overlayWidth); - framebuffer.drawRectDiagonalOverlay(overlayX, y + 1, overlayWidth, height - 2, style.border, 3); + + int baseX = mirror + ? x + (width - fillWidth) + : x; + + int overlayX = mirror + ? baseX + : baseX + (fillWidth - overlayWidth); + + framebuffer.drawRectDiagonalOverlay( + overlayX, + y + 1, + overlayWidth, + height - 2, + style.border, + 3); } else // Vertical { int fillHeight = static_cast(height * value); int overlayHeight = static_cast(height * overlay_value); - int overlayY = y + (fillHeight - overlayHeight); - framebuffer.drawRectDiagonalOverlay(x + 1, overlayY + (height - overlayHeight), width - 2, overlayHeight, style.border, 3); + + int baseY = mirror + ? y + : y + (height - fillHeight); + + int overlayY = mirror + ? baseY + (fillHeight - overlayHeight) + : baseY; + + framebuffer.drawRectDiagonalOverlay( + x + 1, + overlayY, + width - 2, + overlayHeight, + style.border, + 3); } } } diff --git a/src/display/ui/bar/Bar.h b/src/display/ui/bar/Bar.h index 454fb44..54dd7dd 100644 --- a/src/display/ui/bar/Bar.h +++ b/src/display/ui/bar/Bar.h @@ -13,7 +13,7 @@ namespace display::ui::bar void draw(display::graphics::Framebuffer &framebuffer, int x, int y, - float value, float overlay_value = 0.0, bool active = true); // 0.0 .. 1.0 + float value, float overlay_value = 0.0, bool active = true, bool mirror = false); // 0.0 .. 1.0 private: int width; diff --git a/src/display/ui/hostblock/HostBlock.cpp b/src/display/ui/hostblock/HostBlock.cpp index 028167c..89addb0 100644 --- a/src/display/ui/hostblock/HostBlock.cpp +++ b/src/display/ui/hostblock/HostBlock.cpp @@ -27,7 +27,12 @@ namespace display::ui::hostblock config.hostblock.width - config.hostblock.padding * 2, config.hostblock.memory.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}) + display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 120, 200}, display::graphics::Color{80, 80, 80}, true}), + netBar( + config.hostblock.networks.width, + config.hostblock.networks.height, + display::ui::bar::Orientation::Horizontal, + display::ui::bar::Style{display::graphics::Color{40, 40, 40}, display::graphics::Color{0, 180, 0}, display::graphics::Color{80, 80, 80}, true}) { } @@ -60,7 +65,7 @@ namespace display::ui::hostblock x + config.hostblock.padding + 2, cursorY + config.hostblock.header.height - 1, hostname, - display::ui::theme::text::TEXT, + online ? display::ui::theme::text::TEXT : display::ui::theme::text::OFFLINE, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.header.font.name, config.hostblock.header.font.size}); @@ -110,7 +115,7 @@ namespace display::ui::hostblock x + config.hostblock.memory.font.padding, cursorY + 8, "MEM: " + display::ui::text::formatFloat(static_cast(metrics.memory.memory.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.memory.memory.total) / 1073741824), - display::ui::theme::text::TEXT, + online ? display::ui::theme::text::TEXT : display::ui::theme::text::OFFLINE, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size}); @@ -130,7 +135,7 @@ namespace display::ui::hostblock x + config.hostblock.memory.font.padding, cursorY + 8, "SWP: " + display::ui::text::formatFloat(static_cast(metrics.memory.swap.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.memory.swap.total) / 1073741824), - display::ui::theme::text::TEXT, + online ? display::ui::theme::text::TEXT : display::ui::theme::text::OFFLINE, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size}); @@ -152,79 +157,43 @@ namespace display::ui::hostblock x + config.hostblock.disks.font.padding, by + 8, "D/" + metrics.disks[i].name + ": " + display::ui::text::formatFloat(static_cast(metrics.disks[i].metrics.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast(metrics.disks[i].metrics.total) / 1073741824), - display::ui::theme::text::TEXT, + online ? display::ui::theme::text::TEXT : display::ui::theme::text::OFFLINE, display::ui::theme::text::OUTLINE, display::ui::text::Font{config.hostblock.disks.font.name, config.hostblock.disks.font.size}); } - cursorY += diskCount * (config.hostblock.disks.height + config.hostblock.disks.gap); + cursorY += diskCount * (config.hostblock.disks.height + config.hostblock.disks.gap) + config.hostblock.gap; // ===== Network bar ===== - for (const auto &iface : metrics.networks) + int networksCount = std::min(metrics.networks.size(), + config.hostblock.networks.max_per_row * config.hostblock.networks.rows); + + for (int i = 0; i < networksCount; ++i) { - int by = cursorY; + const metrics::Network &network = metrics.networks[i]; - float rxNorm = 0.0f; - float txNorm = 0.0f; + int row = i / config.hostblock.networks.max_per_row; + int col = i % config.hostblock.networks.max_per_row; - if (iface.online) - { - if (iface.rxMaxBps > 0) - rxNorm = std::clamp(static_cast(iface.rxBps / iface.rxMaxBps), 0.0f, 1.0f); - if (iface.txMaxBps > 0) - txNorm = std::clamp(static_cast(iface.txBps / iface.txMaxBps), 0.0f, 1.0f); - } + int bx = x + config.hostblock.padding + + col * (config.hostblock.networks.width * 2 + config.hostblock.networks.gap_h); - // background bar - memBar.draw( - fb, - x + config.hostblock.padding, - by, - 0.0f, - 0.0f, - iface.online && online); + int by = cursorY + + row * (config.hostblock.networks.height + config.hostblock.networks.gap_v); - int barX = x + config.hostblock.padding; - int barW = config.hostblock.width - config.hostblock.padding * 2; - int barH = config.hostblock.networks.height; + float value_rx = std::clamp(network.rxBps / network.rxMaxBps, 0.0f, 1.0f); + float value_tx = std::clamp(network.txBps / network.txMaxBps, 0.0f, 1.0f); - // RX overlay - fb.fillRect( - barX, - by, - static_cast(barW * rxNorm), - barH, - {0, 180, 0}); + netBar.draw(fb, bx, by, value_rx, 0, network.online && online, true); + netBar.draw(fb, bx + config.hostblock.networks.width, by, value_tx, 0, network.online && online); - // TX overlay - fb.fillRect( - barX, - by, - static_cast(barW * txNorm), - barH, - {200, 200, 0}); - - // border - fb.drawRect(barX, by, barW, barH, display::ui::theme::hostblock::BORDER); - - // interface name + speeds - std::string label = - iface.name + " " + - display::ui::text::formatSpeed(iface.rxBps) + " ↓ " + - display::ui::text::formatSpeed(iface.txBps) + " ↑"; - - text.drawTextOutlined( - fb, - barX + config.hostblock.networks.font.padding, - by + barH - 1, - label, - display::ui::theme::text::TEXT, - display::ui::theme::text::OUTLINE, - display::ui::text::Font{ - config.hostblock.networks.font.name, - config.hostblock.networks.font.size}); - - cursorY += config.hostblock.networks.height + config.hostblock.networks.gap; + text.drawTextOutlined(fb, + bx + config.hostblock.networks.font.padding, + by + 8, + display::ui::text::formatSpeed(network.rxBps) + " " + network.name, + network.online && online ? display::ui::theme::text::TEXT : display::ui::theme::text::OFFLINE, + display::ui::theme::text::OUTLINE, + display::ui::text::Font{config.hostblock.networks.font.name, config.hostblock.networks.font.size}); } } } diff --git a/src/display/ui/hostblock/HostBlock.h b/src/display/ui/hostblock/HostBlock.h index 3ba9f05..b624aa6 100644 --- a/src/display/ui/hostblock/HostBlock.h +++ b/src/display/ui/hostblock/HostBlock.h @@ -29,5 +29,6 @@ namespace display::ui::hostblock display::ui::bar::Bar cpuBar; display::ui::bar::Bar cpuTempBar; display::ui::bar::Bar memBar; + display::ui::bar::Bar netBar; }; } diff --git a/src/display/ui/text/Helpers.cpp b/src/display/ui/text/Helpers.cpp index d1e9881..3513ba3 100644 --- a/src/display/ui/text/Helpers.cpp +++ b/src/display/ui/text/Helpers.cpp @@ -22,12 +22,19 @@ namespace display::ui::text { char buf[32]; + /* if (bps < 1024) + snprintf(buf, sizeof(buf), "%.0f B/s", bps); + else if (bps < 1024 * 1024) + snprintf(buf, sizeof(buf), "%.1f kB/s", bps / 1024.0); + else + snprintf(buf, sizeof(buf), "%.1f MB/s", bps / (1024.0 * 1024.0)); + */ if (bps < 1024) - snprintf(buf, sizeof(buf), "%.0f B/s", bps); + snprintf(buf, sizeof(buf), "%.0f", bps); else if (bps < 1024 * 1024) - snprintf(buf, sizeof(buf), "%.1f kB/s", bps / 1024.0); + snprintf(buf, sizeof(buf), "%.1fk", bps / 1024.0); else - snprintf(buf, sizeof(buf), "%.1f MB/s", bps / (1024.0 * 1024.0)); + snprintf(buf, sizeof(buf), "%.1fM", bps / (1024.0 * 1024.0)); return buf; } diff --git a/src/display/ui/theme/Theme.h b/src/display/ui/theme/Theme.h index d5af862..9aeef31 100644 --- a/src/display/ui/theme/Theme.h +++ b/src/display/ui/theme/Theme.h @@ -8,6 +8,7 @@ namespace display::ui::theme { const display::graphics::Color TEXT{220, 220, 220}; const display::graphics::Color OUTLINE{20, 20, 20}; + const display::graphics::Color OFFLINE{100, 100, 100}; } // namespace text namespace header @@ -31,5 +32,6 @@ namespace display::ui::theme const display::graphics::Color BACKGROUND{30, 30, 30}; const display::graphics::Color FILL{0, 180, 0}; const display::graphics::Color BORDER{80, 80, 80}; + const display::graphics::Color OFFLINE{60, 60, 60}; } // namespace bar } // namespace display::ui::theme diff --git a/src/metrics/Collector.cpp b/src/metrics/Collector.cpp index 813ee8b..1a8dc72 100644 --- a/src/metrics/Collector.cpp +++ b/src/metrics/Collector.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -329,13 +331,21 @@ namespace metrics bool Collector::isInterfaceOnline(const std::string &iface) { - std::ifstream f("/sys/class/net/" + iface + "/operstate"); - if (!f) + int fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) return false; - std::string state; - f >> state; - return state == "up"; + struct ifreq ifr{}; + std::snprintf(ifr.ifr_name, IFNAMSIZ, "%s", iface.c_str()); + + bool online = false; + if (ioctl(fd, SIOCGIFFLAGS, &ifr) == 0) + { + online = (ifr.ifr_flags & IFF_UP) != 0; + } + + close(fd); + return online; } bool Collector::readInterfaceCounters(const std::string &iface, uint64_t &rx, uint64_t &tx)