From d5123ef0a2026066cc2fa44fc0d4ae3f972bb077 Mon Sep 17 00:00:00 2001 From: Vladislav Ostapov Date: Wed, 29 Jan 2025 15:15:26 +0300 Subject: [PATCH] =?UTF-8?q?=D1=81=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=D0=BE=D0=B2,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B8=D1=82=D0=B8=D0=B2=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D1=81=D0=B1=D0=BE=D1=80=20=D0=B6=D1=83=D1=80?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=D0=B0=20=D1=81=D1=82=D0=B0=D1=82=D0=B8=D1=81?= =?UTF-8?q?=D1=82=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devtool.py | 4 +- src/auth/resources.h | 1 + src/main.cpp | 63 +++++++++++--- src/terminal_api_driver.cpp | 140 +++++++++++++++++++++++++++++-- src/terminal_api_driver.h | 45 ++++++---- static/dev.html | 163 ++++++++++++++++++++++++++++-------- 6 files changed, 347 insertions(+), 69 deletions(-) diff --git a/devtool.py b/devtool.py index e7931e9..3cf2418 100755 --- a/devtool.py +++ b/devtool.py @@ -4,8 +4,8 @@ import sys import requests -USERNAME = "admin" -PASSWORD = "admin" +USERNAME = "developer" +PASSWORD = "fuckyou123" def do_login(base_url): diff --git a/src/auth/resources.h b/src/auth/resources.h index 8ce9677..5238b43 100644 --- a/src/auth/resources.h +++ b/src/auth/resources.h @@ -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; // использование функций для разработчиков /** * Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть. diff --git a/src/main.cpp b/src/main.cpp index 82d1ff8..4078bc2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -105,7 +105,16 @@ public: explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique()), api(std::make_unique()) { api->startDaemon(); - auth.users.emplace_back(std::make_shared("admin", "", http::auth::User::SUPERUSER)); + auth.users.emplace_back(std::make_shared("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("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(FIELDS_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); })); s.resources.emplace_back(std::make_unique(VUE_JS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); })); s.resources.emplace_back(std::make_unique(INTERNET_JPG, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(INTERNET_JPG, rep); })); - s.resources.emplace_back(std::make_unique("/dev", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(DEV_HTML, rep); })); s.resources.emplace_back(std::make_unique("/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("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { + s.resources.emplace_back(std::make_unique("/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("/dev", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { + s.resources.emplace_back(std::make_unique("/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("/dev/cpapicall", this->auth, http::auth::User::SUPERUSER, [this](const http::server::Request& req, auto& rep) { + s.resources.emplace_back(std::make_unique("/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("/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("/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("/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; diff --git a/src/terminal_api_driver.cpp b/src/terminal_api_driver.cpp index bab0fba..0b3ac93 100644 --- a/src/terminal_api_driver.cpp +++ b/src/terminal_api_driver.cpp @@ -10,7 +10,7 @@ #include #include - +#define TIME_NOW() std::chrono::time_point_cast(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 logPeriodMs = 1000; + std::atomic 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("en"); + const int newInterval = pt.get("logPeriodMs"); + const int newMaxAgeMs = pt.get("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(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(out.length())); + this->logFile.flush(); + } + } + + // void collectExpiredItems(); + + // void resetLogs() { + // std::lock_guard _lock(mutex); + // logs.clear(); + // } + + ~StatisticsLogger() = default; +private: + // std::pmr::deque 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::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::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& 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{}; diff --git a/src/terminal_api_driver.h b/src/terminal_api_driver.h index d4418c1..3b9ec44 100644 --- a/src/terminal_api_driver.h +++ b/src/terminal_api_driver.h @@ -1,6 +1,7 @@ #ifndef TERMINAL_API_DRIVER_H #define TERMINAL_API_DRIVER_H +#include #include #include #include @@ -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& callback); + void executeInApi(const std::function &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& 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 diff --git a/static/dev.html b/static/dev.html index 7550adf..edfd153 100644 --- a/static/dev.html +++ b/static/dev.html @@ -22,9 +22,6 @@ padding-top: var(--header-height); } - .l3-proto-label { - margin: 0 0 0 0.5em; - } .l3-proto-label > * { display: inline-block; } @@ -36,64 +33,164 @@ - - +