сделал страницу для разработчиков, добавил примитивный сбор журнала статистики
This commit is contained in:
parent
f9b919facf
commit
d5123ef0a2
@ -4,8 +4,8 @@ import sys
|
||||
import requests
|
||||
|
||||
|
||||
USERNAME = "admin"
|
||||
PASSWORD = "admin"
|
||||
USERNAME = "developer"
|
||||
PASSWORD = "fuckyou123"
|
||||
|
||||
|
||||
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 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
|
||||
|
163
static/dev.html
163
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 @@
|
||||
<body>
|
||||
<div id="app" hidden>
|
||||
<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">
|
||||
<span style="font-weight:bold">RSCM-101</span>
|
||||
<a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a>
|
||||
<a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a>
|
||||
<a href="#qos" class="tabs-btn" @click="activeTab = 'qos'" :class="{ active: activeTab === 'qos' }">QoS</a>
|
||||
<a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
|
||||
<a href="/#monitoring" class="tabs-btn">Мониторинг</a>
|
||||
<a href="/#setup" class="tabs-btn">Настройки</a>
|
||||
<a href="/#qos" class="tabs-btn">QoS</a>
|
||||
<a href="/#admin" class="tabs-btn">Администрирование</a>
|
||||
<a class="tabs-btn active">Разработчикам</a>
|
||||
<a href="/logout" class="tabs-btn">Выход</a>
|
||||
</div>
|
||||
</header>
|
||||
<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">
|
||||
<h2>Настройки записи пакетов в IPS</h2>
|
||||
<label>
|
||||
<span>Режим работы</span>
|
||||
<select v-model="param.general.isCinC">
|
||||
<option :value="false">SCPC</option>
|
||||
<option :value="true">CinC</option>
|
||||
<span>Активировать запись</span>
|
||||
<span class="toggle-input"><input type="checkbox" v-model="params.loggingIps" /><span class="slider"></span></span>
|
||||
</label>
|
||||
<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>
|
||||
</label>
|
||||
<label>
|
||||
<span>Полоса поиска, кгц ±</span>
|
||||
<input v-model="param.num" type="number" min="0" max="100" step="1"/>
|
||||
<span>Время хранения</span>
|
||||
<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>
|
||||
<button class="action-button" @click="settingsSubmitLoggingStatistics()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingIps"></span></button>
|
||||
</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>
|
||||
|
||||
<script src="/js/vue.js"></script>
|
||||
<script src="/js/vue.js?v=3.5.13"></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({
|
||||
data() {
|
||||
return {
|
||||
stat_rx: {
|
||||
state: false,
|
||||
// false - означает что статистика не отправляется, true - отправляется
|
||||
submitStatus: {
|
||||
loggingIps: false,
|
||||
loggingStatistics: false,
|
||||
logView: false
|
||||
},
|
||||
param: {
|
||||
general: {
|
||||
isCinC: true,
|
||||
isTestInputData: false,
|
||||
modulatorMode: 'test'
|
||||
},
|
||||
num: 1
|
||||
params: {
|
||||
loggingIps: false,
|
||||
loggingStatistics: {
|
||||
en: false,
|
||||
logPeriodMs: 1000,
|
||||
maxAgeMs: 0
|
||||
}
|
||||
},
|
||||
logfileContent: "",
|
||||
|
||||
initState: '?',
|
||||
activeTab: 'setup'
|
||||
settingFetchComplete: false
|
||||
}
|
||||
},
|
||||
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() {
|
||||
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")
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user