сделал страницу для разработчиков, добавил примитивный сбор журнала статистики
This commit is contained in:
parent
f9b919facf
commit
d5123ef0a2
@ -4,8 +4,8 @@ import sys
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
USERNAME = "admin"
|
USERNAME = "developer"
|
||||||
PASSWORD = "admin"
|
PASSWORD = "fuckyou123"
|
||||||
|
|
||||||
|
|
||||||
def do_login(base_url):
|
def do_login(base_url):
|
||||||
|
@ -51,6 +51,7 @@ namespace http::auth {
|
|||||||
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
|
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
|
||||||
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
|
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
|
||||||
static constexpr uint32_t SETUP_QOS = 0x0040; // управление профилем QoS
|
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>()) {
|
explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
|
||||||
api->startDaemon();
|
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);
|
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
|
||||||
#ifdef USE_DEBUG
|
#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>(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>(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>(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) {
|
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") {
|
if (req.method != "GET") {
|
||||||
@ -386,7 +394,7 @@ public:
|
|||||||
this->upgradeOrRebootRunning = true;
|
this->upgradeOrRebootRunning = true;
|
||||||
system(REBOOT_COMMAND);
|
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") {
|
if (req.method != "POST") {
|
||||||
http::server::stockReply(http::server::bad_request, rep);
|
http::server::stockReply(http::server::bad_request, rep);
|
||||||
return;
|
return;
|
||||||
@ -432,12 +440,12 @@ public:
|
|||||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
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);
|
boost::ignore_unused(req);
|
||||||
sf->serve(DEV_HTML, rep);
|
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") {
|
if (req.method != "POST") {
|
||||||
http::server::stockReply(http::server::bad_request, rep);
|
http::server::stockReply(http::server::bad_request, rep);
|
||||||
return;
|
return;
|
||||||
@ -473,13 +481,44 @@ public:
|
|||||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
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) {
|
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/settings", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
|
||||||
// rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
std::string result;
|
||||||
// std::string result = R"({"status":"ok","fwsize":)";
|
if (req.method == "GET") {
|
||||||
// result += std::to_string(req.payload.size());
|
result = R"({"status":"ok","logstat":)";
|
||||||
// result += R"(,"sha256":")";
|
result += this->api->getLoggingStatisticsSettings();
|
||||||
// result += "\"}";
|
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;
|
~ServerResources() = default;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <boost/property_tree/json_parser.hpp>
|
#include <boost/property_tree/json_parser.hpp>
|
||||||
#include <sys/sysinfo.h>
|
#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;
|
typedef boost::property_tree::ptree::path_type json_path;
|
||||||
|
|
||||||
@ -64,6 +64,9 @@ static inline void rtrim(std::string &s) {
|
|||||||
return !std::isspace(ch);
|
return !std::isspace(ch);
|
||||||
}).base(), s.end());
|
}).base(), s.end());
|
||||||
}
|
}
|
||||||
|
static const char* boolAsStr(bool value) {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
class TerminalNetworkSettings {
|
class TerminalNetworkSettings {
|
||||||
public:
|
public:
|
||||||
@ -110,6 +113,104 @@ static std::ostream& operator<<(std::ostream& out, CP_Result result) {
|
|||||||
return out;
|
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, а так же корректно сохранять настройки
|
* Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки
|
||||||
*/
|
*/
|
||||||
@ -140,6 +241,8 @@ private:
|
|||||||
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmpDevState));
|
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmpDevState));
|
||||||
#endif
|
#endif
|
||||||
#ifdef MODEM_IS_SCPC
|
#ifdef MODEM_IS_SCPC
|
||||||
|
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDebugMetrics()", CP_GetDebugMetrics(sid, debugMetrics));
|
||||||
|
|
||||||
bool isCinC = getIsCinC();
|
bool isCinC = getIsCinC();
|
||||||
if (isCinC) {
|
if (isCinC) {
|
||||||
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetCinCState()", CP_GetCinCState(sid, cinc));
|
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetCinCState()", CP_GetCinCState(sid, cinc));
|
||||||
@ -306,6 +409,22 @@ private:
|
|||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateState(): " << e.what();
|
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]() {
|
{.lastUpdate = 0, .periodMs = CACHE_SETTINGS_UPDATE_MS, .callback = [this]() {
|
||||||
@ -344,12 +463,12 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t sleepTime = 60000; // минута по-умолчанию
|
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) {
|
for (auto& u: updaters) {
|
||||||
if (u.checkNeedUpdate(now)) {
|
if (u.checkNeedUpdate(now)) {
|
||||||
u.lastUpdate = now;
|
u.lastUpdate = now;
|
||||||
u.callback();
|
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));
|
sleepTime = std::min(sleepTime, u.getNextUpdate(now));
|
||||||
@ -366,6 +485,7 @@ private:
|
|||||||
demodulator_state demodState{};
|
demodulator_state demodState{};
|
||||||
device_state devState{};
|
device_state devState{};
|
||||||
std::string deviceInitState;
|
std::string deviceInitState;
|
||||||
|
debug_metrics debugMetrics{};
|
||||||
#ifdef MODEM_IS_SCPC
|
#ifdef MODEM_IS_SCPC
|
||||||
CinC_state cincState{};
|
CinC_state cincState{};
|
||||||
#endif
|
#endif
|
||||||
@ -390,6 +510,8 @@ private:
|
|||||||
TerminalFirmwareVersion firmware;
|
TerminalFirmwareVersion firmware;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
StatisticsLogger statsLogs;
|
||||||
|
|
||||||
std::mutex cpApiMutex;
|
std::mutex cpApiMutex;
|
||||||
TSID sid;
|
TSID sid;
|
||||||
boost::thread daemon;
|
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 api_driver::buildEscapedString(const std::string& source) {
|
||||||
std::string str(source);
|
std::string str(source);
|
||||||
size_t start_pos = 0;
|
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::string api_driver::ApiDriver::loadSysInfo() {
|
||||||
std::stringstream result;
|
std::stringstream result;
|
||||||
struct sysinfo info{};
|
struct sysinfo info{};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef TERMINAL_API_DRIVER_H
|
#ifndef TERMINAL_API_DRIVER_H
|
||||||
#define TERMINAL_API_DRIVER_H
|
#define TERMINAL_API_DRIVER_H
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
@ -12,6 +13,7 @@ namespace api_driver {
|
|||||||
constexpr int CACHE_SETTINGS_UPDATE_MS = 5000;
|
constexpr int CACHE_SETTINGS_UPDATE_MS = 5000;
|
||||||
constexpr int CACHE_QOS_UPDATE_MS = 5000;
|
constexpr int CACHE_QOS_UPDATE_MS = 5000;
|
||||||
|
|
||||||
|
class StatisticsLogger;
|
||||||
class TerminalApiDaemon;
|
class TerminalApiDaemon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,6 +70,19 @@ namespace api_driver {
|
|||||||
|
|
||||||
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();
|
static std::string loadSysInfo();
|
||||||
|
|
||||||
~ApiDriver();
|
~ApiDriver();
|
||||||
|
163
static/dev.html
163
static/dev.html
@ -22,9 +22,6 @@
|
|||||||
padding-top: var(--header-height);
|
padding-top: var(--header-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
.l3-proto-label {
|
|
||||||
margin: 0 0 0 0.5em;
|
|
||||||
}
|
|
||||||
.l3-proto-label > * {
|
.l3-proto-label > * {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
@ -36,64 +33,164 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="app" hidden>
|
<div id="app" hidden>
|
||||||
<header>
|
<header>
|
||||||
<span class="nav-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === false, indicator_good: stat_rx.state === true, indicator: true }"></span></span>
|
|
||||||
<span :class="{ value_bad: initState !== 'Успешная инициализация системы' }">{{ initState }}</span>
|
|
||||||
<div class="tabs-header">
|
<div class="tabs-header">
|
||||||
<span style="font-weight:bold">RSCM-101</span>
|
<span style="font-weight:bold">RSCM-101</span>
|
||||||
<a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a>
|
<a href="/#monitoring" class="tabs-btn">Мониторинг</a>
|
||||||
<a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a>
|
<a href="/#setup" class="tabs-btn">Настройки</a>
|
||||||
<a href="#qos" class="tabs-btn" @click="activeTab = 'qos'" :class="{ active: activeTab === 'qos' }">QoS</a>
|
<a href="/#qos" class="tabs-btn">QoS</a>
|
||||||
<a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
|
<a href="/#admin" class="tabs-btn">Администрирование</a>
|
||||||
|
<a class="tabs-btn active">Разработчикам</a>
|
||||||
<a href="/logout" class="tabs-btn">Выход</a>
|
<a href="/logout" class="tabs-btn">Выход</a>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<p>Прием: <span :class="{ indicator_bad: stat_rx.state === false, indicator_good: stat_rx.state === true, indicator: true }"></span></p>
|
|
||||||
<p>Скорость: <span>{{ stat_rx.speedOnRxKbit }}</span></p>
|
|
||||||
|
|
||||||
<h2>Настройки</h2>
|
|
||||||
<div class="settings-set-container">
|
<div class="settings-set-container">
|
||||||
|
<h2>Настройки записи пакетов в IPS</h2>
|
||||||
<label>
|
<label>
|
||||||
<span>Режим работы</span>
|
<span>Активировать запись</span>
|
||||||
<select v-model="param.general.isCinC">
|
<span class="toggle-input"><input type="checkbox" v-model="params.loggingIps" /><span class="slider"></span></span>
|
||||||
<option :value="false">SCPC</option>
|
</label>
|
||||||
<option :value="true">CinC</option>
|
<button class="action-button" @click="settingsSubmitLoggingIps()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingIps"></span></button>
|
||||||
|
</div>
|
||||||
|
<div class="settings-set-container" v-if="settingFetchComplete">
|
||||||
|
<h2>Настройки журналирования статистики</h2>
|
||||||
|
<label>
|
||||||
|
<span>Активировать запись</span>
|
||||||
|
<span class="toggle-input"><input type="checkbox" v-model="params.loggingStatistics.en" /><span class="slider"></span></span>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Период сбора статистики</span>
|
||||||
|
<select v-model="params.loggingStatistics.logPeriodMs">
|
||||||
|
<option :value="30000">30s</option>
|
||||||
|
<option :value="10000">10s</option>
|
||||||
|
<option :value="5000">5s</option>
|
||||||
|
<option :value="2000">2s</option>
|
||||||
|
<option :value="1000">1s</option>
|
||||||
|
<option :value="500">500ms</option>
|
||||||
|
<option :value="250">250ms (!)</option>
|
||||||
|
<option :value="50">FULL SPEED (!)</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Полоса поиска, кгц ±</span>
|
<span>Время хранения</span>
|
||||||
<input v-model="param.num" type="number" min="0" max="100" step="1"/>
|
<select v-model="params.loggingStatistics.maxAgeMs">
|
||||||
|
<option :value="60000">1 мин</option>
|
||||||
|
<option :value="300000">5 мин</option>
|
||||||
|
<option :value="900000">15 мин</option>
|
||||||
|
<option :value="1800000">30 мин</option>
|
||||||
|
<option :value="3600000">1 час</option>
|
||||||
|
<option :value="7200000">2 часа</option>
|
||||||
|
<option :value="21600000">6 часов</option>
|
||||||
|
<option :value="43200000">12 часов</option>
|
||||||
|
<option :value="86400000">24 часа</option>
|
||||||
|
<option :value="172800000">48 часов</option>
|
||||||
|
<option :value="604800000">7 суток</option>
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
<button class="action-button" @click="settingsSubmitLoggingStatistics()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingIps"></span></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-set-container">
|
||||||
|
<h2>Просмотр логов</h2>
|
||||||
|
<button class="action-button" @click="logView()">Обновить <span class="submit-spinner" v-show="submitStatus.logView"></span></button>
|
||||||
|
<a href="/dev/logs.csv" class="action-button">Скачать</a>
|
||||||
|
<pre style="overflow-x: auto">{{ logfileContent }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<script src="/js/vue.js"></script>
|
<script src="/js/vue.js?v=3.5.13"></script>
|
||||||
<script>
|
<script>
|
||||||
|
// для обновления высоты хидера
|
||||||
|
function updateHeaderHeight() { const header = document.querySelector('header'); document.body.style.setProperty('--header-height', `${header.offsetHeight}px`); }
|
||||||
|
window.addEventListener('load', updateHeaderHeight); window.addEventListener('resize', updateHeaderHeight);
|
||||||
|
|
||||||
const app = Vue.createApp({
|
const app = Vue.createApp({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
stat_rx: {
|
// false - означает что статистика не отправляется, true - отправляется
|
||||||
state: false,
|
submitStatus: {
|
||||||
|
loggingIps: false,
|
||||||
|
loggingStatistics: false,
|
||||||
|
logView: false
|
||||||
},
|
},
|
||||||
param: {
|
params: {
|
||||||
general: {
|
loggingIps: false,
|
||||||
isCinC: true,
|
loggingStatistics: {
|
||||||
isTestInputData: false,
|
en: false,
|
||||||
modulatorMode: 'test'
|
logPeriodMs: 1000,
|
||||||
},
|
maxAgeMs: 0
|
||||||
num: 1
|
}
|
||||||
},
|
},
|
||||||
|
logfileContent: "",
|
||||||
|
|
||||||
initState: '?',
|
settingFetchComplete: false
|
||||||
activeTab: 'setup'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
settingsSubmitLoggingIps() {
|
||||||
|
if (this.submitStatus.loggingIps) { return }
|
||||||
|
|
||||||
|
this.submitStatus.loggingIps = true
|
||||||
|
fetch(`/dev/cpapicall?f=SetDmaDebug¶m=log_bool&value=${this.params.loggingIps}`, {method: 'POST', headers: {'Content-Type': 'application/json'}, credentials: 'same-origin' })
|
||||||
|
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } })
|
||||||
|
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
|
||||||
|
.finally(() => { this.submitStatus.loggingIps = false })
|
||||||
|
},
|
||||||
|
settingsSubmitLoggingStatistics() {
|
||||||
|
function modcodToStr(modcod) {
|
||||||
|
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
|
||||||
|
const modcods = [
|
||||||
|
"DUMMY",
|
||||||
|
"QPSK 1/4", "QPSK 1/3", "QPSK 2/5", "QPSK 1/2", "QPSK 3/5", "QPSK 2/3", "QPSK 3/4", "QPSK 4/5", "QPSK 5/6", "QPSK 8/9", "QPSK 9/10",
|
||||||
|
"8PSK 3/5", "8PSK 2/3", "8PSK 3/4", "8PSK 5/6", "8PSK 8/9", "8PSK 9/10",
|
||||||
|
"16APSK 2/3", "16APSK 3/4", "16APSK 4/5", "16APSK 5/6", "16APSK 8/9", "16APSK 9/10",
|
||||||
|
"32APSK 3/4", "32APSK 4/5", "32APSK 5/6", "32APSK 8/9", "32APSK 9/10",
|
||||||
|
]
|
||||||
|
if (typeof modcod != "number") {
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
if (modcod < 0 || modcod >= modcods.length) {
|
||||||
|
return `? (${modcod})`
|
||||||
|
}
|
||||||
|
return modcods[modcod]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.submitStatus.loggingStatistics) { return }
|
||||||
|
|
||||||
|
let query = {
|
||||||
|
"en": this.params.loggingStatistics.en,
|
||||||
|
"logPeriodMs": this.params.loggingStatistics.logPeriodMs,
|
||||||
|
"maxAgeMs": this.params.loggingStatistics.maxAgeMs,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.submitStatus.loggingStatistics = true
|
||||||
|
fetch('/dev/settings', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query), credentials: 'same-origin' })
|
||||||
|
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateLoggingStatisticsSettings(vals) })
|
||||||
|
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
|
||||||
|
.finally(() => { this.submitStatus.loggingStatistics = false })
|
||||||
|
},
|
||||||
|
logView() {
|
||||||
|
if (this.submitStatus.logView) { return }
|
||||||
|
|
||||||
|
this.submitStatus.logView = true
|
||||||
|
fetch(`/dev/logs.csv`, {method: 'GET', credentials: 'same-origin' })
|
||||||
|
.then(async (resp) => { this.logfileContent = await resp.text() })
|
||||||
|
.catch((reason) => { alert(`Ошибка при чтении логов: ${reason}`) })
|
||||||
|
.finally(() => { this.submitStatus.logView = false })
|
||||||
|
},
|
||||||
|
updateLoggingStatisticsSettings(vals) {
|
||||||
|
this.params.loggingStatistics.en = vals['logstat']['en']
|
||||||
|
this.params.loggingStatistics.logPeriodMs = vals['logstat']['logPeriodMs']
|
||||||
|
this.params.loggingStatistics.maxAgeMs = vals['logstat']['maxAgeMs']
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
const doFetchSettings = async () => {
|
||||||
|
let d = await fetch("/dev/settings")
|
||||||
|
let vals = await d.json()
|
||||||
|
this.settingFetchComplete = true
|
||||||
|
this.updateLoggingStatisticsSettings(vals)
|
||||||
|
}
|
||||||
|
doFetchSettings().then(() => {})
|
||||||
document.getElementById("app").removeAttribute("hidden")
|
document.getElementById("app").removeAttribute("hidden")
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user