Compare commits

..

80 Commits

Author SHA1 Message Date
479200df9e изменил формат аптайма 2025-01-09 12:49:59 +03:00
20cf08e2a1 добавил получение sysinfo и вывод аптайма в веб-морде 2025-01-09 12:02:42 +03:00
ab654a754c патч для переподключения к API в случае ошибок 2025-01-09 11:28:35 +03:00
8990fed8f0 зачатки страницы для разработчиков 2024-12-28 16:42:14 +03:00
be6c8023c5 добавил защиту от двойного обновления прошивки, добавил настройки TCP-акселерации в веб (они пока не работает из-за API) 2024-12-28 16:41:58 +03:00
24cb1061a7 добавил отключенную скорость кода 3/5 2024-12-28 11:20:37 +03:00
77ba05e407 косметические исправления: увеличил шрифт, убрал жирный шрифт в таблицах 2024-12-25 11:05:22 +03:00
f3454897d8 исправление полей в SCPC модеме 2024-12-24 17:21:04 +03:00
dc4e37eb8a Merge branch 'refs/heads/dev-scpc-new-rolloff' 2024-12-23 13:57:59 +03:00
522abee794 косметические исправления страниц 2024-12-23 12:52:52 +03:00
8278e4119f косметические исправления страниц 2024-12-23 09:48:02 +03:00
0a3a282d0f некоторые нововведения в front-generator 2024-12-23 09:40:43 +03:00
3d97824ee7 фикс модкода CCM 2024-12-23 09:39:50 +03:00
55fa498dc1 обновил поле rolloff 2024-12-16 09:39:39 +03:00
f5c5caa31c добавил зачатки front-generator, исправил мелкий баг QoS 2024-12-16 09:30:16 +03:00
833374a80e сделал API статической библиотекой 2024-11-29 17:52:25 +03:00
b67011b9a3 добавил получение статуса из API вместе со статистикой 2024-11-29 17:37:25 +03:00
91e9c0301e фикс: билд SCPC модема 2024-11-29 15:12:27 +03:00
98dcc06a6a логгирование ошибок API (установка параметров) 2024-11-29 15:04:38 +03:00
e0aacfe8aa логгирование ошибок API на всех периодических запросах 2024-11-29 14:11:36 +03:00
3e4ffc8281 изменил логику подключения к API 2024-11-29 13:36:39 +03:00
1f8ea04f43 добавил динамическую линковку для boost::log 2024-11-28 17:43:41 +03:00
ad6d734f4a скрыл ржачную картинку 2024-11-28 16:58:57 +03:00
6d79d60eb1 исправил надпись 2024-11-28 16:56:34 +03:00
572a2583f0 логика работы TDMA 2024-11-28 16:56:20 +03:00
925fec6dda добавил фронт для TDMA 2024-11-28 14:57:29 +03:00
5f3d5791da исправление замечаний от Кости 2024-11-27 11:20:55 +03:00
c05a9cff7a добавил повторные попытки подключения к API, если не удалось подключиться 2024-11-15 15:49:20 +03:00
dff0ba1cd3 фикс: центральная частота 2024-11-15 15:37:08 +03:00
43f35da9a2 фикс: цвета в темной теме 2024-11-15 15:22:56 +03:00
ac04c0545b фикс: версия ПО и прочее не показывалось в вебке 2024-11-15 15:12:19 +03:00
3e46f82c0e фикс: билда релизной версии 2024-11-15 14:30:02 +03:00
1536914888 фикс: не использовался путь для статических файлов + немного поменял цвета 2024-11-15 14:26:30 +03:00
1a80e9d455 фикс: не использовался путь для статических файлов 2024-11-15 14:15:10 +03:00
e2618e0300 обновление vue js 2024-11-15 13:50:14 +03:00
1d73547eae cleanup + изменение цветов темы 2024-11-15 13:45:25 +03:00
4a27a46c27 фикс работы с синхронизацией потоков 2024-11-15 10:55:52 +03:00
55448c2bfe поменял название полей у QoS, чтобы прасер был проще 2024-11-15 10:38:52 +03:00
87725ad20a фикс ошибок: пустая строка qos.class.filters.proto в запросе, не применяющиеся настройки сети, нет шага у "ACM*" и "*ослабление" 2024-11-15 10:13:21 +03:00
cc354b73e3 фикс ошибок: пустая строка qos.class.filters.proto в запросе, не применяющиеся настройки сети, нет шага у "ACM*" и "*ослабление" 2024-11-15 09:50:43 +03:00
200dfef698 мелкие исправления предупреждений 2024-11-14 17:30:33 +03:00
5ab16a89db фикс действий с системой 2024-11-14 16:59:41 +03:00
e27164a8b3 исправил шаг в полях с частотой 2024-11-14 16:45:54 +03:00
ccc7766e88 кучка мелких фиксов + добавление перезагрузки модема и сброса настроек 2024-11-14 16:42:24 +03:00
6d076f03cd сделал загрузку обновленной прошивки и само обновление раздельными 2024-11-14 15:42:05 +03:00
ed1bd12c95 фикс бага с настройками сети в отладочной версии API 2024-11-14 15:18:18 +03:00
515a05ec9b добавил получение версии ПО 2024-11-14 15:10:32 +03:00
eda26319c4 добавил sha256 для файла обновления 2024-11-14 11:34:28 +03:00
0dcc562b7d добавил обновление прошивки из веб морды 2024-11-14 11:09:53 +03:00
6467333846 рефактор параметров QoS и tcp-акселерации 2024-11-14 09:48:57 +03:00
484a6abe08 добавил все настройки в веб, сделал cleanup интерфейса 2024-11-13 17:44:42 +03:00
9577ac844d исправления типов параметров в JS 2024-11-13 11:37:06 +03:00
ed0bfce64d фикс http keep alive 2024-11-13 11:29:31 +03:00
8da8c054bf фикс ошибок релизной сборки и парсинга большого тела запроса 2024-11-13 11:25:48 +03:00
90b1f221ea фикс ошибок применения rx/tx параметров 2024-11-12 17:04:06 +03:00
df4b990316 добавил изменение параметров rx/tx + изменил верстку блоков с настройками 2024-11-12 17:01:17 +03:00
dc2d464f41 добавил изменение параметров dpdi (CinC) 2024-11-12 15:37:58 +03:00
087da149f1 изменил политику кеширования, убрал строковые константы из статистик (пилоты, размер кадра) 2024-11-12 14:13:07 +03:00
c0e7e1e300 окно подтверждения применения настроек BUC и LNB, фикс нулевой длинны HTTP запроса 2024-11-12 13:34:59 +03:00
800473b4e3 окно подтверждения применения настроек BUC и LNB 2024-11-12 13:22:58 +03:00
dd0a6813a8 добавил правила QoS по-умолчанию (пустой набор правил) 2024-11-12 11:39:09 +03:00
e2c9877017 фикс некорректного ответа QoS 2024-11-12 11:08:22 +03:00
b51e303006 фикс некорректного ответа QoS 2024-11-12 11:07:22 +03:00
00aa17bb37 добавил корректное сохранение конфига 2024-11-12 11:04:52 +03:00
5175362d1b сделал систему прав, теперь все действия с апи выполняются только при наличии прав (и в целом авторизации) 2024-11-12 10:56:26 +03:00
857a01528b добавил запись настроек BucLnb 2024-11-12 10:18:03 +03:00
cb9d412c8e куча изменений, но зато теперь сохраняются настройки QoS и есть алгоритм сохранения параметров в API 2024-11-11 17:35:25 +03:00
435f215118 сделал включение/отключение классов QoS и правил QoS 2024-11-11 10:45:01 +03:00
4c1ce6031b сделал вторую версию прототипа для QoS, осталось для нее только сохранение прикрутить 2024-11-08 18:08:47 +03:00
1f5eb54225 добавил применение buc lnb для теста 2024-11-08 17:50:02 +03:00
d1ad7baad1 прототип QoS 2024-11-08 17:11:14 +03:00
16b9776bfd визуальные изменения в вебе 2024-11-08 15:34:05 +03:00
a833c0f68a сделал кеширование статистики и настроек терминала 2024-11-08 12:14:09 +03:00
fae7a2ffc8 куча мелких изменений в вебе 2024-11-07 18:07:06 +03:00
4a293e10f6 сделал чтение настроек, удалил пару рудиментов 2024-11-07 13:59:20 +03:00
eaee827261 нововведение: QoS (пока пустой). добавил чтение настроек модема, пока что оно не работает в браузере и не все параметры есть. найден баг с крашем приложения при получении параметров dpdm и acm 2024-11-06 18:00:29 +03:00
6f62c3e1fa сделал получение статистики CinC только когда модем в режиме CinC 2024-11-05 17:19:17 +03:00
40e12f1c67 исправления логики веб-страницы и статистики 2024-11-05 17:11:36 +03:00
541b08a40e переход на получение статистики устройства новым API библиотеки control_system_client 2024-11-05 16:06:16 +03:00
5bfc5cebaf мелкие изменения во фронте 2024-11-05 14:39:26 +03:00
42 changed files with 7722 additions and 1162 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ cmake-build-*
cert.pem
key.pem
dh.pem
/web-action

View File

@@ -15,8 +15,21 @@ else()
message(FATAL_ERROR "You must set build type \"Debug\" or \"Release\". Another build types not supported!")
endif()
if("${MODEM_TYPE}" STREQUAL "SCPC")
add_definitions(-DMODEM_IS_SCPC)
message(STATUS "Selected SCPC modem")
elseif ("${MODEM_TYPE}" STREQUAL "TDMA")
add_definitions(-DMODEM_IS_TDMA)
message(STATUS "Selected TDMA modem")
else()
message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!")
endif()
add_compile_options(-Wall -Wextra -Wsign-conversion)
# максимальный размер тела запроса 200mb
add_definitions(-DHTTP_MAX_PAYLOAD=200000000)
add_subdirectory(dependencies/control_system)
add_executable(terminal-web-server
@@ -44,6 +57,8 @@ add_executable(terminal-web-server
src/auth/utils.h
)
add_definitions(-DBOOST_LOG_DYN_LINK)
find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED)
find_package(OpenSSL REQUIRED)
target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} terminal-client-api)

8
dependencies/control_system/CMakeLists.txt vendored Normal file → Executable file
View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.20)
project(terminal-client-api)
project(terminal)
set(CMAKE_CXX_STANDARD 17)
@@ -14,15 +14,15 @@ include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS -fPIC)
set(default_build_type "Release")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror")
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s -DNDEBUG ")
message(${CMAKE_CXX_FLAGS})
message("CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE})
message(${CMAKE_CXX_FLAGS})
add_library(terminal-client-api SHARED
add_library(terminal-client-api STATIC
client/main.cpp
client/sock_client.cpp
client/system_client.cpp

View File

@@ -1,11 +1,252 @@
#include <shared_mutex>
#include "terminal_api/ControlProtoCInterface.h"
#include <terminal_api/ControlProtoCInterface.h>
#include "system_client.h"
std::shared_mutex mtx;
TSID sid_counter { 0 };
std::map<TSID, std::unique_ptr<system_client>> clients;
EXTERNC CP_Result CP_SetQoSSettings(TSID sid, const std::string &qos_settings_json, bool is_enable){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
auto resp = clients[sid]->send_set_qos_settings_json(qos_settings_json, is_enable);
if (resp == response_type::error)
return ERROR;
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetQoSSettings(TSID sid, std::string &qos_settings_json, bool &is_enable){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
auto resp = clients[sid]->send_get_qos_settings_json(qos_settings_json, is_enable);
if (resp == response_type::error)
return ERROR;
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_SetBUC_LNB_settings(TSID sid, buc_lnb_settings &settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
auto settings_ = reinterpret_cast<buc_lnb_settings_com&>(settings);
auto resp = clients[sid]->send_set_buc_lnb_settings(settings_);
if (resp == response_type::error)
return ERROR;
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetBUC_LNB_settings(TSID sid, buc_lnb_settings &settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
buc_lnb_settings_com settings_com;
auto resp = clients[sid]->send_get_buc_lnb_settings(settings_com);
if (resp == response_type::error)
return ERROR;
settings = reinterpret_cast<buc_lnb_settings&>(settings_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_SetModulatorSettings(TSID sid, modulator_settings& settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
auto settings_ = reinterpret_cast<modulator_settings_com&>(settings);
auto resp = clients[sid]->send_set_modulator_settings(settings_);
if (resp == response_type::error)
return ERROR;
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_SetDemodulatorSettings(TSID sid, demodulator_settings& settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
auto settings_ = reinterpret_cast<demodulator_settings_com&>(settings);
auto resp = clients[sid]->send_set_demodulator_settings(settings_);
if (resp == response_type::error)
return ERROR;
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetModulatorSettings(TSID sid, modulator_settings& settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
modulator_settings_com settings_com;
auto resp = clients[sid]->send_get_modulator_settings(settings_com);
if (resp == response_type::error)
return ERROR;
settings = reinterpret_cast<modulator_settings&>(settings_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetDemodulatorSettings(TSID sid, demodulator_settings& settings){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
demodulator_settings_com settings_com;
auto resp = clients[sid]->send_get_demodulator_settings(settings_com);
if (resp == response_type::error)
return ERROR;
std::cout << "settings_com.rollof: " << settings_com.rollof << std::endl;
std::cout << "settings_com.gain: " << settings_com.gain << std::endl;
settings = reinterpret_cast<demodulator_settings&>(settings_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetDemodulatorState(TSID sid, demodulator_state &state){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
demodulator_state_com state_com;
auto resp = clients[sid]->send_get_demodulator_state(state_com);
if (resp == response_type::error)
{
std::cout << "error" << std::endl;
return ERROR;
}
state = reinterpret_cast<demodulator_state&>(state_com);
state.locks = reinterpret_cast<demodulator_locks&>(state_com.locks);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetModulatorState(TSID sid, modulator_state &state){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
modulator_state_com state_com;
auto resp = clients[sid]->send_get_modulator_state(state_com);
if (resp == response_type::error)
return ERROR;
state = reinterpret_cast<modulator_state&>(state_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetCinCState(TSID sid, CinC_state &state){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
CinC_state_com state_com;
auto resp = clients[sid]->send_get_cinc_state(state_com);
if (resp == response_type::error)
return ERROR;
state = reinterpret_cast<CinC_state&>(state_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_GetDeviceState(TSID sid, device_state &state){
std::shared_lock lock(mtx);
try
{
if (clients.find(sid) == clients.end())
return ERROR;
device_state_com state_com;
auto resp = clients[sid]->send_get_device_state(state_com);
if (resp == response_type::error)
return ERROR;
state = reinterpret_cast<device_state&>(state_com);
return OK;
}
catch(const std::exception& e)
{
return ERROR;
}
}
EXTERNC CP_Result CP_SetRollofBaudrate(TSID sid, double rollof, double baudrate)
{
std::shared_lock lock(mtx);
@@ -285,6 +526,16 @@ EXTERNC CP_Result CP_GetNetwork(TSID sid, const char *param_name, std::string *v
net_val = network_value::mac_eth1;
else if(cmd == "name_serv")
net_val = network_value::name_serv;
else if(cmd == "network_debug_send")
net_val = network_value::network_debug_send;
else if(cmd == "network_port_metric")
net_val = network_value::network_port_metric;
else if(cmd == "network_port_data")
net_val = network_value::network_port_data;
else if(cmd == "if_debug_mode")
net_val = network_value::if_debug_mode;
else if(cmd == "periodic_send_metrics")
net_val = network_value::periodic_send_metrics;
auto resp = clients[sid]->send_get_network_settings(net_val, val_from_server);
if (resp == response_type::error)

View File

@@ -29,8 +29,8 @@ void client::start()
seq_packet::seqpacket_protocol::socket::message_flags in_flags { MSG_WAITALL };
socket_.async_receive(boost::asio::buffer(data_), in_flags,
std::bind(&client::handle_read, this,
std::placeholders::_1,
std::placeholders::_2));
std::placeholders::_1,
std::placeholders::_2));
}
void client::handle_connect(const boost::system::error_code & error)

View File

@@ -315,6 +315,83 @@ void system_client::data_received(const std::vector<uint8_t> & data)
break;
}
case cmd_type::get_demodulator_state:
{
if(cmd.rsp == response_type::ok)
{
cmd_get_demodulator_state value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_modulator_state:
{
if(cmd.rsp == response_type::ok)
{
cmd_get_modulator_state value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_lnb_buc_settings:
{
if(cmd.rsp == response_type::ok)
{
cmd_lnb_buc_settings value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_demodulator_settings:
{
if(cmd.rsp == response_type::ok)
{
cmd_demodulator_settings value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_modulator_settings:
{
if(cmd.rsp == response_type::ok)
{
cmd_modulator_settings value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_device_state:
{
if(cmd.rsp == response_type::ok)
{
cmd_get_device_state value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_cinc_state:
{
if(cmd.rsp == response_type::ok)
{
cmd_get_cinc_state value;
iarchive(value);
data_from_serv = value;
}
break;
}
case cmd_type::get_dma_debugg:
{
if (cmd.rsp == response_type::ok)
@@ -356,6 +433,16 @@ void system_client::data_received(const std::vector<uint8_t> & data)
}
break;
}
case cmd_type::get_qos_settings_json:
{
if (cmd.rsp == response_type::ok)
{
cmd_qos_settings value;
iarchive(value);
data_from_serv = value;
}
break;
}
default: break;
}
@@ -628,6 +715,40 @@ response_type system_client::send_get_qos_params(std::string &node, const name_c
return result;
}
response_type system_client::send_set_qos_settings_json(const std::string &json_string, bool is_enable){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_qos_header{curr_id, cmd_type::set_qos_settings_json};
cmd_qos_settings qos_settings{json_string, is_enable};
send_to_socket(cmd_qos_header, qos_settings);
std::any data_to_serv;
auto result = wait_for_response(curr_id, cmd_type::set_qos_settings, data_to_serv);
return result;
}
response_type system_client::send_get_qos_settings_json(std::string &json_string, bool &is_enable){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_qos_header{curr_id, cmd_type::get_qos_settings_json};
cmd_qos_settings qos_settings{json_string, is_enable};
send_to_socket(cmd_qos_header, qos_settings);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_qos_settings_json, data_from_serv);
if (data_from_serv.has_value())
{
json_string = std::any_cast<cmd_qos_settings>(data_from_serv).json_string;
is_enable = std::any_cast<cmd_qos_settings>(data_from_serv).is_enable;
}
return result;
}
response_type system_client::send_set_10g_config(const cmd_10g_config & _10g_config, std::string &value)
{
std::scoped_lock lock(cmd_in_progress_mtx);
@@ -677,6 +798,168 @@ response_type system_client::send_set_lbq_params(const uint32_t & tick_ms, const
return result;
}
response_type system_client::send_set_modulator_settings(modulator_settings_com &settings)
{
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_acm_header{curr_id, cmd_type::set_modulator_settings};
cmd_modulator_settings settings_;
settings_.modulator_settings = settings;
send_to_socket(cmd_acm_header, settings_);
std::any data_to_serv;
auto result = wait_for_response(curr_id, cmd_type::set_modulator_settings, data_to_serv);
return result;
}
response_type system_client::send_set_demodulator_settings(demodulator_settings_com &settings)
{
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_acm_header{curr_id, cmd_type::set_demodulator_settings};
cmd_demodulator_settings settings_;
settings_.demodulator_settings = settings;
send_to_socket(cmd_acm_header, settings_);
std::any data_to_serv;
auto result = wait_for_response(curr_id, cmd_type::set_demodulator_settings, data_to_serv);
return result;
}
response_type system_client::send_get_modulator_settings(modulator_settings_com &settings)
{
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_modulator_settings};
cmd_modulator_settings modulator;
send_to_socket(cmd_dpdi_header, modulator);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_modulator_settings, data_from_serv);
if (data_from_serv.has_value())
settings = std::any_cast<cmd_modulator_settings>(data_from_serv).modulator_settings;
return result;
}
response_type system_client::send_get_demodulator_settings(demodulator_settings_com &settings)
{
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_demodulator_settings};
cmd_demodulator_settings demodulator;
send_to_socket(cmd_dpdi_header, demodulator);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_demodulator_settings, data_from_serv);
if (data_from_serv.has_value())
settings = std::any_cast<cmd_demodulator_settings>(data_from_serv).demodulator_settings;
return result;
}
response_type system_client::send_get_demodulator_state(demodulator_state_com &demodulator_state){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_demodulator_state};
cmd_get_demodulator_state demodulator;
send_to_socket(cmd_dpdi_header, demodulator);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_demodulator_state, data_from_serv);
if (data_from_serv.has_value())
demodulator_state = std::any_cast<cmd_get_demodulator_state>(data_from_serv).demodulator_state;
return result;
}
response_type system_client::send_get_modulator_state(modulator_state_com &modulator_state){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_modulator_state};
cmd_get_modulator_state modulator;
send_to_socket(cmd_dpdi_header, modulator);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_modulator_state, data_from_serv);
if (data_from_serv.has_value())
modulator_state = std::any_cast<cmd_get_modulator_state>(data_from_serv).modulator_state;
return result;
}
response_type system_client::send_set_buc_lnb_settings(buc_lnb_settings_com &settings){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_acm_header{curr_id, cmd_type::set_lnb_buc_settings};
cmd_lnb_buc_settings settings_;
settings_.buc_lnb = settings;
send_to_socket(cmd_acm_header, settings_);
std::any data_to_serv;
auto result = wait_for_response(curr_id, cmd_type::set_lnb_buc_settings, data_to_serv);
return result;
}
response_type system_client::send_get_buc_lnb_settings(buc_lnb_settings_com &settings){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_buc_lnb_header{curr_id, cmd_type::get_lnb_buc_settings};
cmd_lnb_buc_settings lnb_buc;
send_to_socket(cmd_buc_lnb_header, lnb_buc);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_lnb_buc_settings, data_from_serv);
if (data_from_serv.has_value())
settings = std::any_cast<cmd_lnb_buc_settings>(data_from_serv).buc_lnb;
return result;
}
response_type system_client::send_get_device_state(device_state_com &device_state ){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_device_state};
cmd_get_device_state device;
send_to_socket(cmd_dpdi_header, device);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_device_state, data_from_serv);
if (data_from_serv.has_value())
device_state = std::any_cast<cmd_get_device_state>(data_from_serv).device_state;
return result;
}
response_type system_client::send_get_cinc_state(CinC_state_com &cinc_state){
std::scoped_lock lock(cmd_in_progress_mtx);
uint32_t curr_id { ++cmd_id };
cmd_header cmd_dpdi_header{curr_id, cmd_type::get_cinc_state};
cmd_get_cinc_state cinc;
send_to_socket(cmd_dpdi_header, cinc);
std::any data_from_serv;
auto result = wait_for_response(curr_id, cmd_type::get_cinc_state, data_from_serv);
if (data_from_serv.has_value())
cinc_state = std::any_cast<cmd_get_cinc_state>(data_from_serv).cinc_state;
return result;
}
response_type system_client::send_set_acm_params(const ACM_parameters_serv &acm_params)
{
std::scoped_lock lock(cmd_in_progress_mtx);
@@ -752,4 +1035,4 @@ response_type system_client::send_get_network_settings(const network_value & cmd
if (data_from_serv.has_value())
value = std::any_cast<std::string>(data_from_serv);
return result;
}
}

View File

@@ -48,6 +48,20 @@ public:
response_type send_set_acm_params(const ACM_parameters_serv &acm_params);
response_type send_get_acm_params(cmd_get_acm_param &acm_params);
response_type send_get_demodulator_state(demodulator_state_com &demodulator_state);
response_type send_get_modulator_state(modulator_state_com &modulator_state);
response_type send_get_device_state(device_state_com &device_state);
response_type send_get_cinc_state(CinC_state_com &cinc_state);
response_type send_set_modulator_settings(modulator_settings_com &settings);
response_type send_set_demodulator_settings(demodulator_settings_com &settings);
response_type send_get_modulator_settings(modulator_settings_com &settings);
response_type send_get_demodulator_settings(demodulator_settings_com &settings);
response_type send_set_buc_lnb_settings(buc_lnb_settings_com &settings);
response_type send_get_buc_lnb_settings(buc_lnb_settings_com &settings);
response_type send_set_dpdi_params(dpdi_parameters &dpdi_params);
response_type send_get_dpdi_params(dpdi_parameters &dpdi_params);
@@ -56,6 +70,8 @@ public:
response_type send_set_qos_params(const std::string &node, const name_classes_qos & class_qos);
response_type send_get_qos_params(std::string &node, const name_classes_qos & class_qos);
response_type send_set_qos_settings_json(const std::string &json_string, bool is_enable);
response_type send_get_qos_settings_json(std::string &json_string, bool &is_enable);
void set_stdout_callback(std::function<void(const char *, uint32_t)> cb);
void abort();

View File

@@ -47,6 +47,95 @@ enum class cmd_dma_debugg
begin_save_config = 17
};
struct modulator_settings_com{
uint32_t baudrate;
double central_freq_in_kGz;
double rollof;
double attenuation;
bool is_test_data;
bool is_save_current_state;
bool is_carrier;
bool tx_is_on;
bool is_cinc;
uint32_t modcod_tx;
};
struct modulator_state_com{
bool is_tx_on;
float snr_remote;
uint16_t modcod;
bool is_short;
bool is_pilots;
uint32_t speed_in_bytes_tx;
uint32_t speed_in_bytes_tx_iface;
};
struct demodulator_locks_com{
bool pkt_sync;
bool afc_lock;
bool freq_lock;
bool sym_sync_lock;
};
struct demodulator_settings_com
{
uint32_t baudrate;
double central_freq_in_kGz;
double rollof;
bool is_aru_on;
bool is_rvt_iq;
double gain;
};
struct demodulator_state_com{
float snr;
uint16_t modcod;
bool is_short;
bool is_pilots;
float rssi;
double afc_err;
double crs_freq_err;
double sym_err;
double fine_freq_err;
double if_overload;
uint32_t packet_ok_cnt;
uint32_t packet_bad_cnt;;
uint32_t dummy_cnt;
uint32_t speed_in_bytes_rx;
uint32_t speed_in_bytes_rx_iface;
demodulator_locks_com locks;
};
struct CinC_state_com{
float ratio_signal_signal;
bool carrier_lock;
int32_t freq_error_offset;
uint32_t delay_dpdi;
int32_t freq_fine_estimate;
uint32_t cnt_bad_lock;
};
struct device_state_com{
double adrv_temp;
double zynq_temp;
double pl_temp;
};
enum class voltage_lnb_com{
DISABLE = 0, _13V, _18V, _24V
};
enum class voltage_buc_com{
DISABLE = 0, _24V, _48V
};
struct buc_lnb_settings_com
{
voltage_lnb_com lnb;
bool is_ref_10MHz_lnb = false;
voltage_buc_com buc;
bool is_ref_10MHz_buc = false;
bool is_ref_10MHz_output = false;
bool is_save_current_state = false;
};
enum class name_classes_qos
{
realtime1 = 0,
@@ -292,7 +381,19 @@ enum class cmd_type
get_network = 27,
set_qos_settings = 28,
get_qos_settings = 29,
set_lbq_params = 30
set_lbq_params = 30,
get_demodulator_state = 31,
get_modulator_state = 32,
get_cinc_state = 33,
get_device_state = 34,
set_modulator_settings = 35,
set_demodulator_settings = 36,
get_modulator_settings = 37,
get_demodulator_settings = 38,
set_lnb_buc_settings = 39,
get_lnb_buc_settings = 40,
set_qos_settings_json = 41,
get_qos_settings_json = 42
};
struct cmd_lbq_params
@@ -306,6 +407,91 @@ struct cmd_lbq_params
}
};
struct cmd_lnb_buc_settings{
buc_lnb_settings_com buc_lnb;
template<class Archive>
void serialize(Archive & archive)
{
archive(buc_lnb.buc, buc_lnb.is_ref_10MHz_buc, buc_lnb.lnb,buc_lnb.is_ref_10MHz_lnb, buc_lnb.is_ref_10MHz_output, buc_lnb.is_save_current_state);
}
};
struct cmd_get_cinc_state
{
CinC_state_com cinc_state;
template<class Archive>
void serialize(Archive & archive)
{
archive(cinc_state.carrier_lock, cinc_state.cnt_bad_lock,
cinc_state.delay_dpdi, cinc_state.freq_error_offset,
cinc_state.freq_fine_estimate,
cinc_state.ratio_signal_signal);
}
};
struct cmd_get_device_state
{
device_state_com device_state;
template<class Archive>
void serialize(Archive & archive)
{
archive(device_state.adrv_temp, device_state.pl_temp, device_state.zynq_temp);
}
};
struct cmd_demodulator_settings
{
demodulator_settings_com demodulator_settings;
template<class Archive>
void serialize(Archive & archive)
{
archive(demodulator_settings.baudrate,demodulator_settings.central_freq_in_kGz,
demodulator_settings.gain, demodulator_settings.is_aru_on,
demodulator_settings.is_rvt_iq, demodulator_settings.rollof);
}
};
struct cmd_get_demodulator_state
{
demodulator_state_com demodulator_state;
template<class Archive>
void serialize(Archive & archive)
{
archive(demodulator_state.snr, demodulator_state.modcod, demodulator_state.is_short, demodulator_state.is_pilots,
demodulator_state.rssi, demodulator_state.afc_err, demodulator_state.crs_freq_err,
demodulator_state.sym_err, demodulator_state.fine_freq_err, demodulator_state.if_overload,
demodulator_state.packet_ok_cnt, demodulator_state.packet_bad_cnt, demodulator_state.dummy_cnt,
demodulator_state.speed_in_bytes_rx, demodulator_state.speed_in_bytes_rx_iface,
demodulator_state.locks.afc_lock, demodulator_state.locks.freq_lock, demodulator_state.locks.pkt_sync, demodulator_state.locks.sym_sync_lock );
}
};
struct cmd_modulator_settings
{
modulator_settings_com modulator_settings;
template<class Archive>
void serialize(Archive & archive)
{
archive(modulator_settings.attenuation,modulator_settings.baudrate, modulator_settings.central_freq_in_kGz,
modulator_settings.is_carrier, modulator_settings.is_cinc,
modulator_settings.is_save_current_state, modulator_settings.is_test_data,
modulator_settings.rollof, modulator_settings.tx_is_on, modulator_settings.modcod_tx);
}
};
struct cmd_get_modulator_state{
modulator_state_com modulator_state;
template<class Archive>
void serialize(Archive & archive)
{
archive(modulator_state.snr_remote, modulator_state.modcod, modulator_state.is_short, modulator_state.is_pilots,
modulator_state.is_tx_on, modulator_state.speed_in_bytes_tx, modulator_state.speed_in_bytes_tx_iface);
}
};
struct cmd_get_network
{
@@ -382,7 +568,15 @@ struct cmd_get_acm_param
archive(enable, max_attenuation, max_modcod, min_attenuation, min_modcod, snr_treashold, enable_auto_atten, snr_treashold_acm, period_pack);
}
};
struct cmd_qos_settings{
std::string json_string;
bool is_enable;
template<class Archive>
void serialize(Archive & archive)
{
archive(json_string, is_enable);
}
};
struct cmd_set_qos_settings
{
std::string node;

View File

@@ -0,0 +1,76 @@
#include "include/terminal_api/ControlProtoCInterface.h"
#include <iostream>
int main()
{
TSID sid{0};
unsigned int access{0};
if(CP_Login("admin","pass",&sid,&access) == OK)
{
std::cout << "Succsec" << std::endl;
}
CinC_state state_cinc;
state_cinc.carrier_lock = false;
CP_GetCinCState(sid,state_cinc);
std::cout << state_cinc.carrier_lock << std::endl;
std::cout << state_cinc.freq_error_offset << std::endl;
std::cout << state_cinc.delay_dpdi << std::endl;
std::cout << state_cinc.cnt_bad_lock << std::endl;
modulator_state modulator;
CP_GetModulatorState(sid, modulator);
std::cout << modulator.is_pilots << "\n"
<< modulator.modcod << "\n"
<< modulator.snr_remote << "\n";
demodulator_state demodulator;
demodulator.dummy_cnt = 11111;
CP_GetDemodulatorState(sid, demodulator);
std::cout << "end CP_GetDemodulatorState" << std::endl;
std::cout << demodulator.is_short << std::endl;
std::cout << demodulator.modcod << std::endl;
std::cout << "afc_lock: " <<demodulator.locks.afc_lock << std::endl;
std::cout << "sym_sync_lock: " <<demodulator.locks.sym_sync_lock << std::endl;
std::cout << "freq_lock: " <<demodulator.locks.freq_lock << std::endl;
std::cout << "pkt_sync: " << demodulator.locks.pkt_sync << std::endl;
modulator_settings setting_modulator;
setting_modulator.baudrate = 2000000;
setting_modulator.central_freq_in_kGz = 1340000.24;
setting_modulator.rollof = 0.25;
setting_modulator.tx_is_on = true;
setting_modulator.is_test_data = false;
setting_modulator.is_save_current_state = true;
setting_modulator.is_cinc = false;
setting_modulator.is_carrier = true;
CP_SetModulatorSettings(sid, setting_modulator);
demodulator_settings demodulator_sett;
demodulator_sett.baudrate = 1340000;
demodulator_sett.central_freq_in_kGz = 2400000.34;
demodulator_sett.is_rvt_iq = true;
demodulator_sett.is_aru_on = true;
demodulator_sett.gain = 13;
demodulator_sett.rollof = 0.15;
if(CP_SetDemodulatorSettings(sid, demodulator_sett)== OK)
{
std::cout << "OK SET DEMODULATOR SETTINGS" << std::endl;
}
demodulator_settings demodulator_settings_;
if(CP_GetDemodulatorSettings(sid,demodulator_settings_) == OK)
{
std::cout << "OK GET DEMODULATOR SETTINGS" << std::endl;
std::cout << demodulator_settings_.baudrate << std::endl;
std::cout << demodulator_settings_.gain<< std::endl;
std::cout << demodulator_settings_.rollof << std::endl;
std::cout << demodulator_settings_.is_aru_on << std::endl;
std::cout << demodulator_settings_.is_rvt_iq << std::endl;
std::cout << demodulator_settings_.central_freq_in_kGz << std::endl;
}
modulator_settings modulator_settings_;
if(CP_GetModulatorSettings(sid,modulator_settings_)== OK)
{
std::cout << "OK GET MODULATOR SETTINGS" << std::endl;
std::cout << modulator_settings_.baudrate << std::endl;
std::cout << modulator_settings_.attenuation << std::endl;
std::cout << modulator_settings_.central_freq_in_kGz << std::endl;
std::cout << modulator_settings_.is_carrier << std::endl;
std::cout << modulator_settings_.is_cinc << std::endl;
std::cout << modulator_settings_.rollof << std::endl;
}
}

View File

@@ -1,14 +1,14 @@
#ifndef __CONTROL_PROTO_COMMANDS__
#define __CONTROL_PROTO_COMMANDS__
#include <stdint.h>
#include <iostream>
#include <string>
#ifdef __cplusplus
#include <cstdint>
#define EXTERNC extern "C"
#else
#include <stdint.h>
#define EXTERNC extern
#endif
@@ -75,6 +75,111 @@ LO -- lo frequency
*/
EXTERNC CP_Result CP_ModulatorParams(TSID sid, const char *modulator_param, uint32_t value);
struct modulator_state{
bool is_tx_on;
float snr_remote;
uint16_t modcod;
bool is_short;
bool is_pilots;
uint32_t speed_in_bytes_tx;
uint32_t speed_in_bytes_tx_iface;
};
EXTERNC CP_Result CP_GetModulatorState(TSID sid, modulator_state &state);
struct demodulator_locks{
bool pkt_sync;
bool afc_lock;
bool freq_lock;
bool sym_sync_lock;
};
struct demodulator_state{
float snr;
uint16_t modcod;
bool is_short;
bool is_pilots;
float rssi;
double afc_err;
double crs_freq_err;
double sym_err;
double fine_freq_err;
double if_overload;
uint32_t packet_ok_cnt;
uint32_t packet_bad_cnt;
uint32_t dummy_cnt;
uint32_t speed_in_bytes_rx;
uint32_t speed_in_bytes_rx_iface;
demodulator_locks locks;
};
EXTERNC CP_Result CP_GetDemodulatorState(TSID sid, demodulator_state &state);
struct CinC_state{
float ratio_signal_signal;
bool carrier_lock;
int32_t freq_error_offset;
uint32_t delay_dpdi;
int32_t freq_fine_estimate;
uint32_t cnt_bad_lock;
};
EXTERNC CP_Result CP_GetCinCState(TSID sid, CinC_state &state);
struct device_state{
double adrv_temp;
double zynq_temp;
double pl_temp;
};
EXTERNC CP_Result CP_GetDeviceState(TSID sid, device_state &state);
struct modulator_settings{
uint32_t baudrate;
double central_freq_in_kGz;
double rollof;
double attenuation;
bool is_test_data;
bool is_save_current_state;
bool is_carrier;
bool tx_is_on;
bool is_cinc;
uint32_t modcod_tx;
};
EXTERNC CP_Result CP_SetModulatorSettings(TSID sid, modulator_settings& settings);
EXTERNC CP_Result CP_GetModulatorSettings(TSID sid, modulator_settings& settings);
struct demodulator_settings
{
uint32_t baudrate;
double central_freq_in_kGz;
double rollof;
bool is_aru_on;
bool is_rvt_iq;
double gain;
};
EXTERNC CP_Result CP_SetDemodulatorSettings(TSID sid, demodulator_settings& settings);
EXTERNC CP_Result CP_GetDemodulatorSettings(TSID sid, demodulator_settings& settings);
enum class voltage_lnb{
DISABLE = 0, _13V, _18V, _24V
};
enum class voltage_buc{
DISABLE = 0, _24V, _48V
};
struct buc_lnb_settings
{
voltage_lnb lnb;
bool is_ref_10MHz_lnb = false;
voltage_buc buc;
bool is_ref_10MHz_buc = false;
bool is_ref_10MHz_output = false;
bool is_save_current_state = false;
};
EXTERNC CP_Result CP_SetBUC_LNB_settings(TSID sid, buc_lnb_settings &settings);
EXTERNC CP_Result CP_GetBUC_LNB_settings(TSID sid, buc_lnb_settings &settings);
EXTERNC CP_Result CP_SetQoSSettings(TSID sid, const std::string &qos_settings_json, bool is_enable);
EXTERNC CP_Result CP_GetQoSSettings(TSID sid, std::string &qos_settings_json, bool &is_enable);
EXTERNC CP_Result CP_GetModulatorParams(TSID sid, const char *modulator_param, uint32_t *value);
EXTERNC CP_Result CP_GetDemodulatorParams(TSID sid, const char *demodulator_param, uint32_t *value);

View File

@@ -0,0 +1,75 @@
{
"monitoring-params": {},
"params": {
"rxtx": {
"rx.en": {
"model": "w:switch",
"label": "Включить передатчик"
},
"rx.isTestInputData": {
"model": "w:select",
"label": "Включить передатчик",
"items": [
{"value": "false", "label": "Ethernet"},
{"value": "true", "label": "Тест (CW)"}
]
},
"rx.freqKhz": {
"model": "w:number",
"number.type": "int",
"number.step": 1,
"number.min": 500000,
"number.max": 15000000
}
}
},
"modem_types": {
"tdma": {
"modem_name": "RCSM-101 TDMA",
"groupsList": ["rxtx"],
"tabs": [
{
"name": "monitoring",
"desc": "Мониторинг"
},
{
"name": "setup",
"desc": "Настройки",
"widgets": [
{"group": "html", "name": "h3", "payload": "Настройки передатчика"},
{"group": "rxtx", "name": "rx.en"},
{"group": "rxtx", "name": "rx.isTestInputData"},
{"group": "html", "name": "h3", "payload": "Параметры передачи"},
{"group": "rxtx", "name": "rx.freqKhz"}
]
},
{
"name": "admin",
"desc": "Администрирование"
}
]
},
"scpc": {
"modem_name": "RCSM-101",
"groupsList": ["rxtx"],
"tabs": [
{
"name": "monitoring",
"desc": "Мониторинг"
},
{
"name": "setup",
"desc": "Настройки"
},
{
"name": "qos",
"desc": "QoS"
},
{
"name": "admin",
"desc": "Администрирование"
}
]
}
}
}

39
front-generator/render.py Normal file
View File

@@ -0,0 +1,39 @@
import json
from jinja2 import Environment, FileSystemLoader
import sys
def build_modem_env(modem):
with open('render-params.json') as f:
config = json.load(f)
if modem not in config['modem_types']:
raise RuntimeError(f"Modem '{modem}' is not exist in config!")
mc = config['modem_types'][modem]
return {
"modem_name": mc['modem_name'],
"header_tabs": mc['tabs'],
"js_tabs_array": str([t['name'] for t in mc['tabs']]),
"params": {"groupsList": mc["groupsList"]} | config["params"]
}
def render_modem(modem):
loader = FileSystemLoader('template')
env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True)
template = env.get_template('main.html')
context = build_modem_env(modem)
with open(f"main-{modem}.html", "w") as f:
f.write(template.render(context))
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <scpc|tdma>")
render_modem(sys.argv[1])

View File

@@ -0,0 +1,139 @@
{% raw %}// для обновления высоты хидера
function updateHeaderHeight() { const header = document.querySelector('header'); document.body.style.setProperty('--header-height', `${header.offsetHeight}px`); }
window.addEventListener('load', updateHeaderHeight); window.addEventListener('resize', updateHeaderHeight);
function getCurrentTab() {
const sl = window.location.hash.slice(1)
if (availableTabs.indexOf(sl) >= 0) {
return sl
}
return defaultTab
}
function modcodToStr(modcod) {
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
// NOTE модкоды со скоростью хода 3/5 не работают
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" || modcod < 0 || modcod >= modcod.length) {
return "?";
}
return modcods[modcod]
}
function toModcod(modulation, speed) {
switch (modulation.toLowerCase()) {
case 'qpsk':
switch (speed) {
case '1/4': return 1
case '1/3': return 2
case '2/5': return 3
case '1/2': return 4
case '3/5': return 5 // отключено
case '2/3': return 6
case '3/4': return 7
case '4/5': return 8
case '5/6': return 9
case '8/9': return 10
case '9/10': return 11
default: return 1 // минимальная скорость
}
case '8psk':
switch (speed) {
case '3/5': return 12 // отключено
case '2/3': return 13
case '3/4': return 14
case '5/6': return 15
case '8/9': return 16
case '9/10': return 17
default: return 13 // минимальная скорость
}
case '16apsk':
switch (speed) {
case '2/3': return 18
case '3/4': return 19
case '4/5': return 20
case '5/6': return 21
case '8/9': return 22
case '9/10': return 23
default: return 18 // минимальная скорость
}
case '32apsk':
switch (speed) {
case '3/4': return 24
case '4/5': return 25
case '5/6': return 26
case '8/9': return 27
case '9/10': return 28
default: return 24
}
}
}
function extractModulationAndSpeedFromModcod(modcod) {
switch (modcod) {
case 1: return { modulation: 'qpsk', speed: '1/4' }
case 2: return { modulation: 'qpsk', speed: '1/3' }
case 3: return { modulation: 'qpsk', speed: '2/5' }
case 4: return { modulation: 'qpsk', speed: '1/2' }
case 5: return { modulation: 'qpsk', speed: '3/5' }
case 6: return { modulation: 'qpsk', speed: '2/3' }
case 7: return { modulation: 'qpsk', speed: '3/4' }
case 8: return { modulation: 'qpsk', speed: '4/5' }
case 9: return { modulation: 'qpsk', speed: '5/6' }
case 10: return { modulation: 'qpsk', speed: '8/9' }
case 11: return { modulation: 'qpsk', speed: '9/10' }
case 12: return { modulation: '8psk', speed: '3/5' }
case 13: return { modulation: '8psk', speed: '2/3' }
case 14: return { modulation: '8psk', speed: '3/4' }
case 15: return { modulation: '8psk', speed: '5/6' }
case 16: return { modulation: '8psk', speed: '8/9' }
case 17: return { modulation: '8psk', speed: '9/10' }
case 18: return { modulation: '16apsk', speed: '2/3' }
case 19: return { modulation: '16apsk', speed: '3/4' }
case 20: return { modulation: '16apsk', speed: '4/5' }
case 21: return { modulation: '16apsk', speed: '5/6' }
case 22: return { modulation: '16apsk', speed: '8/9' }
case 23: return { modulation: '16apsk', speed: '9/10' }
case 24: return { modulation: '32apsk', speed: '3/4' }
case 25: return { modulation: '32apsk', speed: '4/5' }
case 26: return { modulation: '32apsk', speed: '5/6' }
case 27: return { modulation: '32apsk', speed: '8/9' }
case 28: return { modulation: '32apsk', speed: '9/10' }
}
return { modulation: 'qpsk', speed: '1/4' }
}
{% endraw %}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
isCinC: false,
// false - означает что статистика не отправляется, true - отправляется
submitStatus: {
{% for pg in params.groupsList %}
{{ pg }}: false,
{% endfor %}
firmwareUpload: false,
firmwareUpgrade: false,
// когда модем перезагружается, тут должен быть счетчик. Направление счета - к нулю
modemReboot: null
},
stat: {
}
stat_rx: {
// индикаторы
state: '?', // общее состояние
sym_sync_lock: '?', // захват символьной
freq_search_lock: '?', // Захват поиска по частоте
afc_lock: '?', // захват ФАПЧ
pkt_sync: '?', // захват пакетной синхронизации
// куча других параметров, идет в том же порядке, что и в таблице
snr: '?', rssi: '?',
modcod: '?', frameSizeNormal: '?',
isPilots: '?',
symError: '?',
freqErr: '?', freqErrAcc: '?',
inputSignalLevel: '?',
pllError: '?',
speedOnRxKbit: '?',
speedOnIifKbit: '?',
// статистика пакетов
packetsOk: '?', packetsBad: '?', packetsDummy: '?',
},
stat_tx: {
// состояние
state: '?',
// прочие поля
snr: '?', modcod: '?', frameSizeNormal: '?', isPilots: '?', speedOnTxKbit: '?', speedOnIifKbit: '?',
},
stat_cinc: {
occ: '?',
correlator: null,
correlatorFails: '?',
freqErr: '?', freqErrAcc: '?',
channelDelay: '?'
},
stat_device: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0
},
param: {
general: {
isCinC: Boolean,
txEn: Boolean, // включен/выключен
modulatorMode: 'normal', // режим работы модулятора
autoStartTx: Boolean, // было "режим работы передатчика"
isTestInputData: Boolean, // входные данные: eth или test
},
tx: {
attenuation: Number, // ослабление
rolloff: Number,
cymRate: Number,
centerFreq: Number,
},
dvbs2: {
mode: null, // ccm/acm
frameSizeNormal: null, // 'normal' / 'short'
// isPilots: false,
// CCM
ccm_modulation: null,
ccm_speed: null,
// ACM
acm_maxModulation: null,
acm_maxSpeed: null,
acm_minModulation: null,
acm_minSpeed: null,
snrReserve: null,
servicePacketPeriod: null,
},
// авто-регулировка мощности
acm: {
en: false,
maxAttenuation: null,
minAttenuation: null,
requiredSnr: null,
},
rx: {
gainMode: null, // 'auto'/'manual' режим управления усилением
manualGain: 0, // усиление, только для ручного режима
spectrumInversion: false,
rolloff: 0,
cymRate: 100000,
centerFreq: 1200000.0,
},
cinc: {
mode: null, // 'positional' | 'delay'
searchBandwidth: 0, // полоса поиска в кГц
position: {
station: {
latitude: 0,
longitude: 0
},
satelliteLongitude: 0,
},
delayMin: 0,
delayMax: 0
},
buc: {
refClk10M: false, // подача опоры 10MHz
powering: 0 // 0, 24, 48
},
lnb: {
refClk10M: false, // подача опоры 10MHz
powering: 0 // 0, 13, 18, 24
},
serviceSettings: {
refClk10M: false, // подача опоры 10MHz
autoStart: false
},
network: {
managementIp: '', // 0.0.0.0/24
managementGateway: '',
mode: String, // l2 | l3
dataIp: '', //
dataMtu: 1500
},
debugSend: {
en: false,
receiverIp: '0.0.0.0', // 0.0.0.0
portCinC: 0,
portData: 0,
timeout: 0
},
qos: {
en: false,
rt1: [],
rt2: [],
rt3: [],
cd: [],
},
tcpAccel: {
en: false,
maxConnections: 128
},
},
uploadFw: {
progress: null,
filename: null,
sha256: null
},
// эти "настройки" - read only
about: {
firmwareVersion: '?',
modemUid: '?',
modemSn: '?',
macManagement: '?',
macData: '?',
},
testState: false,
initState: '',
lastUpdateTime: new Date(),
activeTab: getCurrentTab(),
settingFetchComplete: false,

View File

@@ -1,6 +1,7 @@
#include "resources.h"
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string.hpp>
#include <utility>
#include "jwt.h"
#include "utils.h"
@@ -8,6 +9,9 @@
http::auth::User::User(const std::string &username, const std::string &passwordHash): username(username),
passwordHash(passwordHash.empty() ? utils::sha256(username) : passwordHash) {}
http::auth::User::User(const std::string &username, const std::string &passwordHash, uint32_t perms): perms(perms),
username(username), passwordHash(passwordHash.empty() ? utils::sha256(username) : passwordHash) {}
bool http::auth::User::checkPassword(const std::string &pass) const {
return utils::sha256(pass) == passwordHash;
}
@@ -19,9 +23,8 @@ void http::auth::User::setPassword(const std::string &pass) {
bool http::auth::User::checkPremisions(uint32_t p) const {
if (this->perms & SUPERUSER) {
return true;
} else {
return (this->perms & p) == p;
}
return (this->perms & p) == p;
}
void http::auth::User::setPremisions(uint32_t p) {
@@ -73,3 +76,23 @@ std::shared_ptr<http::auth::User> http::auth::AuthProvider::getSession(const ser
}
http::auth::AuthProvider::~AuthProvider() = default;
http::auth::AuthRequiredResource::AuthRequiredResource(const std::string &path, AuthProvider& provider, resource::respGenerator generator):
BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(User::SUPERUSER) {}
http::auth::AuthRequiredResource::AuthRequiredResource(const std::string &path, AuthProvider& provider, uint32_t perms, resource::respGenerator generator):
BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {}
void http::auth::AuthRequiredResource::handle(const server::Request &req, server::Reply &rep) {
if (auto user = this->provider_.getSession(req)) {
if (user->checkPremisions(this->perms)) {
this->generator_(req, rep);
return;
}
stockReply(server::forbidden, rep);
} else {
stockReply(server::unauthorized, rep);
}
}
http::auth::AuthRequiredResource::~AuthRequiredResource() = default;

View File

@@ -16,12 +16,20 @@ namespace http::auth {
std::string passwordHash;
/**
* Конструктор пользователя.
* Конструктор пользователя. По-умолчанию пользователь создается без прав, их нужно задать отдельной функцией.
* @param username Имя пользователя, он же - логин.
* @param passwordHash Хеш sha256 пароля пользователя. Если передать пустой, то пароль будет сгенерирован такой же, как и имя пользователя.
*/
explicit User(const std::string& username, const std::string& passwordHash = "");
/**
* Конструктор пользователя с указанными правами. Аналогично первому конструктору.
* @param username Имя пользователя, он же - логин.
* @param passwordHash Хеш sha256 пароля пользователя. Если передать пустой, то пароль будет сгенерирован такой же, как и имя пользователя.
* @param perms Права пользователя
*/
explicit User(const std::string& username, const std::string& passwordHash, uint32_t perms);
/**
* Проверить пароль на соответствие хешу
* @param pass
@@ -42,6 +50,7 @@ namespace http::auth {
static constexpr uint32_t WATCH_SETTINGS = 0x0008; // просмотр настроек , если недоступно, то вкладки с настройками не будет
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
static constexpr uint32_t SETUP_QOS = 0x0040; // управление профилем QoS
/**
* Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть.
@@ -79,17 +88,19 @@ namespace http::auth {
~AuthProvider();
};
class AuentificationRequiredResource final: public resource::BasicResource {
class AuthRequiredResource final: public resource::BasicResource {
public:
explicit AuentificationRequiredResource(const std::string& path, std::shared_ptr<AuthProvider> provider, resource::respGenerator generator);
explicit AuthRequiredResource(const std::string& path, AuthProvider& provider, resource::respGenerator generator);
explicit AuthRequiredResource(const std::string& path, AuthProvider& provider, uint32_t perms, resource::respGenerator generator);
void handle(const server::Request &req, server::Reply &rep) override;
~AuentificationRequiredResource() override;
~AuthRequiredResource() override;
private:
AuthProvider& provider_;
resource::respGenerator generator_;
std::shared_ptr<AuthProvider> provider_;
uint32_t perms;
};
}

View File

@@ -9,9 +9,12 @@
std::string http::utils::sha256(const std::string &payload) {
// Вычисляем SHA256 хеш
return sha256(payload.c_str(), payload.size());
}
std::string http::utils::sha256(const char* data, size_t size) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256(reinterpret_cast<const unsigned char *>(payload.c_str()), payload.length(), hash);
SHA256(reinterpret_cast<const unsigned char *>(data), size, hash);
// Преобразуем хеш в шестнадцатеричную строку
std::stringstream ss;

View File

@@ -6,6 +6,7 @@
namespace http::utils {
std::string sha256(const std::string& payload);
std::string sha256(const char* data, size_t size);
std::string sha256AsB64(const std::string& payload);
std::string b64Encode(const char* data, size_t size);

View File

@@ -11,7 +11,6 @@
#include <boost/log/utility/setup/formatter_parser.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <cstddef>
#include <memory>
@@ -20,10 +19,14 @@
#include "terminal_api_driver.h"
#include "auth/resources.h"
#include "auth/jwt.h"
#include "auth/utils.h"
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
constexpr const char* REBOOT_COMMAND = "web-action reboot";
constexpr const char* UPGRADE_COMMAND = "web-action upgrade";
static std::vector<char> loadFile(const std::string& path) {
std::ifstream is(path, std::ios::in | std::ios::binary);
@@ -81,39 +84,59 @@ class ServerResources {
std::unique_ptr<api_driver::ApiDriver> api;
http::auth::AuthProvider auth{};
bool upgradeOrRebootRunning = false;
static void onUploadFirmware(const http::server::Request& req) {
std::ofstream f("/tmp/firmware.zip", std::ios::binary);
if (f.is_open()) {
f.write(req.payload.data(), static_cast<long>(req.payload.size()));
f.close();
} else {
throw std::runtime_error("File is not open");
}
}
void doTerminalUpgrade() const {
api->executeInApi([](TSID sid) {
CP_SetDmaDebug(sid, "begin_save_config", "");
system(UPGRADE_COMMAND);
CP_SetDmaDebug(sid, "save_config", "");
});
}
public:
static constexpr const char* INDEX_HTML = "static/main.html";
static constexpr const char* LOGIN_HTML = "static/login.html";
#if defined(MODEM_IS_TDMA)
static constexpr const char* INDEX_HTML = "/main-tdma.html";
#elif defined(MODEM_IS_SCPC)
static constexpr const char* INDEX_HTML = "/main-scpc.html";
#else
#error "Modem type not defined!"
#endif
static constexpr const char* LOGIN_HTML = "/login.html";
// картинки, их даже можно кешировать
static constexpr const char* FAVICON_ICO = "static/favicon.png";
static constexpr const char* KROKODIL_GIF = "static/krokodil.gif";
static constexpr const char* VUE_JS = "static/js/vue.js"; // это тоже можно кешировать
static constexpr const char* FAVICON_ICO = "/favicon.ico";
static constexpr const char* VUE_JS = "/js/vue.js"; // это тоже можно кешировать
// а эти стили нельзя кешировать в отладочной версии
static constexpr const char* STYLE_CSS = "static/style.css";
static constexpr const char* FIELDS_CSS = "static/fields.css";
static constexpr const char* STYLE_CSS = "/style.css";
static constexpr const char* FIELDS_CSS = "/fields.css";
static constexpr const char* INTERNET_JPG = "/internet.jpg";
ServerResources(const ServerResources&) = delete;
ServerResources(): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
auth.users.emplace_back(std::make_shared<http::auth::User>("admin"));
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));
sf->registerFile(INDEX_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_HTML, mime_types::text_html, false);
sf->registerFile(FAVICON_ICO, mime_types::image_png, true);
sf->registerFile(KROKODIL_GIF, mime_types::image_gif, true);
sf->registerFile(VUE_JS, mime_types::javascript, true);
#if USE_DEBUG
constexpr bool allowCacheCss = false;
#else
constexpr bool allowCacheCss = true;
#endif
sf->registerFile(STYLE_CSS, mime_types::text_css, allowCacheCss);
sf->registerFile(FIELDS_CSS, mime_types::text_css, allowCacheCss);
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true);
sf->registerFile(staticFilesPath + STYLE_CSS, STYLE_CSS, mime_types::text_css, true);
sf->registerFile(staticFilesPath + FIELDS_CSS, FIELDS_CSS, mime_types::text_css, true);
sf->registerFile(staticFilesPath + INDEX_HTML, INDEX_HTML, mime_types::text_html, false);
sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true);
sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true);
}
void registerResources(http::server::Server& s) {
@@ -158,34 +181,22 @@ public:
http::server::stockReply(http::server::bad_request, rep);
}
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/logout", [this](const auto& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/logout", [](const auto& req, auto& rep) {
if (req.method == "GET") {
http::server::httpRedirect(rep, "/");
http::server::httpRedirect(rep, "/login");
rep.headers.push_back({.name = "Set-Cookie", .value = http::auth::jwt::EMPTY_AUTH_COOKIE});
} else {
http::server::stockReply(http::server::bad_request, rep);
}
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/favicon.ico", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/images/krokodil_vzryvaetsya_hd.gif", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KROKODIL_GIF, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/style.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_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>("/js/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>(FAVICON_ICO, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(STYLE_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_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>(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>("/api/statistics", [](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const char* json = R"({"key":"value"})";
rep.content.insert(rep.content.end(), json, json + strlen(json));
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/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") {
http::server::stockReply(http::server::bad_request, rep);
}
@@ -195,11 +206,39 @@ public:
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"mainState":)";
result += api->loadTerminalState();
result += R"(,"sysinfo":)";
result += api->loadSysInfo();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/resetPacketStatistics", [this](const auto& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/settings", this->auth, http::auth::User::WATCH_SETTINGS, [this](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const auto result = api->loadFirmwareVersion();
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
@@ -212,18 +251,242 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/settings", [this](const auto& req, auto& rep) {
if (req.method != "GET") {
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/qos", this->auth, http::auth::User::SETUP_QOS, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"settings":)";
result += api->loadSettings();
result += "}";
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setQosSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/qos): Can't set QoS settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/bucLnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setBucLnbSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/bucLnb): Can't set settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
#ifdef MODEM_IS_SCPC
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", 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);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setCincSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/cinc): Can't set CinC settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
#endif
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/rxtx", 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);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setRxTxSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/network", 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);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setNetworkSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/debugSend", 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);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setDebugSendSettings(pt);
std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
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) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
api->resetDefaultSettings();
system(REBOOT_COMMAND);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
if (req.method != "PUT") {
http::server::stockReply(http::server::bad_request, rep);
}
this->upgradeOrRebootRunning = true;
onUploadFirmware(req);
rep.status = http::server::ok;
rep.headers.clear();
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 += http::utils::sha256(req.payload.data(), req.payload.size());
result += "\"}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
this->upgradeOrRebootRunning = false;
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/doFirmwareUpgrade", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
this->upgradeOrRebootRunning = true;
doTerminalUpgrade();
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const auto result = api->loadFirmwareVersion();
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) {
boost::ignore_unused(req);
sf->serve(INTERNET_JPG, rep);
}));
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 += "\"}";
}));
}
@@ -238,9 +501,9 @@ int main(int argc, char *argv[]) {
if (argc != 4 && argc != 5) {
std::cerr << "Usage: http_server <ssl|nossl> <address> <port> [static files directory]\n";
std::cerr << " For IPv4, try:\n";
std::cerr << " receiver nossl 0.0.0.0 80\n";
std::cerr << " receiver nossl 0.0.0.0 80 .\n";
std::cerr << " For IPv6, try:\n";
std::cerr << " receiver nossl 0::0 80\n";
std::cerr << " receiver nossl 0::0 80 .\n";
return 1;
}
@@ -253,10 +516,17 @@ int main(int argc, char *argv[]) {
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
#endif
#ifdef USE_DEBUG
http::auth::jwt::secretKey = "^}u'ZKyQ%;+:lnh^GS7!=G~nRK?7[{``";
BOOST_LOG_TRIVIAL(info) << "DEBUG build use pre-created key " << http::auth::jwt::secretKey;
#else
http::auth::jwt::generateSecretKey();
BOOST_LOG_TRIVIAL(info) << "Generated new secret key " << http::auth::jwt::secretKey;
#endif
ServerResources resources;
const std::string staticFilesPath = (argc == 5 ? argv[4]: ".");
BOOST_LOG_TRIVIAL(info) << "Use static files path: " << staticFilesPath << "/";
ServerResources resources(staticFilesPath);
// Initialise the server.
std::unique_ptr<http::server::Server> s;

View File

@@ -13,6 +13,8 @@ namespace http::server {
}
void Connection::start() {
request_parser_.reset();
request_.reset();
doRead();
}
@@ -23,9 +25,6 @@ namespace http::server {
Connection::~Connection() = default;
void Connection::doRead() {
request_parser_.reset();
request_.reset();
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) {
if (!ec) {
@@ -38,6 +37,7 @@ namespace http::server {
doWrite();
} else if (result == RequestParser::bad) {
stockReply(bad_request, reply_);
needClose = true;
doWrite();
} else {
doRead();
@@ -59,8 +59,10 @@ namespace http::server {
auto self(shared_from_this());
async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
if (!ec) {
if (!ec && !needClose) {
// keep alive Connection
request_parser_.reset();
request_.reset();
doRead();
} else {
connection_manager_.stop(shared_from_this());
@@ -74,6 +76,8 @@ namespace http::server {
void SslConnection::start() {
get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
request_parser_.reset();
request_.reset();
// Perform the SSL handshake
stream_.async_handshake(boost::asio::ssl::stream_base::server, boost::beast::bind_front_handler([this](auto ec) {
@@ -83,6 +87,11 @@ namespace http::server {
}
void SslConnection::stop() {
try {
stream_.shutdown();
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "SslConnection::stop(): Can't shutdown ssl socket: " << e.what();
}
}
SslConnection::~SslConnection() = default;
@@ -90,9 +99,6 @@ namespace http::server {
void SslConnection::doRead() {
get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));
request_parser_.reset();
request_.reset();
auto self(shared_from_this());
stream_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) {
if (!ec) {
@@ -105,6 +111,7 @@ namespace http::server {
doWrite();
} else if (result == RequestParser::bad) {
stockReply(bad_request, reply_);
needClose = true;
doWrite();
} else {
doRead();
@@ -126,8 +133,10 @@ namespace http::server {
auto self(shared_from_this());
async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
if (!ec) {
if (!ec && !needClose) {
// keep alive Connection
request_parser_.reset();
request_.reset();
doRead();
} else {
connection_manager_.stop(shared_from_this());

View File

@@ -72,6 +72,8 @@ namespace http::server {
/// The reply to be sent back to the client.
Reply reply_;
bool needClose = false;
};
class SslConnection final : public ConnectionBase, public std::enable_shared_from_this<SslConnection> {
@@ -114,6 +116,8 @@ namespace http::server {
/// The reply to be sent back to the client.
Reply reply_;
bool needClose = false;
};
typedef std::shared_ptr<ConnectionBase> connection_ptr;

View File

@@ -36,6 +36,7 @@ std::string http::server::mime_types::toString(Mime m) {
case text_plain: return "text/plain";
case text_html: return "text/html";
case text_css: return "text/css";
case video_mp4: return "video/mp4";
case json: return "application/json";
case javascript: return "application/javascript";
case blob:

View File

@@ -15,6 +15,7 @@ namespace http::server::mime_types {
text_plain, // text/plain
text_html, // text/html
text_css, // text/css
video_mp4, // video/mp4
json, // application/json
javascript, // application/javascript
blob // application/octet-stream

View File

@@ -1,11 +1,30 @@
#include "request_parser.hpp"
#include <sstream>
#include "request.hpp"
namespace http::server {
constexpr int HTTP_MAX_HEADERS = 64;
/**
* Функция, позволяющая или запрещающая выделение размера тела для запросов.
* @return true, если тело удовлетворяет размерам
*/
static bool requestBodySizeResolver(Request& req, size_t reqSize) {
// разрешаем тело только для POST запросов
if (req.method == "POST") {
return reqSize < 0x4000; // 16кб на все POST-запросы к API будет более чем достаточно
}
// это для обновления прошивки
if (req.method == "PUT" && req.url->path == "/api/firmwareUpdate") {
return reqSize <= HTTP_MAX_PAYLOAD;
}
return false;
}
static void parseParams(Url& u, const std::string& query) {
std::istringstream iss(query);
std::string param;
@@ -70,6 +89,12 @@ namespace http::server {
RequestParser::result_type RequestParser::consume(Request &req, char input) {
switch (state_) {
case expecting_payload:
req.payload.push_back(input);
if (req.payload.size() < contentLenghtHeader) {
return indeterminate;
}
return good;
case method_start:
if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
@@ -82,36 +107,34 @@ namespace http::server {
if (input == ' ') {
state_ = uri;
return indeterminate;
} else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
} else {
req.method.push_back(input);
return indeterminate;
}
if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
}
req.method.push_back(input);
return indeterminate;
case uri:
if (input == ' ') {
state_ = http_version_h;
return indeterminate;
} else if (is_ctl(input)) {
return bad;
} else {
req.queryUri.push_back(input);
return indeterminate;
}
if (is_ctl(input)) {
return bad;
}
req.queryUri.push_back(input);
return indeterminate;
case http_version_h:
if (input == 'H') {
state_ = http_version_t_1;
return indeterminate;
} else {
return bad;
}
return bad;
case http_version_t_1:
if (input == 'T') {
state_ = http_version_t_2;
return indeterminate;
} else {
return bad;
}
return bad;
case http_version_t_2:
if (input == 'T') {
state_ = http_version_p;
@@ -182,17 +205,23 @@ namespace http::server {
if (input == '\r') {
state_ = expecting_newline_3;
return indeterminate;
} else if (!req.headers.empty() && (input == ' ' || input == '\t')) {
}
if (!req.headers.empty() && (input == ' ' || input == '\t')) {
state_ = header_lws;
return indeterminate;
} else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
} else {
req.headers.emplace_back();
req.headers.back().name.push_back(input);
state_ = header_name;
return indeterminate;
}
if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
}
if (req.headers.size() > HTTP_MAX_HEADERS) {
return bad;
}
req.headers.emplace_back();
req.headers.back().name.push_back(input);
state_ = header_name;
return indeterminate;
case header_lws:
if (input == '\r') {
state_ = expecting_newline_2;
@@ -210,12 +239,12 @@ namespace http::server {
if (input == ':') {
state_ = space_before_header_value;
return indeterminate;
} else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
} else {
req.headers.back().name.push_back(input);
return indeterminate;
}
if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad;
}
req.headers.back().name.push_back(input);
return indeterminate;
case space_before_header_value:
if (input == ' ') {
state_ = header_value;
@@ -247,17 +276,16 @@ namespace http::server {
if (content_len.empty()) {
return good;
}
contentLenghtHeader = std::stol(content_len);
state_ = expecting_payload;
return indeterminate;
contentLenghtHeader = std::stoul(content_len);
if (contentLenghtHeader == 0) {
return good;
}
if (requestBodySizeResolver(req, contentLenghtHeader)) {
state_ = expecting_payload;
return indeterminate;
}
}
return bad;
case expecting_payload:
req.payload.push_back(input);
if (req.payload.size() <= contentLenghtHeader - 1) {
return indeterminate;
}
return good;
default:
return bad;

View File

@@ -8,7 +8,7 @@
namespace http::server {
struct Request;
class Request;
/// Parser for incoming requests.
class RequestParser {

View File

@@ -3,8 +3,6 @@
#include <fstream>
#include <utility>
#include "../../dependencies/control_system/common/protocol_commands.h"
static void loadFile(const std::string& path, std::vector<char>& content) {
std::ifstream is(path, std::ios::in | std::ios::binary);
if (!is) {
@@ -24,37 +22,42 @@ static void loadFile(const std::string& path, std::vector<char>& content) {
http::resource::BasicResource::BasicResource(std::string path): path(std::move(path)) {}
http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache): path(std::move(path)), type(type), allowCache(allowCache) {
http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(const std::string& path, std::string webPath, server::mime_types::Mime type, bool allowCache):
webPath(std::move(webPath)),
#ifdef USE_DEBUG
fsPath(path),
#endif
type(type), allowCache(allowCache) {
#ifdef USE_DEBUG
if (allowCache) {
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path;
loadFile(this->path, this->content);
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->webPath;
loadFile(path, this->content);
} else {
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->path;
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->webPath;
}
#else
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path;
loadFile(this->path, this->content);
BOOST_LOG_TRIVIAL(info) << "Load static file " << path;
loadFile(path, this->content);
#endif
}
http::resource::StaticFileFactory::StaticFileDef::~StaticFileDef() = default;
http::resource::StaticFileFactory::StaticFileFactory() = default;
void http::resource::StaticFileFactory::registerFile(const std::string &path, server::mime_types::Mime type, bool allowCache) {
this->files.emplace_back(path, type, allowCache);
void http::resource::StaticFileFactory::registerFile(const std::string &path, const std::string &webPath, server::mime_types::Mime type, bool allowCache) {
this->files.emplace_back(path, webPath, type, allowCache);
}
void http::resource::StaticFileFactory::serve(const std::string &path, server::Reply &rep) {
for (auto& f: this->files) {
if (f.path == path) {
if (f.webPath == path) {
#ifdef USE_DEBUG
if (f.allowCache) {
rep.content.clear();
rep.content.insert(rep.content.end(), f.content.begin(), f.content.end());
} else {
BOOST_LOG_TRIVIAL(debug) << "Reload file " << path << " (http path: " << path << ")";
loadFile(f.path, rep.content);
loadFile(f.fsPath, rep.content);
}
#else
rep.content.clear();

View File

@@ -23,9 +23,12 @@ namespace http::resource {
class StaticFileFactory {
class StaticFileDef {
public:
StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache = true);
StaticFileDef(const std::string& path, std::string webPath, server::mime_types::Mime type, bool allowCache = true);
std::string path;
std::string webPath;
#ifdef USE_DEBUG
std::string fsPath;
#endif
server::mime_types::Mime type;
bool allowCache;
std::vector<char> content;
@@ -36,7 +39,7 @@ namespace http::resource {
public:
StaticFileFactory();
void registerFile(const std::string& path, server::mime_types::Mime type, bool allowCache = true);
void registerFile(const std::string& path, const std::string &webPath, server::mime_types::Mime type, bool allowCache = true);
void serve(const std::string& path, server::Reply& rep);

View File

@@ -1,6 +1,7 @@
#include "server.hpp"
#include <utility>
#include <boost/beast/core/basic_stream.hpp>
#include <boost/log/trivial.hpp>
namespace http::server {
@@ -126,7 +127,12 @@ namespace http::server {
if (res->path != req.url->path) {
continue;
}
res->handle(req, rep);
try {
res->handle(req, rep);
} catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "Server::requestHandler(): what = " << e.what();
stockReply(internal_server_error, rep);
}
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,81 @@
#ifndef TERMINAL_API_DRIVER_H
#define TERMINAL_API_DRIVER_H
#include <memory>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <terminal_api/ControlProtoCInterface.h>
namespace api_driver {
constexpr int CACHE_STATISTICS_UPDATE_MS = 500;
constexpr int CACHE_SETTINGS_UPDATE_MS = 5000;
constexpr int CACHE_QOS_UPDATE_MS = 5000;
class TerminalApiDaemon;
/**
* Это ApiDriver. Все ответы он будет возвращать в виде json.
*/
* Это ApiDriver. Все ответы он будет возвращать в виде json.
*/
class ApiDriver {
public:
explicit ApiDriver();
/**
* Запросить общее состояние терминала
* @return {"txState":false,"rxState":false,"rx.sym_sync_lock":false,"rx.freq_search_lock":false,"rx.afc_lock":false,"rx.pkt_sync":false}
*/
std::string loadTerminalState();
* Запуск демона
*/
void startDaemon();
/**
* Сбросить статистику пакетов
*/
* Запросить общее состояние терминала
* @return {"txState":false,"rxState":false,"rx.sym_sync_lock":false,"rx.freq_search_lock":false,"rx.afc_lock":false,"rx.pkt_sync":false}
*/
std::string loadTerminalState() const;
/**
* Сбросить статистику пакетов
*/
void resetPacketStatistics() const;
std::string loadSettings();
std::string loadSettings() const;
std::string loadFirmwareVersion() const;
/**
* Установить настройки RX/TX, readback можно получить используя loadTerminalState
*/
void setRxTxSettings(boost::property_tree::ptree &pt);
#ifdef MODEM_IS_SCPC
/**
* Установить настройки CinC, readback можно получить используя loadTerminalState.
*/
void setCincSettings(boost::property_tree::ptree &pt);
#endif
/**
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
*/
void setBucLnbSettings(boost::property_tree::ptree &pt);
/**
* Установить настройки QoS, readback можно получить используя loadTerminalState.
*/
void setQosSettings(boost::property_tree::ptree &pt);
void setNetworkSettings(boost::property_tree::ptree & pt);
void setDebugSendSettings(boost::property_tree::ptree & pt);
void resetDefaultSettings();
void executeInApi(const std::function<void(TSID sid)>& callback);
static std::string loadSysInfo();
~ApiDriver();
private:
TSID sid{0};
unsigned int access{0};
std::unique_ptr<TerminalApiDaemon> daemon;
};
}

16
static/dev-params.json Normal file
View File

@@ -0,0 +1,16 @@
{
"params": [
{
"label": "Запись пакетов",
"name": "log_bool",
"widget": "checkbox",
"function": "DmaDebug"
},
{
"label": "Unused test",
"name": "log_bool",
"widget": "checkbox",
"function": "DmaDebug"
}
]
}

10
static/dev.html Normal file
View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@@ -1,5 +1,5 @@
.tabs-header {
margin: 0.5em 0;
margin: 0.5em 0 3px;
background: var(--brand-bg);
}
.tabs-header > * {
@@ -7,7 +7,6 @@
}
.tabs-btn {
text-decoration: none;
font-size: 18px;
border: none;
padding: 10px 25px;
text-align: center;
@@ -17,7 +16,7 @@
}
.tabs-btn.active {
color: var(--brand-text);
border-bottom: 3px solid var(--brand-text);
border-bottom: 3px solid var(--bg-action);
}
.tabs-body-item {
padding: 20px 0;
@@ -37,6 +36,30 @@
border-radius: 0.5em;
}
.dangerous-button, .action-button {
border: solid 1px var(--text-color2);
border-radius: 0.5em;
padding: 0.3em;
margin: 0.2em;
}
.summary-actions {
display: flex;
flex-direction: row;
align-items: stretch;
}
.summary-actions * {
margin: 0;
}
.dangerous-button {
background: var(--bg-danger);
}
.action-button {
background: var(--bg-action);
}
.nav-bar-element {
margin: 0.5em;
border-bottom: 2px solid var(--text-color2);
@@ -47,48 +70,102 @@
flex-direction: row;
flex-wrap: wrap;
}
.tabs-item-flex-container > * {
flex: 1 1 auto
}
.tabs-item-flex-container > *, .settings-set-container {
padding: 1em;
margin: 1em;
border: 1px solid var(--text-color2);
border-radius: 0.2em;
}
.tabs-item-flex-container th {
.settings-set-container {
padding: 1em;
}
.settings-set-container th {
text-align: left;
font-weight: normal;
padding-right: 1em;
}
.settings-set-container td {
min-width: 10em;
}
.tabs-item-flex-container h2 {
margin-top: 0;
}
form label * {
.settings-set-container > h3 {
margin: 0;
}
label * {
display: block;
}
form label {
margin: 1em 0;
label {
margin: 1em 0.5em;
display: block;
background: var(--bg-selected);
/*background: var(--bg-selected);*/
color: var(--text-color2);
}
form input {
.settings-set-container input, .settings-set-container select {
margin-top: 0.5em;
border: none;
border-bottom: solid 2px var(--text-color);
width: 100%;
border-bottom: solid 2px var(--text-color2);
width: 20em;
box-sizing: border-box;
}
form input:focus {
.settings-set-container input:focus {
outline: none;
border: none;
border-bottom: solid 2px var(--brand-text);
border-bottom: solid 2px var(--bg-action);
}
.settings-set-container input:invalid {
border: solid 1px var(--text-bad);
}
/* костыль для браузеров, которые некорректно стилизуют элементы option */
select * {
background: var(--bg-selected);
color: var(--text-color);
}
.settings-set-container tr > * {
border-bottom: solid 1px var(--text-color2);
}
.settings-set-container tr:hover {
background: var(--bg-selected);
}
details > summary {
display: flex;
justify-content: space-between;
align-items: stretch;
}
.submit-spinner {
display: inline-block;
width: 1em;
height: 1em;
border: 3px solid #f3f3f3; /* Цвет границы */
border-radius: 50%; /* Делаем круг */
border-top-color: #3498db; /* Цвет верхней границы */
animation: spin 0.8s linear infinite; /* Анимация вращения */
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/*********************** Стили для красивых 'switch' ***********************/
.toggle-input {
display: inline-block;
position: relative;
margin: 5px 10px;
width: 50px;
@@ -116,7 +193,7 @@ form input:focus {
.toggle-input input[type="checkbox"]:checked + .slider {
left: 25px;
background-color: var(--brand-text);
background-color: var(--bg-action);
}
.toggle-input input[type="checkbox"]:checked + .slider:before {

BIN
static/internet.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@@ -1,6 +1,6 @@
/*!
* Vue.js v2.7.16
* (c) 2014-2023 Evan You
* Vue.js v2.7.14
* (c) 2014-2022 Evan You
* Released under the MIT License.
*/
(function (global, factory) {
@@ -82,16 +82,9 @@
return val == null
? ''
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
? JSON.stringify(val, replacer, 2)
? JSON.stringify(val, null, 2)
: String(val);
}
function replacer(_key, val) {
// avoid circular deps from v3
if (val && val.__v_isRef) {
return val.value;
}
return val;
}
/**
* Convert an input value to a number for persistence.
* If the conversion fails, return original string.
@@ -253,7 +246,9 @@
*/
function genStaticKeys$1(modules) {
return modules
.reduce(function (keys, m) { return keys.concat(m.staticKeys || []); }, [])
.reduce(function (keys, m) {
return keys.concat(m.staticKeys || []);
}, [])
.join(',');
}
/**
@@ -730,35 +725,30 @@
};
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var uid$2 = 0;
@@ -892,7 +882,7 @@
});
var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
var NO_INITIAL_VALUE = {};
var NO_INIITIAL_VALUE = {};
/**
* In some cases we may want to disable observation inside a component's
* update computation.
@@ -951,7 +941,7 @@
var keys = Object.keys(value);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock);
defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);
}
}
}
@@ -988,8 +978,7 @@
/**
* Define a reactive property on an Object.
*/
function defineReactive(obj, key, val, customSetter, shallow, mock, observeEvenIfShallow) {
if (observeEvenIfShallow === void 0) { observeEvenIfShallow = false; }
function defineReactive(obj, key, val, customSetter, shallow, mock) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
@@ -999,10 +988,10 @@
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) &&
(val === NO_INITIAL_VALUE || arguments.length === 2)) {
(val === NO_INIITIAL_VALUE || arguments.length === 2)) {
val = obj[key];
}
var childOb = shallow ? val && val.__ob__ : observe(val, false, mock);
var childOb = !shallow && observe(val, false, mock);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
@@ -1047,7 +1036,7 @@
else {
val = newVal;
}
childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock);
childOb = !shallow && observe(newVal, false, mock);
{
dep.notify({
type: "set" /* TriggerOpTypes.SET */,
@@ -2510,10 +2499,11 @@
// to the data on the placeholder node.
vm.$vnode = _parentVnode;
// render self
var prevInst = currentInstance;
var prevRenderInst = currentRenderingInstance;
var vnode;
try {
// There's no need to maintain a stack because all render fns are called
// separately from one another. Nested component's render fns are called
// when parent component is patched.
setCurrentInstance(vm);
currentRenderingInstance = vm;
vnode = render.call(vm._renderProxy, vm.$createElement);
@@ -2537,8 +2527,8 @@
}
}
finally {
currentRenderingInstance = prevRenderInst;
setCurrentInstance(prevInst);
currentRenderingInstance = null;
setCurrentInstance();
}
// if the returned array contains only a single node, allow it
if (isArray(vnode) && vnode.length === 1) {
@@ -2803,112 +2793,6 @@
};
}
var activeEffectScope;
var EffectScope = /** @class */ (function () {
function EffectScope(detached) {
if (detached === void 0) { detached = false; }
this.detached = detached;
/**
* @internal
*/
this.active = true;
/**
* @internal
*/
this.effects = [];
/**
* @internal
*/
this.cleanups = [];
this.parent = activeEffectScope;
if (!detached && activeEffectScope) {
this.index =
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
}
}
EffectScope.prototype.run = function (fn) {
if (this.active) {
var currentEffectScope = activeEffectScope;
try {
activeEffectScope = this;
return fn();
}
finally {
activeEffectScope = currentEffectScope;
}
}
else {
warn$2("cannot run an inactive effect scope.");
}
};
/**
* This should only be called on non-detached scopes
* @internal
*/
EffectScope.prototype.on = function () {
activeEffectScope = this;
};
/**
* This should only be called on non-detached scopes
* @internal
*/
EffectScope.prototype.off = function () {
activeEffectScope = this.parent;
};
EffectScope.prototype.stop = function (fromParent) {
if (this.active) {
var i = void 0, l = void 0;
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].teardown();
}
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]();
}
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true);
}
}
// nested scope, dereference from parent to avoid memory leaks
if (!this.detached && this.parent && !fromParent) {
// optimized O(1) removal
var last = this.parent.scopes.pop();
if (last && last !== this) {
this.parent.scopes[this.index] = last;
last.index = this.index;
}
}
this.parent = undefined;
this.active = false;
}
};
return EffectScope;
}());
function effectScope(detached) {
return new EffectScope(detached);
}
/**
* @internal
*/
function recordEffectScope(effect, scope) {
if (scope === void 0) { scope = activeEffectScope; }
if (scope && scope.active) {
scope.effects.push(effect);
}
}
function getCurrentScope() {
return activeEffectScope;
}
function onScopeDispose(fn) {
if (activeEffectScope) {
activeEffectScope.cleanups.push(fn);
}
else {
warn$2("onScopeDispose() is called when there is no active effect scope" +
" to be associated with.");
}
}
var activeInstance = null;
var isUpdatingChildComponent = false;
function setActiveInstance(vm) {
@@ -3211,8 +3095,7 @@
if (setContext === void 0) { setContext = true; }
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget();
var prevInst = currentInstance;
var prevScope = getCurrentScope();
var prev = currentInstance;
setContext && setCurrentInstance(vm);
var handlers = vm.$options[hook];
var info = "".concat(hook, " hook");
@@ -3224,10 +3107,7 @@
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook);
}
if (setContext) {
setCurrentInstance(prevInst);
prevScope && prevScope.on();
}
setContext && setCurrentInstance(prev);
popTarget();
}
@@ -3445,10 +3325,7 @@
var instance = currentInstance;
var call = function (fn, type, args) {
if (args === void 0) { args = null; }
var res = invokeWithErrorHandling(fn, null, args, instance, type);
if (deep && res && res.__ob__)
res.__ob__.dep.depend();
return res;
return invokeWithErrorHandling(fn, null, args, instance, type);
};
var getter;
var forceTrigger = false;
@@ -3473,7 +3350,6 @@
return s.value;
}
else if (isReactive(s)) {
s.__ob__.dep.depend();
return traverse(s);
}
else if (isFunction(s)) {
@@ -3617,6 +3493,112 @@
};
}
var activeEffectScope;
var EffectScope = /** @class */ (function () {
function EffectScope(detached) {
if (detached === void 0) { detached = false; }
this.detached = detached;
/**
* @internal
*/
this.active = true;
/**
* @internal
*/
this.effects = [];
/**
* @internal
*/
this.cleanups = [];
this.parent = activeEffectScope;
if (!detached && activeEffectScope) {
this.index =
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
}
}
EffectScope.prototype.run = function (fn) {
if (this.active) {
var currentEffectScope = activeEffectScope;
try {
activeEffectScope = this;
return fn();
}
finally {
activeEffectScope = currentEffectScope;
}
}
else {
warn$2("cannot run an inactive effect scope.");
}
};
/**
* This should only be called on non-detached scopes
* @internal
*/
EffectScope.prototype.on = function () {
activeEffectScope = this;
};
/**
* This should only be called on non-detached scopes
* @internal
*/
EffectScope.prototype.off = function () {
activeEffectScope = this.parent;
};
EffectScope.prototype.stop = function (fromParent) {
if (this.active) {
var i = void 0, l = void 0;
for (i = 0, l = this.effects.length; i < l; i++) {
this.effects[i].teardown();
}
for (i = 0, l = this.cleanups.length; i < l; i++) {
this.cleanups[i]();
}
if (this.scopes) {
for (i = 0, l = this.scopes.length; i < l; i++) {
this.scopes[i].stop(true);
}
}
// nested scope, dereference from parent to avoid memory leaks
if (!this.detached && this.parent && !fromParent) {
// optimized O(1) removal
var last = this.parent.scopes.pop();
if (last && last !== this) {
this.parent.scopes[this.index] = last;
last.index = this.index;
}
}
this.parent = undefined;
this.active = false;
}
};
return EffectScope;
}());
function effectScope(detached) {
return new EffectScope(detached);
}
/**
* @internal
*/
function recordEffectScope(effect, scope) {
if (scope === void 0) { scope = activeEffectScope; }
if (scope && scope.active) {
scope.effects.push(effect);
}
}
function getCurrentScope() {
return activeEffectScope;
}
function onScopeDispose(fn) {
if (activeEffectScope) {
activeEffectScope.cleanups.push(fn);
}
else {
warn$2("onScopeDispose() is called when there is no active effect scope" +
" to be associated with.");
}
}
function provide(key, value) {
if (!currentInstance) {
{
@@ -3911,7 +3893,7 @@
suspensible = _b === void 0 ? false : _b, // in Vue 3 default is true
userOnError = source.onError;
if (suspensible) {
warn$2("The suspensible option for async components is not supported in Vue2. It is ignored.");
warn$2("The suspensiblbe option for async components is not supported in Vue2. It is ignored.");
}
var pendingRequest = null;
var retries = 0;
@@ -4014,7 +3996,7 @@
/**
* Note: also update dist/vue.runtime.mjs when adding new exports to this file.
*/
var version = '2.7.16';
var version = '2.7.14';
/**
* @internal type is manually declared in <root>/types/v3-define-component.d.ts
*/
@@ -4391,7 +4373,7 @@
"Instead, use a data or computed property based on the prop's " +
"value. Prop being mutated: \"".concat(key, "\""), vm);
}
}, true /* shallow */);
});
}
// static props are already proxied on the component's prototype
// during Vue.extend(). We only need to proxy props defined at
@@ -4707,9 +4689,6 @@
vm.__v_skip = true;
// effect scope
vm._scope = new EffectScope(true /* detached */);
// #13134 edge case where a child component is manually created during the
// render of a parent component
vm._scope.parent = undefined;
vm._scope._vm = true;
// merge options
if (options && options._isComponent) {
@@ -5956,7 +5935,7 @@
return false;
}
function pruneCache(keepAliveInstance, filter) {
var cache = keepAliveInstance.cache, keys = keepAliveInstance.keys, _vnode = keepAliveInstance._vnode, $vnode = keepAliveInstance.$vnode;
var cache = keepAliveInstance.cache, keys = keepAliveInstance.keys, _vnode = keepAliveInstance._vnode;
for (var key in cache) {
var entry = cache[key];
if (entry) {
@@ -5966,7 +5945,6 @@
}
}
}
$vnode.componentOptions.children = undefined;
}
function pruneCacheEntry(cache, key, keys, current) {
var entry = cache[key];
@@ -6288,7 +6266,7 @@
}
var el = document.createElement(tag);
if (tag.indexOf('-') > -1) {
// https://stackoverflow.com/a/28210364/1070244
// http://stackoverflow.com/a/28210364/1070244
return (unknownElementCache[tag] =
el.constructor === window.HTMLUnknownElement ||
el.constructor === window.HTMLElement);
@@ -7163,11 +7141,8 @@
var insert_1 = ancestor.data.hook.insert;
if (insert_1.merged) {
// start at index 1 to avoid re-invoking component mounted hook
// clone insert hooks to avoid being mutated during iteration.
// e.g. for customed directives under transition group.
var cloned = insert_1.fns.slice(1);
for (var i_10 = 0; i_10 < cloned.length; i_10++) {
cloned[i_10]();
for (var i_10 = 1; i_10 < insert_1.fns.length; i_10++) {
insert_1.fns[i_10]();
}
}
}
@@ -8306,8 +8281,10 @@
}
for (name in newStyle) {
cur = newStyle[name];
// ie9 setting to null has no effect, must use empty string
setProp(el, name, cur == null ? '' : cur);
if (cur !== oldStyle[name]) {
// ie9 setting to null has no effect, must use empty string
setProp(el, name, cur == null ? '' : cur);
}
}
}
var style$1 = {
@@ -9554,7 +9531,7 @@
return "continue";
}
}
// https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
if (conditionalComment.test(html)) {
var conditionalEnd = html.indexOf(']>');
if (conditionalEnd >= 0) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -35,7 +35,7 @@
width: 100%;
box-sizing: border-box;
border: none;
border-bottom: var(--brand-bg) 2px solid;
border-bottom: var(--text-color2) 2px solid;
background-color: var(--bg-color);
text-overflow: ellipsis;
min-height: 2em;
@@ -44,7 +44,7 @@
.form-row input:focus {
outline: none;
border: none;
border-bottom: var(--brand-text) 2px solid;
border-bottom: var(--bg-action) 2px solid;
background-color: var(--bg-selected);
}
@@ -52,6 +52,7 @@
border: none;
font-weight: bolder;
background: var(--bg-action);
color: var(--text-color);
text-align: center;
}

1700
static/main-scpc.html Normal file

File diff suppressed because it is too large Load Diff

1351
static/main-tdma.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,586 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RSCM-101</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/fields.css">
</head>
<body>
<div id="app" hidden>
<div>
<span class="nav-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === true, indicator_good: stat_rx.state === false, indicator: true }"></span></span>
<span class="nav-bar-element">Передача: <span :class="{ indicator_bad: stat_tx.state === true, indicator_good: stat_tx.state === false, indicator: true }"></span></span>
<span class="nav-bar-element">Тест: <span :class="{ indicator_bad: testState === true, indicator_good: testState === false, indicator: true }"></span></span>
<!-- Последнее обновление: {{ lastUpdateTime }}-->
</div>
<div class="tabs">
<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="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
<a href="/logout" class="tabs-btn">Выход</a>
</div>
<div class="tabs-body">
<div class="tabs-body-item tabs-item-flex-container" v-show="activeTab === 'monitoring'">
<div>
<h2>Статистика приема</h2>
<table>
<tbody>
<tr><th>Прием</th><td><span :class="{ indicator_bad: stat_rx.state === true, indicator_good: stat_rx.state === false, indicator: true }"></span></td></tr>
<tr><th>Захват символьной</th><td><span :class="{ indicator_bad: stat_rx.sym_sync_lock === true, indicator_good: stat_rx.sym_sync_lock === false, indicator: true }"></span></td></tr>
<tr><th>Захват ФАПЧ</th><td><span :class="{ indicator_bad: stat_rx.afc_lock === true, indicator_good: stat_rx.afc_lock === false, indicator: true }"></span></td></tr>
<tr><th>Захват поиска по частоте</th><td><span :class="{ indicator_bad: stat_rx.freq_search_lock === true, indicator_good: stat_rx.freq_search_lock === false, indicator: true }"></span></td></tr>
<tr><th>Захват пакетной синхр.</th><td><span :class="{ indicator_bad: stat_rx.pkt_sync === true, indicator_good: stat_rx.pkt_sync === false, indicator: true }"></span></td></tr>
<tr><th>ОСШ/RSSI</th><td>{{ stat_rx.snr }} / {{ stat_rx.rssi }}</td></tr>
<tr><th>Modcod/размер кадра</th><td>{{ stat_rx.modcod }} / {{ stat_rx.frameSize }}</td></tr>
<tr><th>Пилот-символы</th><td>{{ stat_rx.pilots }}</td></tr>
<tr><th>Символьная ошибка</th><td>{{ stat_rx.symError }}</td></tr>
<tr><th>Грубая/точная част. ошибка, Гц</th><td>{{ stat_rx.freqErr }} / {{ stat_rx.freqErrAcc }}</td></tr>
<tr><th>Ур. входного сигнала</th><td>{{ stat_rx.inputSignalLevel }}</td></tr>
<tr><th>Ошибка ФАПЧ</th><td>{{ stat_rx.pllError }}</td></tr>
<tr><th>Инф. скорость на приеме</th><td>{{ stat_rx.speedOnRxKbit }} kbit/s</td></tr>
<tr><th>Инф. скорость на интерфейсе</th><td>{{ stat_rx.speedOnIifKbit }} kbit/s</td></tr>
</tbody>
</table>
<p> Статистика пакетов </p>
<table>
<tbody>
<tr><th>Качественных пакетов</th><td>{{ stat_rx.packetsOk }}</td></tr>
<tr><th>Поврежденных пакетов</th><td>{{ stat_rx.packetsBad }}</td></tr>
<tr><th>DUMMY</th><td>{{ stat_rx.packetsDummy }}</td></tr>
</tbody>
</table>
<button @click="resetPacketsStatistics()"> Сброс статистики </button>
</div>
<div>
<h2>Статистика передачи</h2>
<table>
<tbody>
<tr><th>Передача</th><td><span :class="{ indicator_bad: stat_tx.state === true, indicator_good: stat_tx.state === false, indicator: true }"></span></td></tr>
<tr><th>ОСШ дальнего приема</th><td>{{ stat_tx.snr }}</td></tr>
<tr><th>Modcod</th><td>{{ stat_tx.modcod }}</td></tr>
<tr><th>Размер кадра</th><td>{{ stat_tx.frameSize }}</td></tr>
<tr><th>Пилот-символы</th><td>{{ stat_tx.pilots }}</td></tr>
<tr><th>Инф. скорость на передаче</th><td>{{ stat_tx.speedOnTxKbit }} kbit/s</td></tr>
<tr><th>Инф. скорость на интерфейсе</th><td>{{ stat_tx.speedOnIifKbit }} kbit/s</td></tr>
</tbody>
</table>
</div>
<div v-if="isCinC === true">
<h2>Статистика режима CinC</h2>
<table>
<tbody>
<tr><th>ОСС</th><td>{{ stat_cinc.occ }}</td></tr>
<tr><th>Захват коррелятора</th><td><span :class="{ indicator_bad: stat_cinc.correlator === true, indicator_good: stat_cinc.correlator === false, indicator: true }"></span></td></tr>
<tr><th>Кол-во срывов коррелятора</th><td>{{ stat_cinc.correlatorFails }}</td></tr>
<tr><th>Грубая/точная част. ошибка, Гц</th><td>{{ stat_cinc.freqErr }} / {{ stat_cinc.freqErrAcc }}</td></tr>
<tr><th>Задержка в канале, мс</th><td>{{ stat_cinc.channelDelay }}</td></tr>
</tbody>
</table>
</div>
<div>
<h2>Состояние устройства</h2>
<table>
<tbody>
<tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr>
<tr><th>Температура ZYNC</th><td>{{ stat_device.zync }} °C</td></tr>
<tr><th>Температура FPGA</th><td>{{ stat_device.fpga }} °C</td></tr>
</tbody>
</table>
</div>
</div>
<div class="tabs-body-item" v-show="activeTab === 'setup' && settingFetchComplete">
<h2>Настройки приема/передачи</h2>
<form method="POST" onsubmit="" class="settings-set-container">
<label>
<span>Режим работы</span>
<select v-model="param.general.mode">
<option value="scpc">SCPC</option>
<option value="cinc">CinC</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<div class="tabs-item-flex-container">
<form>
<h2>Настройки передатчика</h2>
<label>
<span>Включить передатчик</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.general.txEn" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Автоматический запуск передатчика</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.general.autoStartTx" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Режим работы модулятора</span>
<select v-model="param.general.modulatorMode">
<option value="normal">Нормальный</option>
<option value="test">Тест (CW)</option>
</select>
</label>
<label>
<span>Входные данные</span>
<select v-model="param.general.inputData">
<option value="eth">Ethernet</option>
<option value="test">Тест (CW)</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Параметры передачи</h2>
<label>
<span>Центральная частота, кГц</span>
<input v-model="param.tx.centerFreq"/>
</label>
<label>
<span>Символьная скорость, Бод</span>
<input v-model="param.tx.cymRate"/>
</label>
<label>
<span>Roll-off</span>
<select v-model="param.tx.rolloff">
<option value="5">0.05</option>
<option value="10">0.10</option>
<option value="15">0.15</option>
<option value="20">0.20</option>
<option value="25">0.25</option>
</select>
</label>
<label>
<span>Номер послед-ти Голда</span>
<select v-model="param.tx.goldan">
<option value="0">0</option>
<option value="1">1</option>
</select>
</label>
<label>
<span>Ослабление, dB</span>
<input v-model="param.tx.attenuation"/>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Режим работы DVB-S2</h2>
<label>
<span>Режим</span>
<select v-model="param.dvbs2.mode">
<option value="ccm">CCM</option>
<option value="acm">ACM</option>
</select>
</label>
<label>
<span>Размер кадра</span>
<select v-model="param.dvbs2.frameSize">
<option value="normal">normal</option>
<option value="short">short</option>
</select>
</label>
<label>
<span>Пилот-символы</span>
<select v-model="param.dvbs2.pilots">
<option value="true">pilots</option>
<option value="false">no pilots</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'ccm'">
<span>Модуляция</span>
<select v-model="param.dvbs2.ccm_modulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'ccm'">
<span>Скорость кода</span>
<select v-model="param.dvbs2.ccm_speed">
<option value="1/4">1/4</option>
<option value="1/3">1/3</option>
<option value="2/5">2/5</option>
<option value="1/2">1/2</option>
<option value="2/3">2/3</option>
<option value="3/4">3/4</option>
<option value="4/5">4/5</option>
<option value="5/6">5/6</option>
<option value="8/9">8/9</option>
<option value="9/10">9/10</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (макс. режим)</span>
<select v-model="param.dvbs2.acm_maxModulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Скорость кода (макс. режим)</span>
<select v-model="param.dvbs2.acm_maxSpeed">
<option value="1/4">1/4</option>
<option value="1/3">1/3</option>
<option value="2/5">2/5</option>
<option value="1/2">1/2</option>
<option value="2/3">2/3</option>
<option value="3/4">3/4</option>
<option value="4/5">4/5</option>
<option value="5/6">5/6</option>
<option value="8/9">8/9</option>
<option value="9/10">9/10</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (мин. режим)</span>
<select v-model="param.dvbs2.acm_minModulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Скорость кода (мин. режим)</span>
<select v-model="param.dvbs2.acm_minSpeed">
<option value="'1/4'">1/4</option>
<option value="'1/3'">1/3</option>
<option value="'2/5'">2/5</option>
<option value="'1/2'">1/2</option>
<option value="'2/3'">2/3</option>
<option value="'3/4'">3/4</option>
<option value="'4/5'">4/5</option>
<option value="'5/6'">5/6</option>
<option value="'8/9'">8/9</option>
<option value="'9/10'">9/10</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Настройки авто-регулировки мощности</h2>
<label>
<span>Авто-регулировка мощности</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.acp.en" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Максимальное ослабление</span>
<input v-model="param.acp.maxAttenuation"/>
</label>
<label>
<span>Минимальное ослабление</span>
<input v-model="param.acp.minAttenuation"/>
</label>
<label>
<span>Требуемое ОСШ</span>
<input v-model="param.acp.requiredSnr"/>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Настройка приемника</h2>
<label>
<span>Режим управления усилением</span>
<select v-model="param.rx.gainMode">
<option value="auto">АРУ</option>
<option value="manual">РРУ</option>
</select>
</label>
<label v-show="param.rx.gainMode === 'manual'">
<span>Усиление, dB</span>
<input v-model="param.rx.manualGain"/>
</label>
<label>
<span>Инверсия спектра</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.rx.spectrumInversion" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Центральная частота, кГц</span>
<input v-model="param.rx.centerFreq"/>
</label>
<label>
<span>Символьная скорость, Бод</span>
<input v-model="param.rx.cymRate"/>
</label>
<label>
<span>Roll-off</span>
<select v-model="param.rx.rolloff">
<option value="5">0.05</option>
<option value="10">0.10</option>
<option value="15">0.15</option>
<option value="20">0.20</option>
<option value="25">0.25</option>
</select>
</label>
<label>
<span>Номер послед-ти Голда</span>
<select v-model="param.rx.goldan">
<option value="0">0</option>
<option value="1">1</option>
</select>
</label>
<label>
<span>Ослабление, dB</span>
<input v-model="param.rx.attenuation"/>
</label>
<button type="submit">Сохранить</button>
</form>
</div>
<div v-show="param.general.mode === 'cinc'">
<h2>Настройки режима CinC</h2>
<p>CinC пока нельзя настроить, но скоро разработчик это поправит)</p>
</div>
<div>
<h2>Настройки питания и опорного генератора</h2>
<p>Эти настройки пока недоступны, но скоро разработчик это поправит)</p>
</div>
</div>
<div class="tabs-body-item" v-show="activeTab === 'admin'">
<p>
Эти настройки пока недоступны, но скоро разработчик это поправит. А пока смотри на крокодила
</p>
<img loading="lazy" src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil">
</div>
</div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div>
</div>
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
<script src="/js/vue.js"></script>
<script>
// const router = useRouter();
const availableTabs = ['monitoring', 'setup', 'admin']
const defaultTab = availableTabs[0]
function getCurrentTab() {
const sl = window.location.hash.slice(1)
if (availableTabs.indexOf(sl) >= 0) {
return sl
}
return defaultTab
}
// TODO: взять модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
// и прикрутить декодинг модкода
const app = new Vue({
el: '#app',
data: {
isCinC: null,
stat_rx: {
// индикаторы
state: '?', // общее состояние
sym_sync_lock: '?', // захват символьной
freq_search_lock: '?', // Захват поиска по частоте
afc_lock: '?', // захват ФАПЧ
pkt_sync: '?', // захват пакетной синхронизации
// куча других параметров, идет в том же порядке, что и в таблице
snr: '?', rssi: '?',
modcod: '?', frameSize: '?',
pilots: '?',
symError: '?',
freqErr: '?', freqErrAcc: '?',
inputSignalLevel: '?',
pllError: '?',
speedOnRxKbit: '?',
speedOnIifKbit: '?',
// статистика пакетов
packetsOk: '?', packetsBad: '?', packetsDummy: '?',
},
stat_tx: {
// состояние
state: '?',
// прочие поля
snr: '?', modcod: '?', frameSize: '?', pilots: '?', speedOnTxKbit: '?', speedOnIifKbit: '?',
},
stat_cinc: {
occ: '?',
correlator: null,
correlatorFails: '?',
freqErr: '?', freqErrAcc: '?',
channelDelay: '?'
},
stat_device: { // температурные датчики
adrv: 0, zync: 0, fpga: 0
},
param: {
general: {
mode: 'scpc',
txEn: false, // включен/выключен
modulatorMode: 'normal', // режим работы модулятора
autoStartTx: false, // было "режим работы передатчика"
inputData: 'eth', // входные данные: eth или test
},
tx: {
attenuation: -3.0, // ослабление
goldan: '0',
rolloff: 20,
cymRate: 100000,
centerFreq: 1200000.0,
},
dvbs2: {
mode: 'ccm',
frameSize: 'normal',
pilots: false,
// CCM
ccm_modulation: 'qpsk',
ccm_speed: '1/2',
// ACM
acm_maxModulation: 'qpsk',
acm_maxSpeed: '1/2',
acm_minModulation: 'qpsk',
acm_minSpeed: '1/2',
snrReserve: 0.5,
servicePacketPeriod: 15,
},
// авто-регулировка мощности
acp: {
en: false,
maxAttenuation: -2.0,
minAttenuation: -3.0,
requiredSnr: -10,
},
rx: {
gainMode: 'auto', // режим управления усилением
manualGain: 70, // усиление, только для ручного режима
spectrumInversion: false,
goldan: '0',
rolloff: 20,
cymRate: 100000,
centerFreq: 1200000.0,
},
buc: {
},
lnb: {},
serviceSettings: {},
},
message: "<err>",
testState: '?',
lastUpdateTime: new Date(),
activeTab: getCurrentTab(),
settingFetchComplete: false
},
methods: {
updateStatistics(vals) {
this.lastUpdateTime = new Date();
this.isCinC = vals["mainState"]["isCinC"]
this.stat_rx.state = vals["mainState"]["rx.state"]
this.stat_rx.sym_sync_lock = vals["mainState"]["rx.sym_sync_lock"]
this.stat_rx.freq_search_lock = vals["mainState"]["rx.freq_search_lock"]
this.stat_rx.afc_lock = vals["mainState"]["rx.afc_lock"]
this.stat_rx.pkt_sync = vals["mainState"]["rx.pkt_sync"]
this.stat_rx.snr = vals["mainState"]["rx.snr"]
this.stat_rx.rssi = vals["mainState"]["rx.rssi"]
this.stat_rx.modcod = vals["mainState"]["rx.modcod"]
this.stat_rx.frameSize = vals["mainState"]["rx.frameSize"]
this.stat_rx.pilots = vals["mainState"]["rx.pilots"]
this.stat_rx.symError = vals["mainState"]["rx.symError"]
this.stat_rx.freqErr = vals["mainState"]["rx.freqErr"]
this.stat_rx.freqErrAcc = vals["mainState"]["rx.freqErrAcc"]
this.stat_rx.inputSignalLevel = vals["mainState"]["rx.inputSignalLevel"]
this.stat_rx.pllError = vals["mainState"]["rx.pllError"]
this.stat_rx.speedOnRxKbit = vals["mainState"]["rx.speedOnRxKbit"]
this.stat_rx.speedOnIifKbit = vals["mainState"]["rx.speedOnIifKbit"]
this.stat_rx.packetsOk = vals["mainState"]["rx.packetsOk"]
this.stat_rx.packetsBad = vals["mainState"]["rx.packetsBad"]
this.stat_rx.packetsDummy = vals["mainState"]["rx.packetsDummy"]
this.stat_tx.state = vals["mainState"]["tx.state"]
this.stat_tx.snr = vals["mainState"]["rx.snr"]
this.stat_tx.modcod = vals["mainState"]["rx.modcod"]
this.stat_tx.frameSize = vals["mainState"]["rx.frameSize"]
this.stat_tx.pilots = vals["mainState"]["rx.pilots"]
this.stat_tx.speedOnTxKbit = vals["mainState"]["tx.speedOnTxKbit"]
this.stat_tx.speedOnIifKbit = vals["mainState"]["rx.speedOnIifKbit"]
this.stat_cinc.occ = vals["mainState"]["cinc.occ"]
this.stat_cinc.correlator = vals["mainState"]["cinc.correlator"]
this.stat_cinc.correlatorFails = vals["mainState"]["cinc.correlatorFails"]
this.stat_cinc.freqErr = vals["mainState"]["cinc.freqErr"]
this.stat_cinc.freqErrAcc = vals["mainState"]["cinc.freqErrAcc"]
this.stat_cinc.channelDelay = vals["mainState"]["cinc.channelDelay"]
this.stat_device.adrv = vals["mainState"]["device.adrv"]
this.stat_device.zync = vals["mainState"]["device.zync"]
this.stat_device.fpga = vals["mainState"]["device.fpga"]
this.testState = vals["mainState"]["testState"]
},
resetPacketsStatistics() {
fetch('/api/resetPacketStatistics', {
method: 'POST'
}).then(() => {
this.stat_rx.packetsOk = 0
this.stat_rx.packetsBad = 0
this.stat_rx.packetsDummy = 0
})
},
updateSettings(vals) {
this.settingFetchComplete = true
}
},
mounted() {
const doFetchStatistics = async () => {
let d = await fetch("/api/get/statistics")
this.updateStatistics(await d.json())
setTimeout(() => {
doFetchStatistics()
}, 1000)
}
doFetchStatistics().then(() => {})
const doFetchSettings = async () => {
let d = await fetch("/api/get/settings")
this.updateSettings(await d.json())
}
doFetchSettings().then(() => {})
document.getElementById("app").removeAttribute("hidden")
}
})
// import MyComponent from './modules/header'
// const sh = new Vue(MyComponent)
</script>
</body>
</html>

View File

@@ -2,16 +2,17 @@
body {
--text-color: #262626;
--text-color2: #3d3d3d;
--text-good: green;
--text-bad: red;
--text-good: #0CF500;
--text-bad: #F5000C;
--brand-bg: #EDF3FE;
--brand-text: #5488F7;
--brand-bg: #B3C0D1;
--brand-text: #0146f4;
--bg-color: #FEFEFE;
--bg-selected: #F1F1F1;
--bg-element: #a7a7a7;
--bg-action: #5181fe;
--bg-action: #81a7ff;
--bg-danger: #ff6464;
}
@media (prefers-color-scheme: dark) {
@@ -19,8 +20,8 @@ body {
body {
--text-color: #eee;
--text-color2: #bbb;
--text-good: greenyellow;
--text-bad: orangered;
--text-good: #91FF00;
--text-bad: #FF1F2A;
--brand-bg: #393E50;
--brand-text: #5F93F3;
@@ -28,7 +29,8 @@ body {
--bg-color: #2d2c33;
--bg-selected: #424248;
--bg-element: #626268;
--bg-action: #4a70d5;
--bg-action: #3a58af;
--bg-danger: #ac1e1e;
}
}
@@ -42,6 +44,7 @@ body {
}
body {
overflow-y: visible;
background: var(--bg-color);
margin: 0; /* браузеры зачем-то ставят свое значение */
}
@@ -50,31 +53,15 @@ body {
margin: 0.5em;
}
/* увеличение размера шрифтов */
* { font-size: large; }
h1 { font-size: xxx-large; }
h2 { font-size: xx-large; }
h3 { font-size: larger; }
/* ========== MAIN STYLES ========== */
header > h1 {
text-align: center;
background-color: var(--brand-bg);
padding: 0.5em;
margin: 0;
}
header * {
color: var(--brand-text);
}
header > nav {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
align-items: center;
}
header > nav > a {
margin: 0.5em;
}
.value-good {
color: var(--text-good);
}