This commit is contained in:
2025-12-25 13:00:42 +00:00
parent f5dc0ccbc9
commit 86947912dc
58 changed files with 959 additions and 328 deletions
+3
View File
@@ -44,4 +44,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(INCLUDE_BASE_DIR ${PROJECT_SOURCE_DIR}/src) 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") add_subdirectory("src")
+11 -55
View File
@@ -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) if (BUILD_SERVER OR BUILD_CLIENT)
message(STATUS "Configuring ${TARGET_NAME_SERVER}") if (BUILD_SERVER)
add_subdirectory(Display)
add_subdirectory(Model)
endif()
add_subdirectory(Helpers)
add_subdirectory(Config)
add_subdirectory(Metrics)
add_subdirectory(Network)
# Основной исполняемый файл add_subdirectory(Main)
add_executable(${TARGET_NAME_SERVER} Server.cpp) endif()
# Линкуем зависимости
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 "")
+9
View File
@@ -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()
+17
View File
@@ -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}
)
+19
View File
@@ -0,0 +1,19 @@
#include "Config/Client/Config.h"
#include "Config/Common/IniParser.h"
#include <sstream>
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
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <string>
#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
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <chrono>
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
+12
View File
@@ -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}
)
+58
View File
@@ -0,0 +1,58 @@
#include "Config/Common/IniParser.h"
#include <fstream>
#include <sstream>
#include <algorithm>
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 &section,
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;
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <string>
#include <unordered_map>
class IniParser
{
public:
bool load(const std::string &path);
std::string get(const std::string &section,
const std::string &key,
const std::string &def = "") const;
private:
using Section = std::unordered_map<std::string, std::string>;
std::unordered_map<std::string, Section> data_;
};
+13
View File
@@ -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
+17
View File
@@ -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}
)
+39
View File
@@ -0,0 +1,39 @@
#include "Config/Server/Config.h"
#include "Config/Common/IniParser.h"
#include <sstream>
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
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <string>
#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
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <chrono>
namespace Config::Server
{
struct DisplayConfig
{
int rotation = 0;
std::chrono::milliseconds refreshMs = std::chrono::milliseconds(1000);
};
} // namespace Config::Server
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <chrono>
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
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <string>
namespace Config::Server
{
struct TextStyleConfig
{
std::string fontPath;
int fontSize = 10;
};
} // namespace Config::Server
+1 -14
View File
@@ -1,17 +1,4 @@
message(STATUS " Configuring Display") message(STATUS "·Configuring Display")
add_library(Display
INTERFACE
)
add_subdirectory(Graphics) add_subdirectory(Graphics)
add_subdirectory(UI) add_subdirectory(UI)
target_link_libraries(Display
INTERFACE Graphics UI
)
target_include_directories(Display
INTERFACE
${INCLUDE_BASE_DIR}
)
+11 -5
View File
@@ -1,15 +1,21 @@
message(STATUS " Configuring Graphics") message(STATUS "··Configuring Graphics")
add_library(Graphics add_library(DisplayGraphics
Framebuffer.cpp Framebuffer.cpp
Renderer.cpp Renderer.cpp
) )
target_link_libraries(Graphics add_library(Display::Graphics ALIAS DisplayGraphics)
PRIVATE Model Helpers UI
target_link_libraries(DisplayGraphics
PRIVATE
Model::All
Helpers::All
Display::UI::Text
Display::UI::HostBlock
) )
target_include_directories(Graphics target_include_directories(DisplayGraphics
PUBLIC PUBLIC
${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}
) )
+86 -52
View File
@@ -9,95 +9,129 @@
namespace Display::Graphics namespace Display::Graphics
{ {
Framebuffer::Framebuffer(const char *device) Framebuffer::Framebuffer(const char *device, FramebufferRotation rotation) : rotation(rotation)
{ {
fd_ = open(device, O_RDWR); fd = open(device, O_RDWR);
if (fd_ < 0) if (fd < 0)
throw std::runtime_error("Cannot open framebuffer"); throw std::runtime_error("Failed to open framebuffer");
fb_var_screeninfo vinfo; fb_var_screeninfo vinfo{};
fb_fix_screeninfo finfo; 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); if (rotation == FramebufferRotation::R90 || rotation == FramebufferRotation::R270)
ioctl(fd_, FBIOGET_FSCREENINFO, &finfo); {
logical.width = physical.height;
logical.height = physical.width;
}
else
{
logical.width = physical.width;
logical.height = physical.height;
}
width_ = vinfo.xres; buffer.resize(physical.width * physical.height, 0);
height_ = vinfo.yres;
line_len_ = finfo.line_length;
size_ = finfo.smem_len;
fb_ = static_cast<uint8_t *>(mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0)); fb = static_cast<uint8_t *>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
if (fb_ == MAP_FAILED) if (fb == MAP_FAILED)
throw std::runtime_error("mmap failed"); throw std::runtime_error("Failed to mmap framebuffer");
} }
Framebuffer::~Framebuffer() Framebuffer::~Framebuffer()
{ {
munmap(fb_, size_); munmap(fb, size);
close(fd_); close(fd);
} }
void Framebuffer::clear(uint16_t color) 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<uint16_t *>(fb_ + y * line_len_); uint16_t *row = reinterpret_cast<uint16_t *>(fb + y * line_length);
for (int x = 0; x < width_; ++x) for (int x = 0; x < logical.width; ++x)
row[x] = color; 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; for (int x = 0; x < logical.width; ++x)
if (py < 0 || py >= height_)
continue;
uint16_t *row = reinterpret_cast<uint16_t *>(fb_ + py * line_len_);
for (int i = 0; i < w; ++i)
{ {
int px = x + i; int fx = 0;
if (px < 0 || px >= width_) int fy = 0;
continue;
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<uint16_t *>(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)
{ {
// верх for (int j = 0; j < h; ++j)
fillRect(x, y, w, 1, c); for (int i = 0; i < w; ++i)
// низ setPixel(x + i, y + j, color);
fillRect(x, y + h - 1, w, 1, c);
// лево
fillRect(x, y, 1, h, c);
// право
fillRect(x + w - 1, y, 1, h, c);
} }
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<uint16_t *>(fb_ + y * line_len_) + x; void Framebuffer::setPixel(int x, int y, const Color &color)
*p = ((c.r & 0xF8) << 8) | ((c.g & 0xFC) << 3) | (c.b >> 3); {
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 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}; return {0, 0, 0, 255};
uint16_t p = buffer[y * logical.width + x];
uint16_t p = *(reinterpret_cast<uint16_t *>(fb_ + y * line_len_) + x);
Color c; Color c;
c.r = (p >> 8) & 0xF8; c.r = ((p >> 11) & 0x1F) << 3;
c.g = (p >> 3) & 0xFC; c.g = ((p >> 5) & 0x3F) << 2;
c.b = (p << 3) & 0xF8; c.b = (p & 0x1F) << 3;
c.a = 255; c.a = 255;
return c; return c;
} }
+26 -14
View File
@@ -1,31 +1,43 @@
#pragma once #pragma once
#include "Display/Graphics/Color.h" #include <vector>
#include <cstdint> #include <cstdint>
#include <cstddef>
#include "Display/Graphics/Color.h"
#include "Display/Graphics/FramebufferRotation.h"
namespace Display::Graphics namespace Display::Graphics
{ {
class Framebuffer class Framebuffer
{ {
public: public:
Framebuffer(const char *device); Framebuffer(const char *device, FramebufferRotation rotation);
~Framebuffer(); ~Framebuffer();
void clear(uint16_t color); void clear(uint16_t color);
void fillRect(int x, int y, int w, int h, uint16_t color); void present();
void drawRect(int x, int y, int w, int h, const Color &c); void fillRect(int x, int y, int w, int h, const Color &color);
void setPixel(int x, int y, const Color &c); 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; Color getPixel(int x, int y) const;
int width() const { return width_; } int getWidth() const { return logical.width; }
int height() const { return height_; } int getHeight() const { return logical.height; }
FramebufferRotation getRotation() const { return rotation; }
private: private:
int fd_; struct Dimensions
uint8_t *fb_; {
size_t size_; int width;
int width_; int height;
int height_; };
int line_len_; FramebufferRotation rotation = FramebufferRotation::R0;
std::vector<uint16_t> buffer;
Dimensions physical;
Dimensions logical;
int fd;
uint8_t *fb;
size_t size;
int line_length;
}; };
} // namespace Display::Graphics } // namespace Display::Graphics
@@ -0,0 +1,12 @@
#pragma once
namespace Display::Graphics
{
enum class FramebufferRotation
{
R0,
R90,
R180,
R270
};
} // namespace Display::Graphics
+21 -19
View File
@@ -4,38 +4,40 @@ namespace Display::Graphics
{ {
Renderer::Renderer(Framebuffer &framebuffer, Model::HostRegistry &registry) Renderer::Renderer(Framebuffer &framebuffer, Model::HostRegistry &registry)
: framebuffer(framebuffer), : framebuffer(framebuffer),
registry(registry), registry(registry)
barMem(200, 10)
{ {
} }
void Renderer::render() void Renderer::render()
{ {
// очистка экрана
framebuffer.clear(Color{0, 0, 0}); framebuffer.clear(Color{0, 0, 0});
int y = 10; // начальная вертикальная позиция int blocksPerRow =
int lineHeight = 24; // высота строки (подбираем под шрифт) (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()) for (auto &[host, m] : registry.snapshot())
{ {
// рисуем фон строки int col = index % blocksPerRow;
framebuffer.fillRect(10, y - 18, 300, lineHeight, Color{0, 64, 0}); int row = index / blocksPerRow;
// формируем строку int x = START_X + col * (Display::UI::HostBlock::BLOCK_WIDTH + BLOCK_GAP);
std::string line = host + " " + std::to_string(int(m.cpu.loads.at(0))) + int y = START_Y + row * (Display::UI::HostBlock::BLOCK_HEIGHT + BLOCK_GAP);
"% " + 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));
// выводим текст на экран hostblock.draw(
textRenderer.drawText(framebuffer, 12, y, line, Color{255, 255, 255}); framebuffer,
textRenderer,
x,
y,
host,
m);
float mem = (float)m.memory.used / (float)m.memory.total; ++index;
barMem.draw(framebuffer, 10, y + 35, mem);
y += lineHeight; // переход на следующую строку
} }
framebuffer.present();
} }
} // namespace Display::Graphics } // namespace Display::Graphics
+8 -2
View File
@@ -2,12 +2,18 @@
#include "Display/Graphics/Framebuffer.h" #include "Display/Graphics/Framebuffer.h"
#include "Model/HostRegistry.h" #include "Model/HostRegistry.h"
#include "Display/UI/Text/Renderer.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 "Display/Graphics/Color.h"
#include <string> #include <string>
namespace Display::Graphics 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 class Renderer
{ {
public: public:
@@ -22,6 +28,6 @@ namespace Display::Graphics
Framebuffer &framebuffer; Framebuffer &framebuffer;
Model::HostRegistry &registry; Model::HostRegistry &registry;
Display::UI::Text::Renderer textRenderer; Display::UI::Text::Renderer textRenderer;
Display::UI::Bar::Bar barMem; Display::UI::HostBlock::HostBlock hostblock;
}; };
} // namespace Display::Graphics } // namespace Display::Graphics
+38 -8
View File
@@ -3,8 +3,8 @@
namespace Display::UI::Bar namespace Display::UI::Bar
{ {
Bar::Bar(int width, int height, Style style) Bar::Bar(int width, int height, Orientation orientation, Style style)
: width(width), height(height), 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); value = std::clamp(value, 0.0f, 1.0f);
// фон // 1. Рисуем фон
framebuffer.fillRect(x, y, width, height, style.background); framebuffer.fillRect(x, y, width, height, style.background);
// заполнение // 2. Рисуем заполнение
int fillWidth = static_cast<int>(width * value); Display::Graphics::Color fillColor = valueToColor(value);
if (fillWidth > 0)
if (orientation == Orientation::Horizontal)
{ {
framebuffer.fillRect(x, y, fillWidth, height, style.fill); int fillWidth = static_cast<int>(width * value);
framebuffer.fillRect(x, y, fillWidth, height, fillColor);
}
else // Vertical
{
int fillHeight = static_cast<int>(height * value);
framebuffer.fillRect(x, y + (height - fillHeight), width, fillHeight, fillColor);
} }
// рамка // 3. Рисуем рамку
if (style.drawBorder) if (style.drawBorder)
{ {
// можно добавить метод drawRect в Framebuffer
framebuffer.drawRect(x, y, width, height, style.border); 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<uint8_t>(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<uint8_t>(180 * (1.0f - t)), // G: 180 → 0
0};
}
}
} // namespace Display::UI::Bar } // namespace Display::UI::Bar
+6 -14
View File
@@ -1,26 +1,15 @@
#pragma once #pragma once
#include "Display/Graphics/Framebuffer.h" #include "Display/Graphics/Framebuffer.h"
#include "Display/Graphics/Color.h" #include "Display/Graphics/Color.h"
#include "Display/UI/Bar/Orientation.h"
#include "Display/UI/Bar/Style.h"
namespace Display::UI::Bar namespace Display::UI::Bar
{ {
class Bar class Bar
{ {
public: public:
struct Style Bar(int width, int height, Orientation orientation = Orientation::Horizontal, Style 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 = {});
void draw(Display::Graphics::Framebuffer &framebuffer, void draw(Display::Graphics::Framebuffer &framebuffer,
int x, int y, int x, int y,
@@ -29,6 +18,9 @@ namespace Display::UI::Bar
private: private:
int width; int width;
int height; int height;
Orientation orientation;
Style style; Style style;
Display::Graphics::Color valueToColor(float value);
}; };
} // namespace Display::UI::Bar } // namespace Display::UI::Bar
+8 -6
View File
@@ -1,15 +1,17 @@
message(STATUS " Configuring Bar") message(STATUS "···Configuring Bar")
add_library(Bar add_library(DisplayUIBar
Bar.cpp Bar.cpp
) )
target_link_libraries(Bar add_library(Display::UI::Bar ALIAS DisplayUIBar)
PRIVATE Graphics
target_link_libraries(DisplayUIBar
PUBLIC
Display::Graphics
) )
# include-root общий target_include_directories(DisplayUIBar
target_include_directories(Bar
PUBLIC PUBLIC
${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}
) )
+10
View File
@@ -0,0 +1,10 @@
#pragma once
namespace Display::UI::Bar
{
enum class Orientation
{
Horizontal,
Vertical
};
} // namespace Display::UI::Bar
+26
View File
@@ -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
+2 -18
View File
@@ -1,21 +1,5 @@
message(STATUS " Configuring UI") message(STATUS "··Configuring UI")
add_library(UI
INTERFACE
)
add_subdirectory(Bar) add_subdirectory(Bar)
add_subdirectory(Text) add_subdirectory(Text)
add_subdirectory(HostBlock)
target_link_libraries(UI
INTERFACE Bar Text
)
target_include_directories(UI
INTERFACE
${INCLUDE_BASE_DIR}
)
+19
View File
@@ -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}
)
+93
View File
@@ -0,0 +1,93 @@
#include "Display/UI/HostBlock/HostBlock.h"
#include <algorithm>
#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<int>(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));
}
}
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include <string>
#include <vector>
#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;
};
}
+1 -1
View File
@@ -16,7 +16,7 @@ namespace Display::UI::Text
{0x1E, 0x21, 0x21, 0x1F, 0x01, 0x02, 0x1C} // 9 {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) if (d < 0 || d > 9)
return; return;
+1 -1
View File
@@ -8,6 +8,6 @@ namespace Display::UI::Text
class BitmapFont class BitmapFont
{ {
public: 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 } // namespace Display::UI::Text
+10 -5
View File
@@ -1,20 +1,25 @@
message(STATUS " Configuring Text") message(STATUS "···Configuring Text")
add_library(Text add_library(DisplayUIText
BitmapFont.cpp BitmapFont.cpp
FontFace.cpp FontFace.cpp
GlyphCache.cpp GlyphCache.cpp
Renderer.cpp Renderer.cpp
) )
add_library(Display::UI::Text ALIAS DisplayUIText)
find_package(Freetype REQUIRED) find_package(Freetype REQUIRED)
target_link_libraries(Text target_link_libraries(DisplayUIText
PRIVATE Graphics ${FREETYPE_LIBRARIES} PRIVATE
Display::Graphics
${FREETYPE_LIBRARIES}
) )
# include-root общий # include-root общий
target_include_directories(Text target_include_directories(DisplayUIText
PUBLIC PUBLIC
${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}
${FREETYPE_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS}
+5 -6
View File
@@ -1,17 +1,16 @@
message(STATUS " Configuring Model") message(STATUS "·Configuring Model")
# Генерация Version.h из Version.h.in
configure_file( configure_file(
Version.h.in Version.h.in
${CMAKE_CURRENT_BINARY_DIR}/Version.h ${CMAKE_CURRENT_BINARY_DIR}/Version.h
) )
# Создаём "интерфейсную библиотеку", чтобы передавать include dirs
add_library(Helpers INTERFACE) add_library(Helpers INTERFACE)
# Добавляем include директории: и исходники, и сгенерированные файлы add_library(Helpers::All ALIAS Helpers)
target_include_directories(Helpers target_include_directories(Helpers
INTERFACE INTERFACE
${INCLUDE_BASE_DIR} # Color.h ${INCLUDE_BASE_DIR}
${CMAKE_CURRENT_BINARY_DIR} # Version.h ${CMAKE_CURRENT_BINARY_DIR}
) )
+8
View File
@@ -0,0 +1,8 @@
message(STATUS "·Configuring Main")
if (BUILD_SERVER)
add_subdirectory(Server)
endif()
if (BUILD_CLIENT)
add_subdirectory(Client)
endif()
+20
View File
@@ -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}
)
+7 -4
View File
@@ -2,15 +2,18 @@
#include <thread> #include <thread>
#include <iostream> #include <iostream>
#include "Network/Client.h" #include "Network/Client/Client.h"
#include "Network/Agent.h" #include "Network/Client/Agent.h"
#include "Metrics/Collector.h" #include "Metrics/Collector.h"
#include "Config/Client/Config.h"
#include "Config/Client/NetworkConfig.h"
int main(int argc, char **argv) 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; 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(); agent.start();
+24
View File
@@ -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}
)
+29
View File
@@ -0,0 +1,29 @@
#include <chrono>
#include <thread>
#include <iostream>
#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;
}
+3 -1
View File
@@ -1,10 +1,12 @@
message(STATUS " Configuring Metrics") message(STATUS "·Configuring Metrics")
add_library(Metrics add_library(Metrics
Collector.cpp Collector.cpp
Host.cpp Host.cpp
) )
add_library(Metrics::All ALIAS Metrics)
target_include_directories(Metrics target_include_directories(Metrics
PUBLIC PUBLIC
${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}
+20 -12
View File
@@ -19,15 +19,16 @@ namespace Metrics
auto curCpu = readCpuTimes(); auto curCpu = readCpuTimes();
if (prevCpu_.empty()) if (prevCpu.second.empty())
prevCpu_ = curCpu; 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.totalLoad = cpuLoad(prevCpu.first, curCpu.first);
host.cpu.loads[i] = cpuLoad(prevCpu_[i], curCpu[i]); 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.memory = readMemory();
host.disks.push_back(readDisk()); host.disks.push_back(readDisk());
@@ -38,24 +39,31 @@ namespace Metrics
return host; return host;
} }
std::vector<Collector::CpuTimes> Collector::readCpuTimes() std::pair<Collector::CpuTimes, std::vector<Collector::CpuTimes>> Collector::readCpuTimes()
{ {
std::ifstream f("/proc/stat"); std::ifstream f("/proc/stat");
std::vector<Collector::CpuTimes> out; std::pair<CpuTimes, std::vector<CpuTimes>> out;
std::string line; std::string line;
while (std::getline(f, line)) while (std::getline(f, line))
{ {
if (line.rfind("cpu", 0) != 0 || line == "cpu") if (!line.starts_with("cpu"))
continue; break;
std::istringstream ss(line); std::istringstream ss(line);
std::string cpu; 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; 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; return out;
+3 -3
View File
@@ -19,10 +19,10 @@ namespace Metrics
softirq = 0, steal = 0; softirq = 0, steal = 0;
}; };
std::vector<CpuTimes> prevCpu_; std::pair<CpuTimes, std::vector<CpuTimes>> prevCpu;
std::vector<Collector::CpuTimes> readCpuTimes(); std::pair<CpuTimes, std::vector<CpuTimes>> readCpuTimes();
float cpuLoad(const Collector::CpuTimes &prev, const Collector::CpuTimes &cur); float cpuLoad(const CpuTimes &prev, const CpuTimes &cur);
Memory readMemory(); Memory readMemory();
Disk readDisk(const char *path = "/"); Disk readDisk(const char *path = "/");
void readLoad(float &l1, float &l5, float &l15); void readLoad(float &l1, float &l5, float &l15);
+2 -1
View File
@@ -5,6 +5,7 @@ namespace Metrics
{ {
struct Cpu struct Cpu
{ {
std::vector<float> loads; // длина = num_cores float totalLoad;
std::vector<float> coreLoads; // длина = num_cores
}; };
} // namespace Metrics } // namespace Metrics
+12 -3
View File
@@ -35,8 +35,12 @@ namespace Metrics
buffer.insert(buffer.end(), hostname.begin(), hostname.end()); buffer.insert(buffer.end(), hostname.begin(), hostname.end());
// CPU // CPU
buffer.push_back(static_cast<uint8_t>(cpu.loads.size())); uint32_t totalLoadNet = floatToNetwork(cpu.totalLoad);
for (float f : cpu.loads) buffer.insert(buffer.end(),
reinterpret_cast<uint8_t *>(&totalLoadNet),
reinterpret_cast<uint8_t *>(&totalLoadNet) + sizeof(totalLoadNet));
buffer.push_back(static_cast<uint8_t>(cpu.coreLoads.size()));
for (float f : cpu.coreLoads)
{ {
uint32_t net = floatToNetwork(f); uint32_t net = floatToNetwork(f);
buffer.insert(buffer.end(), buffer.insert(buffer.end(),
@@ -93,14 +97,19 @@ namespace Metrics
pos += hostLen; pos += hostLen;
// CPU // CPU
uint32_t totolLoad;
std::memcpy(&totolLoad, &buffer[pos], 4);
pos += 4;
m.cpu.totalLoad = networkToFloat(totolLoad);
uint8_t numCpu = buffer[pos++]; uint8_t numCpu = buffer[pos++];
if (pos + numCpu * 4 > buffer.size()) if (pos + numCpu * 4 > buffer.size())
throw std::runtime_error("Buffer too small for CPU"); throw std::runtime_error("Buffer too small for CPU");
for (int i = 0; i < numCpu; ++i) for (int i = 0; i < numCpu; ++i)
{ {
uint32_t tmp; uint32_t tmp;
std::memcpy(&tmp, &buffer[pos], 4); std::memcpy(&tmp, &buffer[pos], 4);
m.cpu.loads.push_back(networkToFloat(tmp)); m.cpu.coreLoads.push_back(networkToFloat(tmp));
pos += 4; pos += 4;
} }
+5 -2
View File
@@ -1,11 +1,14 @@
message(STATUS " Configuring Model") message(STATUS "·Configuring Model")
add_library(Model add_library(Model
HostRegistry.cpp HostRegistry.cpp
) )
add_library(Model::All ALIAS Model)
target_link_libraries(Model target_link_libraries(Model
PRIVATE Metrics PRIVATE
Metrics::All
) )
target_include_directories(Model target_include_directories(Model
+7 -15
View File
@@ -1,16 +1,8 @@
message(STATUS " Configuring Network") message(STATUS "·Configuring Network")
add_library(Network if (BUILD_SERVER)
Agent.cpp add_subdirectory(Server)
Client.cpp endif()
Server.cpp if (BUILD_CLIENT)
) add_subdirectory(Client)
endif()
target_link_libraries(Network
PRIVATE Model Metrics
)
target_include_directories(Network
PUBLIC
${INCLUDE_BASE_DIR}
)
@@ -1,4 +1,4 @@
#include "Network/Agent.h" #include "Network/Client/Agent.h"
#include <iostream> #include <iostream>
namespace Network namespace Network
@@ -8,7 +8,7 @@ namespace Network
Client client, Client client,
Metrics::Collector collector, Metrics::Collector collector,
std::chrono::milliseconds interval) 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() void Agent::start()
{ {
if (running_) if (running)
return; return;
running_ = true; running = true;
worker_ = std::thread(&Agent::run, this); worker = std::thread(&Agent::run, this);
} }
void Agent::stop() void Agent::stop()
{ {
running_ = false; running = false;
if (worker_.joinable()) if (worker.joinable())
worker_.join(); worker.join();
} }
void Agent::run() void Agent::run()
{ {
while (running_) while (running)
{ {
try try
{ {
auto metrics = collector_.collect(); auto metrics = collector.collect();
client_.sendMetrics(metrics); client.sendMetrics(metrics);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
std::cerr << "Agent error: " << e.what() << std::endl; std::cerr << "Agent error: " << e.what() << std::endl;
} }
std::this_thread::sleep_for(interval_); std::this_thread::sleep_for(interval);
} }
} }
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Network/Client.h" #include "Network/Client/Client.h"
#include "Metrics/Collector.h" #include "Metrics/Collector.h"
#include <thread> #include <thread>
@@ -26,12 +26,12 @@ namespace Network
private: private:
void run(); void run();
Client client_; Client client;
Metrics::Collector collector_; Metrics::Collector collector;
std::chrono::milliseconds interval_; std::chrono::milliseconds interval;
std::thread worker_; std::thread worker;
std::atomic<bool> running_{false}; std::atomic<bool> running{false};
}; };
} }
+16
View File
@@ -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}
)
@@ -1,4 +1,4 @@
#include "Network/Client.h" #include "Network/Client/Client.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
@@ -9,18 +9,18 @@ namespace Network
Client::Client(const std::string &host, uint16_t port) Client::Client(const std::string &host, uint16_t port)
{ {
sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd_ < 0) if (sockfd < 0)
{ {
perror("socket"); perror("socket");
throw std::runtime_error("Cannot create UDP socket"); throw std::runtime_error("Cannot create UDP socket");
} }
memset(&serverAddr_, 0, sizeof(serverAddr_)); memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr_.sin_family = AF_INET; serverAddr.sin_family = AF_INET;
serverAddr_.sin_port = htons(port); 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"); throw std::runtime_error("Invalid server IP");
} }
@@ -28,27 +28,27 @@ namespace Network
Client::~Client() Client::~Client()
{ {
if (sockfd_ >= 0) if (sockfd >= 0)
close(sockfd_); close(sockfd);
} }
Client::Client(Client &&other) noexcept Client::Client(Client &&other) noexcept
{ {
sockfd_ = other.sockfd_; sockfd = other.sockfd;
serverAddr_ = other.serverAddr_; serverAddr = other.serverAddr;
other.sockfd_ = -1; other.sockfd = -1;
} }
Client &Client::operator=(Client &&other) noexcept Client &Client::operator=(Client &&other) noexcept
{ {
if (this != &other) if (this != &other)
{ {
if (sockfd_ >= 0) if (sockfd >= 0)
close(sockfd_); close(sockfd);
sockfd_ = other.sockfd_; sockfd = other.sockfd;
serverAddr_ = other.serverAddr_; serverAddr = other.serverAddr;
other.sockfd_ = -1; other.sockfd = -1;
} }
return *this; return *this;
} }
@@ -56,8 +56,8 @@ namespace Network
bool Client::sendMetrics(const Metrics::Host &metrics) bool Client::sendMetrics(const Metrics::Host &metrics)
{ {
auto buffer = metrics.serialize(); auto buffer = metrics.serialize();
ssize_t sent = sendto(sockfd_, buffer.data(), buffer.size(), 0, ssize_t sent = sendto(sockfd, buffer.data(), buffer.size(), 0,
(struct sockaddr *)&serverAddr_, sizeof(serverAddr_)); (struct sockaddr *)&serverAddr, sizeof(serverAddr));
if (sent < 0) if (sent < 0)
{ {
perror("sendto"); perror("sendto");
@@ -22,8 +22,8 @@ namespace Network
bool sendMetrics(const Metrics::Host &metrics); bool sendMetrics(const Metrics::Host &metrics);
private: private:
int sockfd_; int sockfd;
struct sockaddr_in serverAddr_; struct sockaddr_in serverAddr;
}; };
} // namespace Network } // namespace Network
+16
View File
@@ -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}
)
@@ -1,4 +1,4 @@
#include "Network/Server.h" #include "Network/Server/Server.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #include <cstring>
-25
View File
@@ -1,25 +0,0 @@
#include <chrono>
#include <thread>
#include <iostream>
#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;
}