Add network
This commit is contained in:
@@ -8,5 +8,6 @@ namespace config::client
|
||||
struct CollectorConfig
|
||||
{
|
||||
std::vector<std::vector<std::string>> disks;
|
||||
std::vector<std::vector<std::string>> networks;
|
||||
};
|
||||
} // namespace config::client
|
||||
|
||||
@@ -19,6 +19,14 @@ namespace config::client
|
||||
for (const std::string &disk : disks_vec)
|
||||
cfg.collector.disks.push_back(config::splitList(disk, ':'));
|
||||
|
||||
const std::string networks_line = ini.get("collector", "networks", "");
|
||||
if (!networks_line.empty())
|
||||
{
|
||||
const std::vector<std::string> networks_vec = config::splitList(networks_line, ',');
|
||||
for (const std::string &network : networks_vec)
|
||||
cfg.collector.networks.push_back(config::splitList(network, ':'));
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
} // namespace config::client
|
||||
|
||||
@@ -35,6 +35,51 @@ namespace config::server
|
||||
cfg.barStyle.fill = parseColor(
|
||||
ini.get("bar", "fill", "0,180,0"));
|
||||
|
||||
cfg.style.gap = std::stoi(ini.get("style", "gap", "2"));
|
||||
|
||||
cfg.style.header.width = std::stoi(ini.get("style", "header.width", "240"));
|
||||
cfg.style.header.height = std::stoi(ini.get("style", "header.height", "16"));
|
||||
cfg.style.header.padding = std::stoi(ini.get("style", "header.padding", "4"));
|
||||
cfg.style.header.font.name = ini.get("style", "header.font.name", "Pixel10");
|
||||
cfg.style.header.font.size = std::stoi(ini.get("style", "header.font.size", "14"));
|
||||
|
||||
cfg.style.hostblock.width = std::stoi(ini.get("style", "hostblock.width", "118"));
|
||||
cfg.style.hostblock.height = std::stoi(ini.get("style", "hostblock.height", "150"));
|
||||
cfg.style.hostblock.padding = std::stoi(ini.get("style", "hostblock.padding", "5"));
|
||||
cfg.style.hostblock.gap = std::stoi(ini.get("style", "hostblock.gap", "4"));
|
||||
|
||||
cfg.style.hostblock.header.height = std::stoi(ini.get("style", "hostblock.header.height", "7"));
|
||||
cfg.style.hostblock.header.font.name = ini.get("style", "hostblock.header.font.name", "PixelFive-Regular");
|
||||
cfg.style.hostblock.header.font.size = std::stoi(ini.get("style", "hostblock.header.font.size", "5"));
|
||||
|
||||
cfg.style.hostblock.cpu.width = std::stoi(ini.get("style", "hostblock.cpu.width", "8"));
|
||||
cfg.style.hostblock.cpu.height = std::stoi(ini.get("style", "hostblock.cpu.height", "16"));
|
||||
cfg.style.hostblock.cpu.gap_h = std::stoi(ini.get("style", "hostblock.cpu.gap_h", "4"));
|
||||
cfg.style.hostblock.cpu.gap_v = std::stoi(ini.get("style", "hostblock.cpu.gap_v", "4"));
|
||||
cfg.style.hostblock.cpu.max_per_row = std::stoi(ini.get("style", "hostblock.cpu.max_per_row", "8"));
|
||||
cfg.style.hostblock.cpu.rows = std::stoi(ini.get("style", "hostblock.cpu.rows", "2"));
|
||||
cfg.style.hostblock.cpu.temperature.width = std::stoi(ini.get("style", "hostblock.cpu.temperature.width", "3"));
|
||||
cfg.style.hostblock.cpu.temperature.min = std::stoi(ini.get("style", "hostblock.cpu.temperature.min", "20"));
|
||||
cfg.style.hostblock.cpu.temperature.max = std::stoi(ini.get("style", "hostblock.cpu.temperature.max", "100"));
|
||||
|
||||
cfg.style.hostblock.memory.height = std::stoi(ini.get("style", "hostblock.memory.height", "11"));
|
||||
cfg.style.hostblock.memory.gap = std::stoi(ini.get("style", "hostblock.memory.gap", "2"));
|
||||
cfg.style.hostblock.memory.font.name = ini.get("style", "hostblock.memory.font.name", "PixelFive-Regular");
|
||||
cfg.style.hostblock.memory.font.size = std::stoi(ini.get("style", "hostblock.memory.font.size", "5"));
|
||||
cfg.style.hostblock.memory.font.padding = std::stoi(ini.get("style", "hostblock.memory.font.padding", "8"));
|
||||
|
||||
cfg.style.hostblock.disks.height = std::stoi(ini.get("style", "hostblock.disks.height", "11"));
|
||||
cfg.style.hostblock.disks.gap = std::stoi(ini.get("style", "hostblock.disks.gap", "2"));
|
||||
cfg.style.hostblock.disks.font.name = ini.get("style", "hostblock.disks.font.name", "PixelFive-Regular");
|
||||
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.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"));
|
||||
|
||||
return cfg;
|
||||
}
|
||||
} // namespace config::server
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "config/server/DisplayConfig.h"
|
||||
#include "config/server/NetworkConfig.h"
|
||||
#include "config/server/TextStyleConfig.h"
|
||||
#include "config/server/StyleConfig.h"
|
||||
|
||||
namespace config::server
|
||||
{
|
||||
@@ -17,5 +18,6 @@ namespace config::server
|
||||
NetworkConfig network;
|
||||
TextStyleConfig textStyle;
|
||||
BarStyleConfig barStyle;
|
||||
StyleConfig style;
|
||||
};
|
||||
} // namespace config::server
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace config::server
|
||||
{
|
||||
struct StyleConfig
|
||||
{
|
||||
int gap = 2;
|
||||
|
||||
struct
|
||||
{
|
||||
int width = 240;
|
||||
int height = 16;
|
||||
int padding = 4;
|
||||
struct
|
||||
{
|
||||
std::string name = "Pixel10";
|
||||
int size = 14;
|
||||
} font;
|
||||
} header;
|
||||
|
||||
struct
|
||||
{
|
||||
int width = 118;
|
||||
int height = 150;
|
||||
int padding = 5;
|
||||
int gap = 4;
|
||||
|
||||
struct
|
||||
{
|
||||
int height = 7;
|
||||
struct
|
||||
{
|
||||
std::string name = "PixelFive-Regular";
|
||||
int size = 5;
|
||||
} font;
|
||||
} header;
|
||||
|
||||
struct
|
||||
{
|
||||
int width = 8;
|
||||
int height = 16;
|
||||
int gap_h = 4;
|
||||
int gap_v = 4;
|
||||
int max_per_row = 8;
|
||||
int rows = 2;
|
||||
struct
|
||||
{
|
||||
int width = 3;
|
||||
int min = 20;
|
||||
int max = 100;
|
||||
} temperature;
|
||||
} cpu;
|
||||
|
||||
struct
|
||||
{
|
||||
int height = 11;
|
||||
int gap = 2;
|
||||
struct
|
||||
{
|
||||
std::string name = "PixelFive-Regular";
|
||||
int size = 5;
|
||||
int padding = 8;
|
||||
} font;
|
||||
} memory, disks, networks;
|
||||
} hostblock;
|
||||
};
|
||||
} // namespace config::server
|
||||
@@ -2,9 +2,13 @@
|
||||
|
||||
namespace display::graphics
|
||||
{
|
||||
Renderer::Renderer(Framebuffer &framebuffer, model::HostRegistry ®istry)
|
||||
Renderer::Renderer(Framebuffer &framebuffer, model::HostRegistry ®istry, config::server::StyleConfig &config)
|
||||
: framebuffer(framebuffer),
|
||||
registry(registry)
|
||||
registry(registry),
|
||||
config(config),
|
||||
textRenderer(std::make_unique<display::ui::text::Renderer>()),
|
||||
header(std::make_unique<display::ui::header::Header>(config)),
|
||||
hostblock(std::make_unique<display::ui::hostblock::HostBlock>(config))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -12,10 +16,10 @@ namespace display::graphics
|
||||
{
|
||||
framebuffer.clear(Color{0, 0, 0});
|
||||
|
||||
header.draw(framebuffer, textRenderer, START_X, START_Y);
|
||||
header->draw(framebuffer, *textRenderer, START_X, START_Y);
|
||||
|
||||
int blocksPerRow =
|
||||
(SCREEN_WIDTH + BLOCK_GAP) / (display::ui::hostblock::BLOCK_WIDTH + BLOCK_GAP);
|
||||
(SCREEN_WIDTH + config.gap) / (config.hostblock.width + config.gap);
|
||||
if (blocksPerRow < 1)
|
||||
blocksPerRow = 1;
|
||||
|
||||
@@ -26,12 +30,12 @@ namespace display::graphics
|
||||
int col = index % blocksPerRow;
|
||||
int row = index / blocksPerRow;
|
||||
|
||||
int x = START_X + col * (display::ui::hostblock::BLOCK_WIDTH + BLOCK_GAP);
|
||||
int y = START_Y + header.height() + BLOCK_GAP + row * (display::ui::hostblock::BLOCK_HEIGHT + BLOCK_GAP);
|
||||
int x = START_X + col * (config.hostblock.width + config.gap);
|
||||
int y = START_Y + config.header.height + config.gap + row * (config.hostblock.height + config.gap);
|
||||
|
||||
hostblock.draw(
|
||||
hostblock->draw(
|
||||
framebuffer,
|
||||
textRenderer,
|
||||
*textRenderer,
|
||||
x,
|
||||
y,
|
||||
host,
|
||||
|
||||
@@ -5,20 +5,21 @@
|
||||
#include "display/ui/header/Header.h"
|
||||
#include "display/ui/hostblock/HostBlock.h"
|
||||
#include "display/graphics/Color.h"
|
||||
#include "config/server/StyleConfig.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace display::graphics
|
||||
{
|
||||
static constexpr int START_X = 0; // 0
|
||||
static constexpr int START_Y = 0; // 20
|
||||
static constexpr int BLOCK_GAP = 4; // 4
|
||||
|
||||
static constexpr int SCREEN_WIDTH = 240;
|
||||
|
||||
class Renderer
|
||||
{
|
||||
public:
|
||||
Renderer(Framebuffer &framebuffer, model::HostRegistry ®istry);
|
||||
Renderer(Framebuffer &framebuffer, model::HostRegistry ®istry, config::server::StyleConfig &config);
|
||||
|
||||
Renderer(const Renderer &) = delete;
|
||||
Renderer &operator=(const Renderer &) = delete;
|
||||
@@ -28,8 +29,9 @@ namespace display::graphics
|
||||
private:
|
||||
Framebuffer &framebuffer;
|
||||
model::HostRegistry ®istry;
|
||||
display::ui::text::Renderer textRenderer;
|
||||
display::ui::header::Header header;
|
||||
display::ui::hostblock::HostBlock hostblock;
|
||||
config::server::StyleConfig &config;
|
||||
std::unique_ptr<display::ui::text::Renderer> textRenderer;
|
||||
std::unique_ptr<display::ui::header::Header> header;
|
||||
std::unique_ptr<display::ui::hostblock::HostBlock> hostblock;
|
||||
};
|
||||
} // namespace display::graphics
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace display::ui::bar
|
||||
{
|
||||
}
|
||||
|
||||
void Bar::draw(display::graphics::Framebuffer &framebuffer, int x, int y, float value, float overlay_value)
|
||||
void Bar::draw(display::graphics::Framebuffer &framebuffer, int x, int y, float value, float overlay_value, bool active)
|
||||
{
|
||||
value = std::clamp(value, 0.0f, 1.0f);
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace display::ui::bar
|
||||
framebuffer.fillRect(x, y, width, height, style.background);
|
||||
|
||||
// 2. Рисуем заполнение
|
||||
display::graphics::Color fillColor = valueToColor(value);
|
||||
display::graphics::Color fillColor = active ? valueToColor(value) : display::graphics::Color{0x66, 0x66, 0x66};
|
||||
|
||||
if (orientation == Orientation::Horizontal)
|
||||
{
|
||||
|
||||
@@ -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); // 0.0 .. 1.0
|
||||
float value, float overlay_value = 0.0, bool active = true); // 0.0 .. 1.0
|
||||
|
||||
private:
|
||||
int width;
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
namespace display::ui::header
|
||||
{
|
||||
Header::Header()
|
||||
Header::Header(config::server::StyleConfig &config)
|
||||
: config(config)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -23,19 +24,19 @@ namespace display::ui::header
|
||||
int x, int y)
|
||||
{
|
||||
// ===== Block background =====
|
||||
fb.fillRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, display::ui::theme::header::BACKGROUND);
|
||||
fb.drawRect(x, y, HEADER_WIDTH, HEADER_HEIGHT, display::ui::theme::header::BORDER);
|
||||
fb.fillRect(x, y, config.header.width, config.header.height, display::ui::theme::header::BACKGROUND);
|
||||
fb.drawRect(x, y, config.header.width, config.header.height, display::ui::theme::header::BORDER);
|
||||
|
||||
int cursorY = y + PADDING;
|
||||
int cursorY = y + config.header.padding;
|
||||
|
||||
text.drawTextOutlined(
|
||||
fb,
|
||||
x + PADDING + 120 - 2,
|
||||
x + config.header.padding + 120 - 2,
|
||||
cursorY + 8,
|
||||
getCurrentDateTime(),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE});
|
||||
display::ui::text::Font{config.header.font.name, config.header.font.size});
|
||||
}
|
||||
|
||||
std::string Header::getCurrentDateTime()
|
||||
|
||||
@@ -6,23 +6,14 @@
|
||||
#include "display/graphics/Framebuffer.h"
|
||||
#include "display/graphics/Color.h"
|
||||
#include "display/ui/text/Renderer.h"
|
||||
#include "config/server/StyleConfig.h"
|
||||
|
||||
namespace display::ui::header
|
||||
{
|
||||
// ===== Layout =====
|
||||
constexpr int HEADER_WIDTH = 240; // 116
|
||||
constexpr int HEADER_HEIGHT = 16; // 116
|
||||
constexpr int PADDING = 4; // 4
|
||||
constexpr std::string_view HEADER_FONT_NAME = "Pixel10";
|
||||
constexpr int HEADER_FONT_SIZE = 14; // 12
|
||||
|
||||
class Header
|
||||
{
|
||||
public:
|
||||
Header();
|
||||
|
||||
static constexpr int width() { return HEADER_WIDTH; }
|
||||
static constexpr int height() { return HEADER_HEIGHT; }
|
||||
Header(config::server::StyleConfig &config);
|
||||
|
||||
void draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
@@ -31,6 +22,7 @@ namespace display::ui::header
|
||||
|
||||
private:
|
||||
std::string getCurrentDateTime();
|
||||
config::server::StyleConfig &config;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -11,20 +11,21 @@
|
||||
|
||||
namespace display::ui::hostblock
|
||||
{
|
||||
HostBlock::HostBlock()
|
||||
: cpuBar(
|
||||
CPU_BAR_WIDTH,
|
||||
CPU_BAR_HEIGHT,
|
||||
HostBlock::HostBlock(config::server::StyleConfig &config)
|
||||
: config(config),
|
||||
cpuBar(
|
||||
config.hostblock.cpu.width,
|
||||
config.hostblock.cpu.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}),
|
||||
cpuTempBar(
|
||||
CPU_BAR_TEMP_WIDTH,
|
||||
CPU_BAR_HEIGHT,
|
||||
config.hostblock.cpu.temperature.width,
|
||||
config.hostblock.cpu.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,
|
||||
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})
|
||||
|
||||
@@ -38,60 +39,60 @@ namespace display::ui::hostblock
|
||||
const std::string &hostname,
|
||||
const metrics::Host &metrics)
|
||||
{
|
||||
// ===== Block background =====
|
||||
fb.fillRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, display::ui::theme::hostblock::BACKGROUND);
|
||||
fb.drawRect(x, y, BLOCK_WIDTH, BLOCK_HEIGHT, display::ui::theme::hostblock::BORDER);
|
||||
bool online = std::chrono::high_resolution_clock::now() - metrics.packetTimepoint < std::chrono::seconds(5);
|
||||
|
||||
int cursorY = y + PADDING;
|
||||
// ===== Block background =====
|
||||
fb.fillRect(x, y, config.hostblock.width, config.hostblock.height, display::ui::theme::hostblock::BACKGROUND);
|
||||
fb.drawRect(x, y, config.hostblock.width, config.hostblock.height, display::ui::theme::hostblock::BORDER);
|
||||
|
||||
int cursorY = y + config.hostblock.padding;
|
||||
|
||||
// ===== Header =====
|
||||
fb.fillRect(
|
||||
x + PADDING,
|
||||
x + config.hostblock.padding,
|
||||
cursorY,
|
||||
BLOCK_WIDTH - PADDING * 2,
|
||||
HEADER_HEIGHT,
|
||||
std::chrono::high_resolution_clock::now() - metrics.packetTimepoint < std::chrono::seconds(5) ? display::ui::theme::hostblock::HEADER : display::graphics::Red());
|
||||
config.hostblock.width - config.hostblock.padding * 2,
|
||||
config.hostblock.header.height,
|
||||
online ? display::ui::theme::hostblock::HEADER : display::graphics::Red());
|
||||
|
||||
text.drawTextOutlined(
|
||||
fb,
|
||||
x + PADDING + 2,
|
||||
cursorY + HEADER_HEIGHT - 1,
|
||||
x + config.hostblock.padding + 2,
|
||||
cursorY + config.hostblock.header.height - 1,
|
||||
hostname,
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
// display::ui::text::Font{"LiberationSans-Regular", HEADER_FONT_SIZE});
|
||||
// display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
display::ui::text::Font{std::string(HEADER_FONT_NAME.begin(), HEADER_FONT_NAME.end()), HEADER_FONT_SIZE});
|
||||
display::ui::text::Font{config.hostblock.header.font.name, config.hostblock.header.font.size});
|
||||
|
||||
cursorY += HEADER_HEIGHT + SECTION_GAP;
|
||||
cursorY += config.hostblock.header.height + config.hostblock.gap;
|
||||
|
||||
// ===== CPU bars =====
|
||||
int cpuCount = std::min<int>(metrics.cpu.coreLoads.size(),
|
||||
CPU_MAX_PER_ROW * CPU_ROWS);
|
||||
config.hostblock.cpu.max_per_row * config.hostblock.cpu.rows);
|
||||
|
||||
for (int i = 0; i < cpuCount; ++i)
|
||||
{
|
||||
int row = i / CPU_MAX_PER_ROW;
|
||||
int col = i % CPU_MAX_PER_ROW;
|
||||
int row = i / config.hostblock.cpu.max_per_row;
|
||||
int col = i % config.hostblock.cpu.max_per_row;
|
||||
|
||||
int bx = x + PADDING +
|
||||
col * (CPU_BAR_WIDTH + CPU_BAR_TEMP_WIDTH - 1 + CPU_BAR_GAP);
|
||||
int bx = x + config.hostblock.padding +
|
||||
col * (config.hostblock.cpu.width + config.hostblock.cpu.temperature.width - 1 + config.hostblock.cpu.gap_h);
|
||||
|
||||
int by = cursorY +
|
||||
row * (CPU_BAR_HEIGHT + CPU_BAR_GAP);
|
||||
row * (config.hostblock.cpu.height + config.hostblock.cpu.gap_v);
|
||||
|
||||
float value = std::clamp(metrics.cpu.coreLoads[i] / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
cpuBar.draw(fb, bx, by, value);
|
||||
cpuBar.draw(fb, bx, by, value, 0, online);
|
||||
|
||||
value = std::clamp((metrics.cpu.coreTemps[i].current - CPU_BAR_TEMP_MIN) / ((metrics.cpu.coreTemps[i].max == 0 ? 100.0f : metrics.cpu.coreTemps[i].max) - CPU_BAR_TEMP_MIN), 0.0f, 1.0f);
|
||||
value = std::clamp((metrics.cpu.coreTemps[i].current - config.hostblock.cpu.temperature.min) / ((metrics.cpu.coreTemps[i].max == 0 ? static_cast<float>(config.hostblock.cpu.temperature.max) : metrics.cpu.coreTemps[i].max) - config.hostblock.cpu.temperature.min), 0.0f, 1.0f);
|
||||
|
||||
cpuTempBar.draw(fb, bx + CPU_BAR_WIDTH - 1, by, value);
|
||||
cpuTempBar.draw(fb, bx + config.hostblock.cpu.width - 1, by, value, 0, online);
|
||||
}
|
||||
|
||||
cursorY += CPU_ROWS * CPU_BAR_HEIGHT +
|
||||
(CPU_ROWS - 1) * CPU_BAR_GAP +
|
||||
SECTION_GAP;
|
||||
cursorY += config.hostblock.cpu.rows * config.hostblock.cpu.height +
|
||||
(config.hostblock.cpu.rows - 1) * config.hostblock.cpu.gap_v +
|
||||
config.hostblock.gap;
|
||||
|
||||
// ===== Memory bar =====
|
||||
float memValue, swapValue = 0.0f, hugepagesValue = 0.0f;
|
||||
@@ -102,18 +103,18 @@ namespace display::ui::hostblock
|
||||
|
||||
memBar.draw(
|
||||
fb,
|
||||
x + PADDING,
|
||||
x + config.hostblock.padding,
|
||||
cursorY,
|
||||
std::clamp(memValue, 0.0f, 1.0f), std::clamp(hugepagesValue, 0.0f, 1.0f));
|
||||
std::clamp(memValue, 0.0f, 1.0f), std::clamp(hugepagesValue, 0.0f, 1.0f), online);
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
x + config.hostblock.memory.font.padding,
|
||||
cursorY + 8,
|
||||
"MEM: " + display::ui::text::formatFloat(static_cast<float>(metrics.memory.memory.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast<float>(metrics.memory.memory.total) / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size});
|
||||
|
||||
cursorY += MEM_BAR_HEIGHT + SECTION_GAP;
|
||||
cursorY += config.hostblock.memory.height + config.hostblock.memory.gap;
|
||||
|
||||
if (metrics.memory.swap.total > 0)
|
||||
{
|
||||
@@ -121,19 +122,19 @@ namespace display::ui::hostblock
|
||||
|
||||
memBar.draw(
|
||||
fb,
|
||||
x + PADDING,
|
||||
x + config.hostblock.padding,
|
||||
cursorY,
|
||||
std::clamp(swapValue, 0.0f, 1.0f));
|
||||
std::clamp(swapValue, 0.0f, 1.0f), 0, online);
|
||||
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
x + config.hostblock.memory.font.padding,
|
||||
cursorY + 8,
|
||||
"SWP: " + display::ui::text::formatFloat(static_cast<float>(metrics.memory.swap.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast<float>(metrics.memory.swap.total) / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
display::ui::text::Font{config.hostblock.memory.font.name, config.hostblock.memory.font.size});
|
||||
|
||||
cursorY += MEM_BAR_HEIGHT + SECTION_GAP;
|
||||
cursorY += config.hostblock.memory.height + config.hostblock.gap;
|
||||
}
|
||||
|
||||
// ===== Disk bar =====
|
||||
@@ -142,18 +143,88 @@ namespace display::ui::hostblock
|
||||
for (int i = 0; i < diskCount; ++i)
|
||||
{
|
||||
int by = cursorY +
|
||||
i * (MEM_BAR_HEIGHT + SECTION_GAP);
|
||||
i * (config.hostblock.disks.height + config.hostblock.disks.gap);
|
||||
|
||||
float value = std::clamp(static_cast<float>(metrics.disks[i].metrics.used) / metrics.disks[i].metrics.total, 0.0f, 1.0f);
|
||||
|
||||
memBar.draw(fb, x + PADDING, by, value);
|
||||
memBar.draw(fb, x + config.hostblock.padding, by, value, 0, online);
|
||||
text.drawTextOutlined(fb,
|
||||
x + MEM_BAR_TEXT_PADDING_X,
|
||||
x + config.hostblock.disks.font.padding,
|
||||
by + 8,
|
||||
"D/" + metrics.disks[i].name + ": " + display::ui::text::formatFloat(static_cast<float>(metrics.disks[i].metrics.used) / 1073741824) + "/" + display::ui::text::formatFloat(static_cast<float>(metrics.disks[i].metrics.total) / 1073741824),
|
||||
display::ui::theme::text::TEXT,
|
||||
display::ui::theme::text::OUTLINE,
|
||||
display::ui::text::Font{"PixelFive-Regular", 5});
|
||||
display::ui::text::Font{config.hostblock.disks.font.name, config.hostblock.disks.font.size});
|
||||
}
|
||||
|
||||
cursorY += diskCount * (config.hostblock.disks.height + config.hostblock.disks.gap);
|
||||
|
||||
// ===== Network bar =====
|
||||
for (const auto &iface : metrics.networks)
|
||||
{
|
||||
int by = cursorY;
|
||||
|
||||
float rxNorm = 0.0f;
|
||||
float txNorm = 0.0f;
|
||||
|
||||
if (iface.online)
|
||||
{
|
||||
if (iface.rxMaxBps > 0)
|
||||
rxNorm = std::clamp(static_cast<float>(iface.rxBps / iface.rxMaxBps), 0.0f, 1.0f);
|
||||
if (iface.txMaxBps > 0)
|
||||
txNorm = std::clamp(static_cast<float>(iface.txBps / iface.txMaxBps), 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
// background bar
|
||||
memBar.draw(
|
||||
fb,
|
||||
x + config.hostblock.padding,
|
||||
by,
|
||||
0.0f,
|
||||
0.0f,
|
||||
iface.online && online);
|
||||
|
||||
int barX = x + config.hostblock.padding;
|
||||
int barW = config.hostblock.width - config.hostblock.padding * 2;
|
||||
int barH = config.hostblock.networks.height;
|
||||
|
||||
// RX overlay
|
||||
fb.fillRect(
|
||||
barX,
|
||||
by,
|
||||
static_cast<int>(barW * rxNorm),
|
||||
barH,
|
||||
{0, 180, 0});
|
||||
|
||||
// TX overlay
|
||||
fb.fillRect(
|
||||
barX,
|
||||
by,
|
||||
static_cast<int>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,39 +8,14 @@
|
||||
#include "display/ui/text/Renderer.h"
|
||||
#include "display/ui/bar/Bar.h"
|
||||
#include "metrics/Host.h"
|
||||
#include "config/server/StyleConfig.h"
|
||||
|
||||
namespace display::ui::hostblock
|
||||
{
|
||||
// ===== Layout =====
|
||||
constexpr int BLOCK_WIDTH = 118; // 118
|
||||
constexpr int BLOCK_HEIGHT = 146; // 146
|
||||
constexpr int PADDING = 5; // 5
|
||||
|
||||
constexpr int HEADER_HEIGHT = 7; // 12
|
||||
constexpr std::string_view HEADER_FONT_NAME = "PixelFive-Regular";
|
||||
constexpr int HEADER_FONT_SIZE = 5; // 10
|
||||
constexpr int SECTION_GAP = 4; // 4
|
||||
|
||||
// ===== CPU bars =====
|
||||
constexpr int CPU_BAR_WIDTH = 8; // 10
|
||||
constexpr int CPU_BAR_TEMP_WIDTH = 3;
|
||||
constexpr int CPU_BAR_GAP = 4; // 4
|
||||
constexpr int CPU_BAR_HEIGHT = 16; // 16
|
||||
constexpr int CPU_MAX_PER_ROW = 8; // 8
|
||||
constexpr int CPU_ROWS = 2; // 2
|
||||
constexpr int CPU_BAR_TEMP_MIN = 20;
|
||||
|
||||
// ===== Memory bar =====
|
||||
constexpr int MEM_BAR_HEIGHT = 11; // 8
|
||||
constexpr int MEM_BAR_TEXT_PADDING_X = PADDING + 3;
|
||||
|
||||
class HostBlock
|
||||
{
|
||||
public:
|
||||
HostBlock();
|
||||
|
||||
static constexpr int width() { return BLOCK_WIDTH; }
|
||||
static constexpr int height() { return BLOCK_HEIGHT; }
|
||||
HostBlock(config::server::StyleConfig &config);
|
||||
|
||||
void draw(
|
||||
display::graphics::Framebuffer &fb,
|
||||
@@ -50,6 +25,7 @@ namespace display::ui::hostblock
|
||||
const metrics::Host &metrics);
|
||||
|
||||
private:
|
||||
config::server::StyleConfig &config;
|
||||
display::ui::bar::Bar cpuBar;
|
||||
display::ui::bar::Bar cpuTempBar;
|
||||
display::ui::bar::Bar memBar;
|
||||
|
||||
@@ -17,4 +17,18 @@ namespace display::ui::text
|
||||
std::snprintf(buffer, sizeof(buffer), "%.*f", decimals, value);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
std::string formatSpeed(float bps)
|
||||
{
|
||||
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));
|
||||
|
||||
return buf;
|
||||
}
|
||||
} // namespace display::ui::text
|
||||
|
||||
@@ -6,4 +6,5 @@ namespace display::ui::text
|
||||
{
|
||||
std::string formatFloat(float value, int decimals = 1);
|
||||
std::string formatDouble(double value, int decimals = 1);
|
||||
std::string formatSpeed(float bps);
|
||||
} // namespace display::ui::text
|
||||
|
||||
@@ -12,7 +12,7 @@ int main(int argc, char **argv)
|
||||
{
|
||||
config::client::Config config = config::client::Config::load("/etc/esdashboard/client.ini");
|
||||
network::Client client(config.network.serverHost, config.network.serverPort);
|
||||
metrics::Collector collector(config.collector.disks);
|
||||
metrics::Collector collector(config.collector.disks, config.collector.networks);
|
||||
network::Agent agent(std::move(client), std::move(collector), config.network.intervalMs);
|
||||
|
||||
agent.start();
|
||||
|
||||
@@ -15,7 +15,7 @@ int main(int argc, char **argv)
|
||||
config::server::Config config = config::server::Config::load("/etc/esdashboard/server.ini");
|
||||
model::HostRegistry registry;
|
||||
display::graphics::Framebuffer fb(config.display.framebuffer.c_str(), display::graphics::FramebufferRotation(config.display.rotation));
|
||||
display::graphics::Renderer renderer(fb, registry);
|
||||
display::graphics::Renderer renderer(fb, registry, config.style);
|
||||
network::Server server(config.network.listenPort, registry);
|
||||
|
||||
server.start();
|
||||
|
||||
+101
-1
@@ -15,7 +15,7 @@
|
||||
namespace metrics
|
||||
{
|
||||
|
||||
Collector::Collector(const std::vector<std::vector<std::string>> &disks) : disks(disks)
|
||||
Collector::Collector(const std::vector<std::vector<std::string>> &disks, const std::vector<std::vector<std::string>> interfaces) : disks(disks)
|
||||
{
|
||||
std::vector<int> cpuLogicalIds;
|
||||
for (const auto &cpuDir : std::filesystem::directory_iterator("/sys/devices/system/cpu"))
|
||||
@@ -43,6 +43,22 @@ namespace metrics
|
||||
f >> coreId;
|
||||
logicalToPhysical[i] = coreId;
|
||||
}
|
||||
|
||||
lastNetUpdate = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (const auto &iface : interfaces)
|
||||
{
|
||||
Network network;
|
||||
network.name = iface.at(0);
|
||||
network.interface = iface.at(1);
|
||||
|
||||
network.online = isInterfaceOnline(network.interface);
|
||||
|
||||
if (network.online)
|
||||
readInterfaceCounters(network.interface, network.rxBytes, network.txBytes);
|
||||
|
||||
netIfaces[network.interface] = network;
|
||||
}
|
||||
}
|
||||
|
||||
Host Collector::collect()
|
||||
@@ -70,6 +86,11 @@ namespace metrics
|
||||
host.uptime = readUptime();
|
||||
host.hostname = readHostname();
|
||||
|
||||
updateNetwork();
|
||||
host.networks.reserve(netIfaces.size());
|
||||
for (const auto &[key, value] : netIfaces)
|
||||
host.networks.push_back(value);
|
||||
|
||||
return host;
|
||||
}
|
||||
|
||||
@@ -306,4 +327,83 @@ namespace metrics
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
bool Collector::isInterfaceOnline(const std::string &iface)
|
||||
{
|
||||
std::ifstream f("/sys/class/net/" + iface + "/operstate");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
std::string state;
|
||||
f >> state;
|
||||
return state == "up";
|
||||
}
|
||||
|
||||
bool Collector::readInterfaceCounters(const std::string &iface, uint64_t &rx, uint64_t &tx)
|
||||
{
|
||||
std::string base = "/sys/class/net/" + iface + "/statistics/";
|
||||
|
||||
std::ifstream frx(base + "rx_bytes");
|
||||
std::ifstream ftx(base + "tx_bytes");
|
||||
|
||||
if (!frx || !ftx)
|
||||
return false;
|
||||
|
||||
frx >> rx;
|
||||
ftx >> tx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Collector::updateNetwork()
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
float dt =
|
||||
std::chrono::duration<float>(now - lastNetUpdate).count();
|
||||
|
||||
if (dt <= 0.0)
|
||||
return;
|
||||
|
||||
for (auto &[name, s] : netIfaces)
|
||||
{
|
||||
bool online = isInterfaceOnline(name);
|
||||
|
||||
if (!online)
|
||||
{
|
||||
s.online = false;
|
||||
s.rxBps = 0;
|
||||
s.txBps = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t rx = 0, tx = 0;
|
||||
if (!readInterfaceCounters(name, rx, tx))
|
||||
{
|
||||
s.online = false;
|
||||
s.rxBps = 0;
|
||||
s.txBps = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (s.online) // был online и раньше
|
||||
{
|
||||
s.rxBps = (rx - s.rxBytes) / dt;
|
||||
s.txBps = (tx - s.txBytes) / dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
// только что появился — не считаем скачок
|
||||
s.rxBps = 0;
|
||||
s.txBps = 0;
|
||||
}
|
||||
|
||||
s.rxBytes = rx;
|
||||
s.txBytes = tx;
|
||||
s.online = true;
|
||||
|
||||
s.rxMaxBps = std::max(s.rxMaxBps * 0.98f, s.rxBps);
|
||||
s.txMaxBps = std::max(s.txMaxBps * 0.98f, s.txBps);
|
||||
}
|
||||
|
||||
lastNetUpdate = now;
|
||||
}
|
||||
|
||||
}
|
||||
+10
-1
@@ -1,15 +1,18 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
#include "metrics/Host.h"
|
||||
#include "metrics/CpuTemperature.h"
|
||||
#include "metrics/Network.h"
|
||||
|
||||
namespace metrics
|
||||
{
|
||||
class Collector
|
||||
{
|
||||
public:
|
||||
Collector(const std::vector<std::vector<std::string>> &disks = {{"R", "/"}});
|
||||
Collector(const std::vector<std::vector<std::string>> &disks = {{"R", "/"}}, const std::vector<std::vector<std::string>> interfaces = {});
|
||||
explicit Collector();
|
||||
|
||||
Host collect();
|
||||
@@ -25,6 +28,9 @@ namespace metrics
|
||||
std::pair<CpuTimes, std::vector<CpuTimes>> prevCpu;
|
||||
std::vector<int> logicalToPhysical;
|
||||
|
||||
std::map<std::string, Network> netIfaces;
|
||||
std::chrono::high_resolution_clock::time_point lastNetUpdate;
|
||||
|
||||
std::pair<CpuTimes, std::vector<CpuTimes>> readCpuTimes();
|
||||
const std::vector<std::vector<std::string>> disks;
|
||||
float cpuLoad(const CpuTimes &prev, const CpuTimes &cur);
|
||||
@@ -34,5 +40,8 @@ namespace metrics
|
||||
void readLoad(float &l1, float &l5, float &l15);
|
||||
uint64_t readUptime();
|
||||
std::string readHostname();
|
||||
bool isInterfaceOnline(const std::string &iface);
|
||||
bool readInterfaceCounters(const std::string &iface, uint64_t &rx, uint64_t &tx);
|
||||
void updateNetwork();
|
||||
};
|
||||
} // namespace metrics
|
||||
|
||||
@@ -67,6 +67,21 @@ namespace metrics
|
||||
buf.writeUint64(d.metrics.total);
|
||||
}
|
||||
|
||||
// Network
|
||||
buf.writeUint8(static_cast<uint8_t>(networks.size()));
|
||||
for (const auto &n : networks)
|
||||
{
|
||||
buf.writeString(n.name);
|
||||
buf.writeString(n.interface);
|
||||
buf.writeBool(n.online);
|
||||
buf.writeUint64(n.rxBytes);
|
||||
buf.writeUint64(n.txBytes);
|
||||
buf.writeFloat(n.rxBps);
|
||||
buf.writeFloat(n.txBps);
|
||||
buf.writeFloat(n.rxMaxBps);
|
||||
buf.writeFloat(n.txMaxBps);
|
||||
}
|
||||
|
||||
return buf.data();
|
||||
}
|
||||
|
||||
@@ -120,6 +135,25 @@ namespace metrics
|
||||
h.disks.push_back(disk);
|
||||
}
|
||||
|
||||
// Network
|
||||
uint8_t numNetworks = buf.readUint8();
|
||||
h.networks.clear();
|
||||
h.networks.reserve(numNetworks);
|
||||
for (uint8_t i = 0; i < numNetworks; ++i)
|
||||
{
|
||||
Network network;
|
||||
network.name = buf.readString();
|
||||
network.interface = buf.readString();
|
||||
network.online = buf.readBool();
|
||||
network.rxBytes = buf.readUint64();
|
||||
network.txBytes = buf.readUint64();
|
||||
network.rxBps = buf.readFloat();
|
||||
network.txBps = buf.readFloat();
|
||||
network.rxMaxBps = buf.readFloat();
|
||||
network.txMaxBps = buf.readFloat();
|
||||
h.networks.push_back(network);
|
||||
}
|
||||
|
||||
h.packetTimepoint = std::chrono::high_resolution_clock::now();
|
||||
|
||||
return h;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "metrics/Cpu.h"
|
||||
#include "metrics/Memory.h"
|
||||
#include "metrics/Disk.h"
|
||||
#include "metrics/Network.h"
|
||||
|
||||
namespace metrics
|
||||
{
|
||||
@@ -17,6 +18,7 @@ namespace metrics
|
||||
Cpu cpu;
|
||||
Memory memory;
|
||||
std::vector<Disk> disks;
|
||||
std::vector<Network> networks;
|
||||
uint64_t uptime;
|
||||
float load1, load5, load15;
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace metrics
|
||||
{
|
||||
struct Network
|
||||
{
|
||||
std::string name;
|
||||
std::string interface;
|
||||
|
||||
bool online = false;
|
||||
|
||||
uint64_t rxBytes = 0;
|
||||
uint64_t txBytes = 0;
|
||||
|
||||
float rxBps = 0.0;
|
||||
float txBps = 0.0;
|
||||
|
||||
float rxMaxBps = 1.0;
|
||||
float txMaxBps = 1.0;
|
||||
};
|
||||
} // namespace metrics
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
namespace network
|
||||
{
|
||||
void Buffer::writeBool(bool v)
|
||||
{
|
||||
writeUint8(v ? 1 : 0);
|
||||
}
|
||||
|
||||
void Buffer::writeUint8(uint8_t v)
|
||||
{
|
||||
buffer.push_back(v);
|
||||
@@ -74,6 +79,12 @@ namespace network
|
||||
}
|
||||
|
||||
// --- Reading ---
|
||||
bool Buffer::readBool()
|
||||
{
|
||||
uint8_t v = readUint8();
|
||||
return v != 0;
|
||||
}
|
||||
|
||||
uint8_t Buffer::readUint8()
|
||||
{
|
||||
checkRemaining(1);
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace network
|
||||
explicit Buffer(size_t reserve = 0) : buffer(reserve), pos(0) {}
|
||||
|
||||
// --- Writing ---
|
||||
void writeBool(bool v);
|
||||
void writeUint8(uint8_t v);
|
||||
void writeUint32(uint32_t v);
|
||||
void writeUint16(uint16_t v);
|
||||
@@ -26,6 +27,7 @@ namespace network
|
||||
void writeBytes(const uint8_t *data, size_t len);
|
||||
void writeString(const std::string &s);
|
||||
// --- Reading ---
|
||||
bool readBool();
|
||||
uint8_t readUint8();
|
||||
uint16_t readUint16();
|
||||
uint32_t readUint32();
|
||||
|
||||
Reference in New Issue
Block a user