сделал страницу для разработчиков, добавил примитивный сбор журнала статистики

This commit is contained in:
2025-01-29 15:15:26 +03:00
parent f9b919facf
commit d5123ef0a2
6 changed files with 347 additions and 69 deletions

View File

@@ -51,6 +51,7 @@ namespace http::auth {
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
static constexpr uint32_t SETUP_QOS = 0x0040; // управление профилем QoS
static constexpr uint32_t DEVELOPER = 0x0080; // использование функций для разработчиков
/**
* Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть.

View File

@@ -105,7 +105,16 @@ public:
explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
api->startDaemon();
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER));
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "",
http::auth::User::WATCH_STATISTICS |
http::auth::User::RESET_PACKET_STATISTICS |
http::auth::User::WATCH_SETTINGS |
http::auth::User::EDIT_SETTINGS |
http::auth::User::UPDATE_FIRMWARE |
http::auth::User::SETUP_QOS));
// пароль fuckyou123
auth.users.emplace_back(std::make_shared<http::auth::User>("developer", "10628cfc434fb87f31d675d37e0402c2d824cfe8393aff7a61ee57aaa7d909c3", http::auth::User::SUPERUSER));
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
#ifdef USE_DEBUG
@@ -177,7 +186,6 @@ public:
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FIELDS_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(VUE_JS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(INTERNET_JPG, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(INTERNET_JPG, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/dev", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(DEV_HTML, rep); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) {
if (req.method != "GET") {
@@ -386,7 +394,7 @@ public:
this->upgradeOrRebootRunning = true;
system(REBOOT_COMMAND);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
return;
@@ -432,12 +440,12 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
boost::ignore_unused(req);
sf->serve(DEV_HTML, rep);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/cpapicall", this->auth, http::auth::User::SUPERUSER, [this](const http::server::Request& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/cpapicall", this->auth, http::auth::User::DEVELOPER, [this](const http::server::Request& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
return;
@@ -473,13 +481,44 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
// s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/fetchParams", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
// rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
// std::string result = R"({"status":"ok","fwsize":)";
// result += std::to_string(req.payload.size());
// result += R"(,"sha256":")";
// result += "\"}";
// }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/settings", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
std::string result;
if (req.method == "GET") {
result = R"({"status":"ok","logstat":)";
result += this->api->getLoggingStatisticsSettings();
result += "}";
} else if (req.method == "POST") {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setLoggingStatisticsSettings(pt);
result = R"({"status":"ok","logstat":)";
result += this->api->getLoggingStatisticsSettings();
result += "}";
} else {
http::server::stockReply(http::server::bad_request, rep);
return;
}
rep.status = http::server::ok;
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/logs.csv", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
return;
}
rep.status = http::server::ok;
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_plain)});
rep.content.clear();
http::resource::loadFile("/tmp/weblog-statistics.csv", rep.content);
}));
}
~ServerResources() = default;

View File

@@ -10,7 +10,7 @@
#include <boost/property_tree/json_parser.hpp>
#include <sys/sysinfo.h>
#define TIME_NOW() std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count()
typedef boost::property_tree::ptree::path_type json_path;
@@ -64,6 +64,9 @@ static inline void rtrim(std::string &s) {
return !std::isspace(ch);
}).base(), s.end());
}
static const char* boolAsStr(bool value) {
return value ? "true" : "false";
}
class TerminalNetworkSettings {
public:
@@ -110,6 +113,104 @@ static std::ostream& operator<<(std::ostream& out, CP_Result result) {
return out;
}
class api_driver::StatisticsLogger {
public:
StatisticsLogger(): timeStart(TIME_NOW()) {}
int64_t timeStart;
bool logEn = false;
std::atomic<int> logPeriodMs = 1000;
std::atomic<int> maxAgeMs = 10000;
/**
*
* @return {"en": bool, "logPeriodMs": int, ""}
*/
std::string getSettings() {
std::lock_guard _lock(mutex);
std::stringstream res;
res << "{\"en\":" << boolAsStr(this->logEn);
res << ",\"logPeriodMs\":" << logPeriodMs;
res << ",\"maxAgeMs\":" << maxAgeMs;
res << '}';
return res.str();
}
void setSettings(boost::property_tree::ptree &pt) {
const bool newEn = pt.get<bool>("en");
const int newInterval = pt.get<int>("logPeriodMs");
const int newMaxAgeMs = pt.get<int>("maxAgeMs");
std::lock_guard _lock(this->mutex);
this->logPeriodMs = newInterval;
this->maxAgeMs = newMaxAgeMs;
if (newEn != this->logEn) {
if (newEn) {
this->logFile.open("/tmp/weblog-statistics.csv", std::ios::out);
if (this->logFile.is_open()) {
const auto* header = "timestamp\tpkt ok\tpkt bad\tfine freq dem\tcrs freq dem\tcrs freq compensator\tcrs time est\tfine time est\tmax level corr\torigin delay\tSNR\tmodcod\tfine freq compensator\tind freq grb\tind freq tochn\tind filt adapt\tfilter corr cinc\tcorr cnt\n";
this->logFile.write(header, static_cast<std::streamsize>(strlen(header)));
this->logEn = true;
this->timeStart = TIME_NOW();
}
} else {
if (this->logFile.is_open()) {
this->logFile.close();
}
this->logEn = false;
}
}
}
/**
* Записать значение в "базу данных". Метку при этом вставлять не нужно, она будет вставлена автоматически.
* @param item
*/
void putItem(const debug_metrics& item) {
std::lock_guard _lock(this->mutex);
if (!logEn) return;
if (this->logFile.is_open()) {
std::stringstream res;
res << TIME_NOW() - this->timeStart << '\t';
res << item.cnt_ok << '\t';
res << item.cnt_bad << '\t';
res << item.fine_freq_dem << '\t';
res << item.crs_freq_dem << '\t';
res << item.crs_freq_compensator << '\t';
res << item.crs_time_est << '\t';
res << item.fine_time_est << '\t';
res << item.max_level_corr << '\t';
res << item.origin_delay << '\t';
res << item.SNR << '\t';
res << item.current_modcod << '\t';
res << item.fine_freq_compensator << '\t';
res << item.ind_freq_grb << '\t';
res << item.ind_freq_tochn << '\t';
res << item.ind_filt_adapt << '\t';
res << item.filter_corr_cinc << '\t';
res << item.corr_cnt << '\n';
const auto out = res.str();
this->logFile.write(out.c_str(), static_cast<std::streamsize>(out.length()));
this->logFile.flush();
}
}
// void collectExpiredItems();
// void resetLogs() {
// std::lock_guard _lock(mutex);
// logs.clear();
// }
~StatisticsLogger() = default;
private:
// std::pmr::deque<LogItem> logs;
std::fstream logFile{};
std::shared_mutex mutex;
};
/**
* Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки
*/
@@ -140,6 +241,8 @@ private:
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmpDevState));
#endif
#ifdef MODEM_IS_SCPC
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDebugMetrics()", CP_GetDebugMetrics(sid, debugMetrics));
bool isCinC = getIsCinC();
if (isCinC) {
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetCinCState()", CP_GetCinCState(sid, cinc));
@@ -306,6 +409,22 @@ private:
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateState(): " << e.what();
}
// запись статистики в лог, если нужно
try {
static int uc = 0;
if (uc == 0) {
this->statsLogs.putItem(debugMetrics);
BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateState(): success write statistics state to log!";
}
uc++;
if (uc * CACHE_STATISTICS_UPDATE_MS >= this->statsLogs.logPeriodMs) {
uc = 0;
}
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateState() failed to log: " << e.what();
}
}},
// обновление кеша настроек
{.lastUpdate = 0, .periodMs = CACHE_SETTINGS_UPDATE_MS, .callback = [this]() {
@@ -344,12 +463,12 @@ private:
}
int64_t sleepTime = 60000; // минута по-умолчанию
auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
auto now = TIME_NOW();
for (auto& u: updaters) {
if (u.checkNeedUpdate(now)) {
u.lastUpdate = now;
u.callback();
now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
now = TIME_NOW();
}
sleepTime = std::min(sleepTime, u.getNextUpdate(now));
@@ -366,6 +485,7 @@ private:
demodulator_state demodState{};
device_state devState{};
std::string deviceInitState;
debug_metrics debugMetrics{};
#ifdef MODEM_IS_SCPC
CinC_state cincState{};
#endif
@@ -390,6 +510,8 @@ private:
TerminalFirmwareVersion firmware;
public:
StatisticsLogger statsLogs;
std::mutex cpApiMutex;
TSID sid;
boost::thread daemon;
@@ -657,10 +779,6 @@ void api_driver::ApiDriver::startDaemon() {
}
}
static const char* boolAsStr(bool value) {
return value ? "true" : "false";
}
std::string api_driver::buildEscapedString(const std::string& source) {
std::string str(source);
size_t start_pos = 0;
@@ -1174,6 +1292,14 @@ void api_driver::ApiDriver::executeInApi(const std::function<void(TSID sid)>& ca
}
}
std::string api_driver::ApiDriver::getLoggingStatisticsSettings() {
return this->daemon->statsLogs.getSettings();
}
void api_driver::ApiDriver::setLoggingStatisticsSettings(boost::property_tree::ptree &pt) {
this->daemon->statsLogs.setSettings(pt);
}
std::string api_driver::ApiDriver::loadSysInfo() {
std::stringstream result;
struct sysinfo info{};

View File

@@ -1,6 +1,7 @@
#ifndef TERMINAL_API_DRIVER_H
#define TERMINAL_API_DRIVER_H
#include <deque>
#include <memory>
#include <string>
#include <boost/property_tree/ptree.hpp>
@@ -12,6 +13,7 @@ namespace api_driver {
constexpr int CACHE_SETTINGS_UPDATE_MS = 5000;
constexpr int CACHE_QOS_UPDATE_MS = 5000;
class StatisticsLogger;
class TerminalApiDaemon;
/**
@@ -42,31 +44,44 @@ namespace api_driver {
std::string loadFirmwareVersion() const;
/**
* Установить настройки RX/TX, readback можно получить используя loadTerminalState
*/
* Установить настройки RX/TX, readback можно получить используя loadTerminalState
*/
void setRxTxSettings(boost::property_tree::ptree &pt);
#ifdef MODEM_IS_SCPC
/**
* Установить настройки CinC, readback можно получить используя loadTerminalState.
*/
* Установить настройки CinC, readback можно получить используя loadTerminalState.
*/
void setCincSettings(boost::property_tree::ptree &pt);
#endif
/**
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
*/
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
*/
void setBucLnbSettings(boost::property_tree::ptree &pt);
/**
* Установить настройки QoS, readback можно получить используя loadTerminalState.
*/
* Установить настройки QoS, readback можно получить используя loadTerminalState.
*/
void setQosSettings(boost::property_tree::ptree &pt);
void setNetworkSettings(boost::property_tree::ptree & pt);
void setNetworkSettings(boost::property_tree::ptree &pt);
void resetDefaultSettings();
void executeInApi(const std::function<void(TSID sid)>& callback);
void executeInApi(const std::function<void(TSID sid)> &callback);
std::string getLoggingStatisticsSettings();
void setLoggingStatisticsSettings(boost::property_tree::ptree &pt);
/**
* Получить статистику в формате json. Выход будет дописан в вектор
* @param jsonOut вектор, куда должен быть записан результат. Данные будут дописаны к существующим, формат: []
* @param timeStart
* @param timeEnd
* @param ordering
* @param maxItems
*/
void readLoggingStatistics(std::vector<uint8_t>& out, int timeStart = -1, int timeEnd = -1, bool ordering = false, int maxItems = -1);
static std::string loadSysInfo();
@@ -77,11 +92,11 @@ namespace api_driver {
};
/**
* Функция для создания экранированной строки (для json)
* @param source исходная строка (например, {123"})
* @return {"123\""}
*/
std::string buildEscapedString(const std::string& source);
* Функция для создания экранированной строки (для json)
* @param source исходная строка (например, {123"})
* @return {"123\""}
*/
std::string buildEscapedString(const std::string &source);
}
#endif //TERMINAL_API_DRIVER_H