сделал страницу для разработчиков, добавил примитивный сбор журнала статистики
This commit is contained in:
@@ -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; // использование функций для разработчиков
|
||||
|
||||
/**
|
||||
* Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть.
|
||||
|
63
src/main.cpp
63
src/main.cpp
@@ -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;
|
||||
|
@@ -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{};
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user