Step 2
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
+9
-53
@@ -1,58 +1,14 @@
|
|||||||
# Добавляем все модули как поддиректории
|
# Добавляем все модули как поддиректории
|
||||||
|
|
||||||
|
if (BUILD_SERVER OR BUILD_CLIENT)
|
||||||
|
if (BUILD_SERVER)
|
||||||
add_subdirectory(Display)
|
add_subdirectory(Display)
|
||||||
add_subdirectory(Helpers)
|
|
||||||
add_subdirectory(Metrics)
|
|
||||||
add_subdirectory(Model)
|
add_subdirectory(Model)
|
||||||
|
endif()
|
||||||
|
add_subdirectory(Helpers)
|
||||||
|
add_subdirectory(Config)
|
||||||
|
add_subdirectory(Metrics)
|
||||||
add_subdirectory(Network)
|
add_subdirectory(Network)
|
||||||
|
|
||||||
set(TARGET_NAME_SERVER ${PROJECT_NAME}-Server)
|
add_subdirectory(Main)
|
||||||
message(STATUS "Configuring ${TARGET_NAME_SERVER}")
|
endif()
|
||||||
|
|
||||||
# Основной исполняемый файл
|
|
||||||
add_executable(${TARGET_NAME_SERVER} Server.cpp)
|
|
||||||
|
|
||||||
# Линкуем зависимости
|
|
||||||
target_link_libraries(${TARGET_NAME_SERVER}
|
|
||||||
PRIVATE
|
|
||||||
Display
|
|
||||||
Helpers
|
|
||||||
Metrics
|
|
||||||
Model
|
|
||||||
Network
|
|
||||||
)
|
|
||||||
|
|
||||||
# Указываем корень include для dashboard
|
|
||||||
target_include_directories(${TARGET_NAME_SERVER}
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "${TARGET_NAME_SERVER} summary")
|
|
||||||
message(STATUS "")
|
|
||||||
|
|
||||||
set(TARGET_NAME_CLIENT ${PROJECT_NAME}-Client)
|
|
||||||
message(STATUS "Configuring ${TARGET_NAME_CLIENT}")
|
|
||||||
|
|
||||||
# Основной исполняемый файл
|
|
||||||
add_executable(${TARGET_NAME_CLIENT} Client.cpp)
|
|
||||||
|
|
||||||
# Линкуем зависимости
|
|
||||||
target_link_libraries(${TARGET_NAME_CLIENT}
|
|
||||||
PRIVATE
|
|
||||||
Helpers
|
|
||||||
Metrics
|
|
||||||
Network
|
|
||||||
)
|
|
||||||
|
|
||||||
# Указываем корень include для dashboard
|
|
||||||
target_include_directories(${TARGET_NAME_CLIENT}
|
|
||||||
PRIVATE
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
message(STATUS "")
|
|
||||||
message(STATUS "${TARGET_NAME_CLIENT} summary")
|
|
||||||
message(STATUS "")
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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 §ion,
|
||||||
|
const std::string &key,
|
||||||
|
const std::string &def) const
|
||||||
|
{
|
||||||
|
auto s = data_.find(section);
|
||||||
|
if (s == data_.end())
|
||||||
|
return def;
|
||||||
|
auto k = s->second.find(key);
|
||||||
|
if (k == s->second.end())
|
||||||
|
return def;
|
||||||
|
return k->second;
|
||||||
|
}
|
||||||
@@ -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 §ion,
|
||||||
|
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_;
|
||||||
|
};
|
||||||
@@ -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
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Config::Server
|
||||||
|
{
|
||||||
|
struct TextStyleConfig
|
||||||
|
{
|
||||||
|
std::string fontPath;
|
||||||
|
int fontSize = 10;
|
||||||
|
};
|
||||||
|
} // namespace Config::Server
|
||||||
@@ -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}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -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}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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 y = 0; y < logical.height; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < logical.width; ++x)
|
||||||
|
{
|
||||||
|
int fx = 0;
|
||||||
|
int fy = 0;
|
||||||
|
|
||||||
|
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::fillRect(int x, int y, int w, int h, const Color &color)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < h; ++j)
|
for (int j = 0; j < h; ++j)
|
||||||
{
|
|
||||||
int py = y + j;
|
|
||||||
if (py < 0 || py >= height_)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint16_t *row = reinterpret_cast<uint16_t *>(fb_ + py * line_len_);
|
|
||||||
for (int i = 0; i < w; ++i)
|
for (int i = 0; i < w; ++i)
|
||||||
{
|
setPixel(x + i, y + j, color);
|
||||||
int px = x + i;
|
|
||||||
if (px < 0 || px >= width_)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
row[px] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Framebuffer::drawRect(int x, int y, int w, int h, const Color &c)
|
void Framebuffer::drawRect(int x, int y, int w, int h, const Color &color)
|
||||||
{
|
{
|
||||||
// верх
|
// верх
|
||||||
fillRect(x, y, w, 1, c);
|
fillRect(x, y, w, 1, color);
|
||||||
// низ
|
// низ
|
||||||
fillRect(x, y + h - 1, w, 1, c);
|
fillRect(x, y + h - 1, w, 1, color);
|
||||||
// лево
|
// лево
|
||||||
fillRect(x, y, 1, h, c);
|
fillRect(x, y, 1, h, color);
|
||||||
// право
|
// право
|
||||||
fillRect(x + w - 1, y, 1, h, c);
|
fillRect(x + w - 1, y, 1, h, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Framebuffer::setPixel(int x, int y, const Color &c)
|
void Framebuffer::setPixel(int x, int y, const Color &color)
|
||||||
{
|
{
|
||||||
if (x < 0 || x >= width_ || y < 0 || y >= height_)
|
if (x < 0 || x >= logical.width || y < 0 || y >= logical.height)
|
||||||
return;
|
return;
|
||||||
|
buffer[y * logical.width + x] = rgb565(color);
|
||||||
uint16_t *p = reinterpret_cast<uint16_t *>(fb_ + y * line_len_) + x;
|
|
||||||
*p = ((c.r & 0xF8) << 8) | ((c.g & 0xFC) << 3) | (c.b >> 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -4,38 +4,40 @@ namespace Display::Graphics
|
|||||||
{
|
{
|
||||||
Renderer::Renderer(Framebuffer &framebuffer, Model::HostRegistry ®istry)
|
Renderer::Renderer(Framebuffer &framebuffer, Model::HostRegistry ®istry)
|
||||||
: 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
|
||||||
|
|||||||
@@ -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 ®istry;
|
Model::HostRegistry ®istry;
|
||||||
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Display::UI::Bar
|
||||||
|
{
|
||||||
|
enum class Orientation
|
||||||
|
{
|
||||||
|
Horizontal,
|
||||||
|
Vertical
|
||||||
|
};
|
||||||
|
} // namespace Display::UI::Bar
|
||||||
@@ -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
|
||||||
@@ -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}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
message(STATUS "·Configuring Main")
|
||||||
|
|
||||||
|
if (BUILD_SERVER)
|
||||||
|
add_subdirectory(Server)
|
||||||
|
endif()
|
||||||
|
if (BUILD_CLIENT)
|
||||||
|
add_subdirectory(Client)
|
||||||
|
endif()
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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();
|
||||||
|
|
||||||
@@ -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}
|
||||||
|
)
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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>
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user