Compare commits

..

No commits in common. "master" and "v1.1-stable" have entirely different histories.

56 changed files with 20052 additions and 22066 deletions

7
.gitignore vendored
View File

@ -5,10 +5,3 @@ cert.pem
key.pem key.pem
dh.pem dh.pem
/web-action /web-action
# эти файлы после генерации должны быть перемещены в `/static`
front-generator/main-scpc.html
front-generator/main-tdma.html
# логи сервера в релизной версии
http_server_*.log

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "dependencies/control_system_client"]
path = dependencies/control_system_client
url = http://gitlab.devrss.vg/mf-tdma/protocol_processing/control_system_client.git

View File

@ -25,33 +25,12 @@ else()
message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!") message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!")
endif() endif()
SET(PROJECT_GIT_REVISION "0") add_compile_options(-Wall -Wextra -Wsign-conversion)
FIND_PACKAGE(Git)
IF (GIT_FOUND)
EXECUTE_PROCESS (
COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
OUTPUT_VARIABLE GIT_HEAD
# ERROR_VARIABLE ERROR_RESULT
# RESULT_VARIABLE INFO_RESULT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF ( ${GIT_HEAD} MATCHES "^.+$" )
STRING ( SUBSTRING ${GIT_HEAD} 0 8 VERSION_REVISION )
SET ( PROJECT_GIT_REVISION ${VERSION_REVISION} )
ENDIF()
ENDIF()
add_compile_options(-Wall -Wextra -Wsign-conversion -DPROJECT_GIT_REVISION="${PROJECT_GIT_REVISION}")
# максимальный размер тела запроса 200mb # максимальный размер тела запроса 200mb
add_definitions(-DHTTP_MAX_PAYLOAD=200000000) add_definitions(-DHTTP_MAX_PAYLOAD=200000000)
add_subdirectory(dependencies/control_system_client) add_subdirectory(dependencies/control_system)
include_directories(src/)
add_executable(terminal-web-server add_executable(terminal-web-server
src/server/mime_types.hpp src/server/mime_types.hpp
@ -76,7 +55,6 @@ add_executable(terminal-web-server
src/auth/jwt.h src/auth/jwt.h
src/auth/utils.cpp src/auth/utils.cpp
src/auth/utils.h src/auth/utils.h
src/version.h
) )
add_definitions(-DBOOST_LOG_DYN_LINK) add_definitions(-DBOOST_LOG_DYN_LINK)

36
dependencies/control_system/CMakeLists.txt vendored Executable file
View File

@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.20)
project(terminal)
set(CMAKE_CXX_STANDARD 17)
if (CMAKE_VERSION VERSION_LESS 3.2)
set(UPDATE_DISCONNECTED_IF_AVAILABLE "")
else()
set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
endif()
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS -fPIC)
set(default_build_type "Release")
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 STATIC
client/main.cpp
client/sock_client.cpp
client/system_client.cpp
)
target_include_directories(terminal-client-api PUBLIC "include/")
find_package(Boost 1.53.0 COMPONENTS system log log_setup REQUIRED)
find_package(cereal REQUIRED)
target_link_libraries(terminal-client-api ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} cereal::cereal)
target_include_directories(terminal-client-api PRIVATE ${Boost_INCLUDE_DIR})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
#include "sock_client.h"
client::client(boost::asio::io_context & io_context, const std::string & unix_file, std::function<void(const std::vector<uint8_t>&)> func_cb)
: socket_(io_context)
, callback_func(func_cb)
{
socket_.async_connect(seq_packet::seqpacket_protocol::endpoint(unix_file),
std::bind(&client::handle_connect, this,
std::placeholders::_1));
}
client::~client()
{
do_close();
}
void client::write(const std::vector<uint8_t> & data)
{
boost::asio::write(socket_, boost::asio::buffer(data));
}
void client::do_close()
{
socket_.close();
}
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));
}
void client::handle_connect(const boost::system::error_code & error)
{
if (!error)
start();
}
void client::handle_read(const boost::system::error_code & error, size_t bytes_transferred)
{
if (!error && callback_func && bytes_transferred)
{
callback_func(std::vector<uint8_t>(std::begin(data_), std::begin(data_) + bytes_transferred));
start();
}
//else
// do_close();
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "../common/seq_packet.h"
class client
{
public:
client(boost::asio::io_context & io_context, const std::string & unix_file, std::function<void(const std::vector<uint8_t>&)> func_cb = nullptr);
~client();
void write(const std::vector<uint8_t> & data);
void do_close();
private:
client(const client&) = delete;
const client& operator=(const client&) = delete;
seq_packet::seqpacket_protocol::socket socket_;
std::array<uint8_t, 2000> data_;
std::function<void(const std::vector<uint8_t>&)> callback_func;
void handle_connect(const boost::system::error_code & error);
void handle_read(const boost::system::error_code & error, size_t bytes_transferred);
void start();
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
#pragma once
#include <map>
#include <queue>
#include <any>
#include "sock_client.h"
#include "../common/protocol_commands.h"
enum class state
{
disconnected,
connected,
logged_in
};
class system_client
{
public:
system_client(const std::string & unix_file, std::function<void(const std::string&)> log_func = nullptr);
~system_client();
state get_current_state() const;
response_type send_login_cmd (const std::string & username, const std::string & pass, access_rights & access);
response_type send_logout_cmd ();
response_type send_ping_cmd (const std::string & ip, size_t count);
response_type send_traceroute_cmd (const std::string & ip);
response_type send_show_interface_cmd(const interface_value & interface);
response_type send_copy_cmd (const fm::side_description & src, const fm::side_description & dst);
response_type send_set_dem_freq_cmd(uint32_t freq);
response_type send_get_dem_freq_cmd(uint32_t & freq);
response_type send_set_gain_param (const gain_value & interface_gain, double gain);
response_type send_get_gain_param (const gain_value & interface_gain, double & gain);
response_type send_radio_enable (const cmd_radio & enable_choice, bool enbl_);
response_type send_modulator_param(const modulator_value & mod_val, uint32_t value);
response_type send_get_modulator_param(const modulator_value & mod_val, uint32_t &value);
response_type send_set_demodulator_param(const demodulator_value & demod_val, uint32_t value);
response_type send_get_demodulator_param(const getdemodulator_value & demod_val, uint32_t &value);
response_type send_zynq_cmd(const zynq_value & zynq_val, double & value);
response_type send_network_settings(const network_value & cmd_netw, const std::string & value);
response_type send_get_network_settings(const network_value & cmd_netw, std::string & value);
response_type send_rollof_and_baudrate(double & rollof, double &baudrate);
response_type send_get_level_dem(const cmd_level_dem & lvl_dem_val, double &value);
response_type send_get_dma_debug(const cmd_get_dma_debugg_enum & dma_debug, std::string &value);
response_type send_set_10g_config(const cmd_10g_config & _10g_config, std::string &value);
response_type send_set_dma_debug(const cmd_dma_debugg & dma_debugg, std::string &value);
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);
response_type send_set_lbq_params(const uint32_t & tick_ms, const uint32_t & bucket_size);
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();
private:
system_client(const system_client&) = delete;
const system_client& operator=(const system_client&) = delete;
boost::asio::io_context io_context;
client clt;
std::function<void(const std::string&)> log;
std::thread ctx_thread;
std::atomic<uint32_t> cmd_id;
std::mutex cb_mtx;
std::function<void(const char *, uint32_t len)> stdout_cb;
std::mutex responses_mtx;
std::map<uint32_t, std::queue<response_type>> responses;
std::map<uint32_t, std::queue<std::any>> responses_data;
std::condition_variable cv_resp;
state current_state;
std::mutex cmd_in_progress_mtx;
response_type wait_for_response (uint32_t id, const cmd_type & type, std::any & data);
response_type wait_for_progressing_response(uint32_t c_id, const cmd_type & cmd_t);
void process_response (uint32_t id, response_type resp, std::any && data);
void data_received (const std::vector<uint8_t> & data);
void send_abort (uint32_t id);
template<typename... Args>
void send_to_socket(Args&&... args)
{
//TO-DO: need case for empty parameter pack?
std::stringstream s;
s << std::noskipws;
{
cereal::BinaryOutputArchive oarchive(s);
oarchive(std::forward<Args>(args)...);
}
std::vector<uint8_t> data((std::istream_iterator<uint8_t>(s)), std::istream_iterator<uint8_t>());
clt.write(data);
}
};

View File

@ -0,0 +1,865 @@
#pragma once
#include <cereal/archives/binary.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/string.hpp>
namespace fm
{
enum class side_type
{
local = 0,
tftp = 1,
config = 2
};
struct side_description
{
side_type s_type;
std::string filepath;
template<class Archive>
void serialize(Archive & archive)
{
archive(s_type, filepath);
}
};
}
enum class cmd_dma_debugg
{
start = 0,
stop = 1,
reset = 2,
reinit = 3,
modcod = 4,
log_bool = 5,
data_mode = 6,
default_params = 7,
buc_voltage = 8,
lnb_voltage = 9,
_10mhz_tx = 10,
_10mhz_rx = 11,
powerdown_plata = 12,
_10MHz_out = 13,
current_state_tx = 14,
current_state_oib = 15,
save_config = 16,
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,
realtime2,
realtime3,
critical1,
critical2,
critical3,
cir,
pir,
wcr1,
wcr2,
wcr3,
enable,
ful_node
};
enum class cmd_get_dma_debugg_enum
{
speed_tx = 0,
speed_rx = 1,
modcod = 2,
drop_bad_rx = 3,
drop_full_rx = 4,
packet_ok_rx = 5,
packet_little_tx = 6,
packet_big_tx = 7,
bad_modcod_tx = 8,
bad_length_tx = 9,
reset_cnt_rx = 10,
data_mode = 11,
buc_voltage = 12,
lnb_voltage = 13,
_10mhz_tx = 14,
_10mhz_rx = 15,
powerdown_plata = 16,
_10MHz_out = 17,
current_state_tx = 18,
real_dpdi_shift = 19,
freq_error_offset = 20,
freq_fine_estimate = 21,
speed_tx_iface = 22,
speed_rx_iface = 23,
current_state_oib = 24,
ratio_signal_signal = 25,
status_init = 26
};
enum class cmd_10g_config
{
udp_payload_lenght = 0,
local_ip = 1,
dest_ip = 2,
gateway_ip = 3,
subnet_mask = 4,
udp_source_port = 5,
udp_dest_port = 6,
local_mac_31_0 = 7,
local_mac_32_47 = 8,
mtx_chan0 = 9,
mtx_chan1 = 10
};
enum class cmd_level_dem
{
//! ОСШ на входе
snr = 0,
//! RSSI на входе
rssi = 1,
sym_sync_lock = 2,
freq_search_lock = 3,
afc_lock = 4,
pkt_sync = 5,
phase_inv = 6,
afc_error = 7,
sym_error = 8,
fine_freq_error = 9,
crs_freq_error = 10,
gc_gain_aru = 11,
rollof = 12,
carrier_lock = 13,
filt_adapt_lock = 14,
snr_acm = 15,
modcod_tx = 16
};
enum class interface_value
{
all = 0,
sat0 = 1,
gigabit = 2,
modulator = 3,
demodulator = 4
};
enum class zynq_value
{
ps_volt = 0,
pl_volt = 1,
adrv_temp = 2,
pl_temp = 3,
ps_temp = 4,
adrv_temp2 = 5
};
enum class modulator_value
{
lo_freaquency = 0,
baud_rate = 1,
temp_treshold = 2,
mod_reset = 3,
modcod = 4,
rollof = 5,
mode_transmitt = 6,
if_overload = 7,
gold_seq = 8
};
enum class demodulator_value
{
lo_freaquency = 0,
baud_rate = 1,
freq_search = 2,
demod_reset = 3,
fec_frame_size = 4,
rvt = 5,
mode_gain_control = 6,
sig_min = 7,
sig_max = 8,
afc_rst = 9,
mode_demod = 10,
gold_seq = 11,
attitude_signals = 12,
is_reset_CinC = 13
};
enum class getdemodulator_value
{
lo_freaquency = 0,
baud_rate = 1,
dummy_counter = 2,
modcod = 3,
mode_gain_control = 4,
sig_min = 5,
sig_max = 6,
rvt = 7,
fec_size = 8,
mode_demod = 9,
cnt_bad_lock_cinc = 10,
gold_seq = 11,
attitude_signals = 12,
cnt_dem_time_sinc = 13,
cnt_dem_lock_afc = 14,
cnt_dem_sync_pack = 15,
cnt_dem_sinc_frec_corse = 16,
freq_lock_estimate = 17,
freq_fine_estimate = 18,
is_reset_CinC = 19,
type_pack = 20,
is_pilots = 21
};
enum class gain_value
{
tx1 = 0,
tx2 = 1,
rx1 = 2,
rx2 = 3,
txpwd = 4,
rxpwd = 5
};
enum class acm_value
{
enable = 0,
modcod_min = 1,
modcod_max = 2,
attenuation_min = 3,
attenuatin_max = 4,
required_snr = 5
};
enum class cmd_radio
{
tx1 = 0,
tx2 = 1,
rx1 = 2,
rx2 = 3,
global = 4,
global2 = 5
};
enum class network_value
{
network = 0,
mode_interface = 1,
version = 2,
mask = 3,
gateway = 4,
dhcp_on = 5,
dhcp_range = 6,
network_data = 7,
chip_id = 8,
serial = 9,
mac_eth0 = 10,
mac_eth1 = 11,
name_serv = 12,
network_debug_send = 13,
network_port_metric = 14,
network_port_data = 15,
periodic_send_metrics = 16,
if_debug_mode = 17
};
enum class cmd_type
{
login = 0,
exit = 1,
ping = 2,
tracert = 3,
interface = 4,
copy = 5,
abort = 6,
set_demodulator_frequency = 7,
get_demodulator_frequency = 8,
set_gain_param = 9,
get_gain_param = 10,
radio_on_of = 11,
modulator_param = 12,
zynq_param = 13,
set_demodulator_param = 14,
get_demodulator_param = 15,
get_modulator_param = 16,
set_network = 17,
get_level_dmd = 18,
set_10g_config = 19,
set_dma_debugg = 20,
get_dma_debugg = 21,
set_baudrate_rollof_dmd = 22,
set_acm_params = 23,
get_acm_params = 24,
set_params_dpdi = 25,
get_params_dpdi = 26,
get_network = 27,
set_qos_settings = 28,
get_qos_settings = 29,
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
{
uint32_t tick_ms;
uint32_t bucket_size;
template<class Archive>
void serialize(Archive & archive)
{
archive(tick_ms, bucket_size);
}
};
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
{
std::string network;
network_value net_val;
template<class Archive>
void serialize(Archive & archive)
{
archive(net_val, network);
}
};
struct cmd_set_network
{
std::string network;
network_value net_val;
template<class Archive>
void serialize(Archive & archive)
{
archive(net_val, network);
}
};
struct dpdi_parameters
{
uint8_t latitude_station_grad = 0;
uint8_t latitude_station_minute = 0;
uint8_t longitude_station_grad = 0;
uint8_t longitude_station_minute = 0;
uint8_t longitude_sattelite_grad = 0;
uint8_t longitude_sattelite_minute = 0;
bool is_delay_window = 0;
uint32_t max_delay = 1;
uint32_t min_delay = 0;
uint32_t freq_offset = 0;
template<class Archive>
void serialize(Archive & archive)
{
archive(latitude_station_grad, latitude_station_minute,
longitude_station_grad, longitude_station_minute,
longitude_sattelite_grad, longitude_sattelite_minute,
is_delay_window, max_delay, min_delay, freq_offset);
}
};
struct ACM_parameters_serv
{
double snr_treashold = 0;
double snr_treashold_acm = 0.5;
uint32_t period_pack = 15;
uint8_t max_modcod = 4;
uint8_t min_modcod = 4;
int max_attenuation = 0;
int min_attenuation = 0;
bool enable = false;
bool enable_auto_atten = false;
};
struct cmd_get_acm_param
{
double snr_treashold = 0;
double snr_treashold_acm = 0.5;
uint32_t period_pack = 15;
uint8_t max_modcod = 4;
uint8_t min_modcod = 4;
int max_attenuation = 0;
int min_attenuation = 0;
bool enable = false;
bool enable_auto_atten = false;
template<class Archive>
void serialize(Archive & archive)
{
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;
name_classes_qos class_qos;
template<class Archive>
void serialize(Archive & archive)
{
archive(node, class_qos);
}
};
struct cmd_get_qos_settings
{
std::string node;
name_classes_qos class_qos;
template<class Archive>
void serialize(Archive & archive)
{
archive(node, class_qos);
}
};
struct cmd_set_acm_param
{
ACM_parameters_serv acm_params;
template<class Archive>
void serialize(Archive & archive)
{
archive(acm_params.enable, acm_params.max_attenuation, acm_params.max_modcod, acm_params.min_attenuation, acm_params.min_modcod, acm_params.snr_treashold, acm_params.enable_auto_atten, acm_params.snr_treashold_acm, acm_params.period_pack);
}
};
struct cmd_get_dma_debug
{
std::string value;
cmd_get_dma_debugg_enum dma_debugg;
template<class Archive>
void serialize(Archive & archive)
{
archive(dma_debugg, value);
}
};
struct cmd_set_dma_debug
{
std::string value;
cmd_dma_debugg dma_debugg;
template<class Archive>
void serialize(Archive & archive)
{
archive(dma_debugg, value);
}
};
struct cmd_set_10g_config
{
std::string value;
cmd_10g_config _10g_config;
template<class Archive>
void serialize(Archive & archive)
{
archive(_10g_config, value);
}
};
struct cmd_get_level_dem
{
double value;
cmd_level_dem lvl_dem;
template<class Archive>
void serialize(Archive & archive)
{
archive(lvl_dem, value);
}
};
struct cmd_zynq_param
{
zynq_value cmd_zynq;
template<class Archive>
void serialize(Archive & archive)
{
archive(cmd_zynq);
}
};
struct radio_enable
{
cmd_radio cmd_radio_;
bool enbl;
template<class Archive>
void serialize(Archive & archive)
{
archive(cmd_radio_, enbl);
}
};
struct cmd_set_modulator_param
{
modulator_value mod_val;
long long value;
template<class Archive>
void serialize(Archive & archive)
{
archive(mod_val,value);
}
};
struct cmd_get_modulator_param
{
modulator_value mod_val;
uint32_t value;
template<class Archive>
void serialize(Archive & archive)
{
archive(mod_val,value);
}
};
struct cmd_set_rollof_and_demod
{
double baudrate;
double rollof;
template<class Archive>
void serialize(Archive & archive)
{
archive(baudrate,rollof);
}
};
struct cmd_set_demodulator_param
{
demodulator_value demod_val;
uint32_t value;
template<class Archive>
void serialize(Archive & archive)
{
archive(demod_val,value);
}
};
struct cmd_get_demodulator_param
{
getdemodulator_value demod_val;
uint32_t value;
template<class Archive>
void serialize(Archive & archive)
{
archive(demod_val,value);
}
};
struct set_gain_par
{
gain_value g_val;
double _gain;
template<class Archive>
void serialize(Archive & archive)
{
archive(g_val,_gain);
}
};
struct get_gain_par
{
gain_value g_val;
template<class Archive>
void serialize(Archive & archive)
{
archive(g_val);
}
};
struct set_dem_freq_cmd
{
uint32_t frequency;
template<class Archive>
void serialize(Archive & archive)
{
archive(frequency);
}
};
enum class response_type
{
ok = 0,
error = 1,
in_progress = 3,
abort = 4,
busy = 5
};
enum class access_rights
{
not_allowed = 0,
user = 1,
admin = 2
};
struct cmd_header
{
uint32_t id;
cmd_type cmd;
template<class Archive>
void serialize(Archive & archive)
{
archive(id, cmd);
}
};
struct login_cmd
{
std::string username;
std::string pass;
template<class Archive>
void serialize(Archive & archive)
{
archive(username, pass);
}
};
struct ping_cmd
{
std::string ip_address;
size_t count;
template<class Archive>
void serialize(Archive & archive)
{
archive(ip_address, count);
}
};
struct tracert_cmd
{
std::string ip_address;
template<class Archive>
void serialize(Archive & archive)
{
archive(ip_address);
}
};
struct interface_cmd
{
interface_value val;
template<class Archive>
void serialize(Archive & archive)
{
archive(val);
}
};
struct copy_cmd
{
fm::side_description src;
fm::side_description dst;
template<class Archive>
void serialize(Archive & archive)
{
archive(src, dst);
}
};
struct response_header
{
uint32_t id;
cmd_type cmd;
response_type rsp;
template<class Archive>
void serialize(Archive & archive)
{
archive(id, cmd, rsp);
}
};

View File

@ -0,0 +1,23 @@
#pragma once
#include <boost/asio.hpp>
namespace seq_packet
{
using namespace boost::asio::local;
struct seqpacket_protocol
{
int type() const { return SOCK_SEQPACKET; }
int protocol() const { return 0; }
int family() const { return AF_UNIX; }
using endpoint = basic_endpoint<seqpacket_protocol>;
using socket = boost::asio::basic_stream_socket<seqpacket_protocol>;
using acceptor = boost::asio::basic_socket_acceptor<seqpacket_protocol>;
#if !defined(BOOST_ASIO_NO_IOSTREAM)
/// The UNIX domain iostream type.
typedef boost::asio::basic_socket_iostream<seqpacket_protocol> iostream;
#endif // !defined(BOOST_ASIO_NO_IOSTREAM)
};
}

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

@ -0,0 +1,329 @@
#ifndef __CONTROL_PROTO_COMMANDS__
#define __CONTROL_PROTO_COMMANDS__
#include <stdint.h>
#include <iostream>
#include <string>
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC extern
#endif
typedef unsigned int TSID;
typedef enum CP_Res {
OK = 0,
TIMEOUT,
ERROR,
ABORT,
BUSY
} CP_Result;
typedef void (*CP_cmd_stdout_cb)(const char * str, uint32_t len);
/*
cb - callback for receive stdout of command
*/
EXTERNC void CP_SetCmdStdoutCallback(TSID sid, CP_cmd_stdout_cb cb);
/*
sid -- current session ID
*/
EXTERNC void CP_CmdAbort(TSID sid);
/*
host -- host name
user -- user name
pwd -- password hash
sid -- output session ID (used for all requests)
access -- output type of privilegies {admin|operator|...etc}
*/
EXTERNC CP_Result CP_Login(const char * user, const char * pwd, TSID * sid, unsigned int * access);
/*
sid -- current session ID
*/
EXTERNC CP_Result CP_Logout(TSID sid);
/*
sid -- current session ID
ip_address -- IP address of the host
packet_count -- count of packets to send
*/
EXTERNC CP_Result CP_GetDmaDebug(TSID sid, const char *command, std::string *val);
EXTERNC CP_Result CP_SetDemFreq(TSID sid, uint32_t freq);
EXTERNC CP_Result CP_GetDemFreq(TSID sid, uint32_t * freq);
EXTERNC CP_Result CP_SetDmaDebug(TSID sid, const char *command, std::string val);
EXTERNC CP_Result CP_Set10gConfig(TSID sid, const char *parameter, std::string val);
EXTERNC CP_Result CP_SetRollofBaudrate(TSID sid, double rollof,double baudrate);
//interfaces<TX1><TX2><RX1><RX2>
EXTERNC CP_Result CP_GetGain(TSID sid, const char *gain_interface, double *gain);
//interfaces<TX1><TX2><RX1><RX2>
EXTERNC CP_Result CP_SetGain(TSID sid, const char *gain_interface, double gain);
//interfaces<TX1><TX2><RX1><RX2>
EXTERNC CP_Result CP_RadioEnable(TSID sid, const char *radio_interface, bool on_of);
//interfaces<TX1><TX2><RX1><RX2>
/*
BOD -- baud_rate
SPREAD -- koef spread
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);
EXTERNC CP_Result CP_GetLevelDemod(TSID sid, const char * param ,double *value);
EXTERNC CP_Result CP_DemodulatorParams(TSID sid, const char *demodulator_param, uint32_t value);
EXTERNC CP_Result CP_SetLBQParams(TSID sid, const uint32_t &tick_ms, const uint32_t &bucket_size);
EXTERNC CP_Result CP_SetQoSParams(TSID sid, const std::string &type_node, const std::string & node);
EXTERNC CP_Result CP_GetQoSParams(TSID sid, const std::string &type_node, std::string * node);
struct ACM_parameters_serv_
{
double snr_treashold = 0;
double snr_treashold_acm = 0.5;
uint32_t period_pack = 15;
uint8_t max_modcod = 4;
uint8_t min_modcod = 4;
int max_attenuation = 0;
int min_attenuation = 0;
bool enable = false;
bool enable_auto_atten = false;
};
struct DPDI_parmeters
{
uint8_t latitude_station_grad = 0;
uint8_t latitude_station_minute = 0;
uint8_t longitude_station_grad = 0;
uint8_t longitude_station_minute = 0;
uint8_t longitude_sattelite_grad = 0;
uint8_t longitude_sattelite_minute = 0;
bool is_delay_window = 0;
uint32_t max_delay = 1;
uint32_t min_delay = 0;
uint32_t freq_offset = 0;
};
EXTERNC CP_Result CP_SetAcmParams(TSID sid, ACM_parameters_serv_ acm_params);
EXTERNC CP_Result CP_GetAcmParams(TSID sid, ACM_parameters_serv_ *acm_params);
EXTERNC CP_Result CP_SetDpdiParams(TSID sid, DPDI_parmeters dpdi_params);
EXTERNC CP_Result CP_GetDpdiParams(TSID sid, DPDI_parmeters *dpdi_pars_get);
/*
PSV
PLV
PST
PLT
ADRVT
*/
EXTERNC CP_Result CP_ZynqParams(TSID sid, const char *zynq_param, double *value);
EXTERNC CP_Result CP_SetNetwork(TSID sid, const char *param_name, const char *val);
EXTERNC CP_Result CP_GetNetwork(TSID sid, const char *param_name, std::string *val);
/*
ip_address -- new IP address fot the host
*/
EXTERNC CP_Result CP_Ping(TSID sid, const char * ip_address, int packet_count);
/*
sid -- current session ID
ip_address -- IP address of the host
*/
EXTERNC CP_Result CP_Traceroute(TSID sid, const char * ip_address);
/*
sid -- current session ID
src -- {"running-config"|"startup-config"|host_address}
dst -- {"running-config"|"startup-config"|host_address}
NOTE:
src and dst both must be different
*/
EXTERNC CP_Result CP_Copy(TSID sid, const char * src, const char * dst);
/*
sid -- current session ID
interface -- {"all"|"sat0"|"gigabit"|"modulator"|"demodulator"}
output data goes to callback
*/
EXTERNC CP_Result CP_ShowInterface(TSID sid, const char * interface);
/*
sid -- current session ID
out_startup_config -- received information about startup config
*/
EXTERNC CP_Result CP_ShowStartupConfig(TSID sid, char ** out_startup_config);
/*
sid -- current session ID
interface -- {"all"|"sat0"|"gigabit"|"modulator"|"demodulator"}
out_startup_config -- received information about startup config
*/
EXTERNC CP_Result CP_ShowStartupConfigInterface(TSID sid, const char * interface, char ** out_startup_config);
// Demodulator
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
rate -- symbol rate
*/
EXTERNC CP_Result CP_SetDemodSymrate(TSID sid, const char * interface, uint32_t rate);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
rate -- frequency value in Hertz
*/
EXTERNC CP_Result CP_SetDemodFrequency(TSID sid, const char * interface, uint32_t freq);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
mode -- {"on"|"off"|"auto"}
*/
EXTERNC CP_Result CP_SetDemodSpectrum(TSID sid, const char * interface, const char * mode);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
mode -- {"on"|"off"}
*/
EXTERNC CP_Result CP_SetDemodReference(TSID sid, const char * interface, const char * mode);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
rate -- frequency value in Hertz
*/
EXTERNC CP_Result CP_SetDemodSearch(TSID sid, const char * interface, uint32_t freq);
// Modulator
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
rate -- symbol rate
*/
EXTERNC CP_Result CP_SetModSymrate(TSID sid, const char * interface, uint32_t rate);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
rate -- frequency value in Hertz
*/
EXTERNC CP_Result CP_SetModSymFrequency(TSID sid, const char * interface, uint32_t freq);
/*
sid -- current session ID
interface -- {"SAT"|"Ethernet"|"Loopback"}
mode -- {"on"|"off"}
*/
EXTERNC CP_Result CP_SetModReference(TSID sid, const char * interface, const char * mode);
#endif

@ -1 +0,0 @@
Subproject commit 467f8d6bf6f6da11c622949ecb49e3fbff11f372

View File

@ -1,74 +0,0 @@
#!/usr/bin/python3
import json
import sys
import requests
USERNAME = "developer"
PASSWORD = "fuckyou123"
def do_login(base_url):
session = requests.session()
login_data = json.dumps({
"username": USERNAME,
"password": PASSWORD
}, ensure_ascii=True, indent=0)
session.get(f"{base_url}/login")
login_result = json.loads(session.post(f"{base_url}/login", headers={'Content-Type': 'application/json'}, data=login_data).content.decode('utf-8'))
if "error" in login_result:
raise RuntimeError(f"Failed to login: {login_result}")
return session
def cp_set_dma_debug(base_url, param_name, value):
session = do_login(base_url)
res = session.post(f"{base_url}/dev/cpapicall", params={
"f": "SetDmaDebug",
"param": param_name,
"value": value
})
return res.content.decode('utf-8')
def cp_get_dma_debug(base_url, param_name):
session = do_login(base_url)
res = session.post(f"{base_url}/dev/cpapicall", params={
"f": "GetDmaDebug",
"param": param_name
})
return res.content.decode('utf-8')
def set_logging(base_url, value):
print(cp_set_dma_debug(base_url, "log_bool", value))
if __name__ == '__main__':
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} http(s)://terminal-url logging on|off")
print(f" set_dma_debug <param_name> <value>")
print(f" get_dma_debug <param_name>")
exit(1)
if sys.argv[2] == "logging":
if len(sys.argv) != 4:
print("Wrong logging usage!")
else:
set_logging(sys.argv[1], {"on": "true", "off": "false"}[sys.argv[3]])
elif sys.argv[2] == "set_dma_debug":
if len(sys.argv) != 5:
print("Wrong set dma debug usage!")
else:
print(cp_set_dma_debug(sys.argv[1], sys.argv[3], sys.argv[4]))
elif sys.argv[2] == "get_dma_debug":
if len(sys.argv) != 4:
print("Wrong set dma debug usage!")
else:
print(cp_get_dma_debug(sys.argv[1], sys.argv[3]))
else:
print(f"Unknown action: {sys.argv[1]}")

View File

@ -1,358 +1,74 @@
{ {
"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": { "modem_types": {
"tdma": { "tdma": {
"modem_name": "RCSM-101 TDMA", "modem_name": "RCSM-101 TDMA",
"dangerousParamGroups": { "groupsList": ["rxtx"],
"buclnb": "Применение неправильных настроек может вывести из строя оборудование! Продолжить?",
"network": "Применение этих настроек может сделать модем недоступным! Продолжить?"
},
"params": {
"rxtx": [
{"widget": "h2", "label": "Настройки приема/передачи"},
{
"widget": "flex-container",
"childs": [
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки передатчика"},
{"widget": "checkbox", "label": "Включить передатчик", "name": "txEn"},
{
"widget": "select", "label": "Режим работы модулятора", "name": "txModulatorIsTest",
"values": [{"label": "Нормальный", "value": "false"}, {"label": "Тест (CW)", "value": "true"}]
},
{"widget": "number-int", "label": "Центральная частота, КГц", "name": "txCentralFreq", "min": 900000, "step": 0.01, "v_show": "paramRxtx.txModulatorIsTest"},
{"widget": "number", "label": "Ослабление, дБ", "name": "txAttenuation", "max": 0, "min": -40, "step": 1}
]
},
{
"widget": "settings-container",
"childs": [
{ "widget": "h3", "label": "Настройки приемника" },
{
"widget": "select", "label": "Режим управления усилением", "name": "rxAgcEn",
"values": [
{"label": "АРУ", "value": "true"},
{"label": "РРУ", "value": "false"}
]
},
{"widget": "number", "label": "Ручное усиление, дБ", "name": "rxManualGain", "v_show": "!paramRxtx.rxAgcEn", "min": -40},
{"widget": "checkbox", "label": "Инверсия спектра", "name": "rxSpectrumInversion"},
{"widget": "number-int", "label": "Центральная частота, КГц", "name": "rxCentralFreq", "min": 900000, "step": 0.01},
{"widget": "number-int", "label": "Символьная скорость, Бод", "name": "rxBaudrate", "min": 200000, "max": 54000000, "step": 1},
{
"widget": "select", "label": "Roll-off", "name": "rxRolloff",
"values": [{"label": "0.02", "value": "2"}, {"label": "0.05", "value": "5"}, {"label": "0.10", "value": "10"}, {"label": "0.15", "value": "15"}, {"label": "0.20", "value": "20"}, {"label": "0.25", "value": "25"}]
}
]
}
]
}
],
"buclnb": [
{"widget": "h2", "label": "Настройки питания и опорного генератора"},
{
"widget": "flex-container",
"childs": [
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки BUC"},
{"widget": "checkbox", "label": "Подача опоры 10МГц", "name": "bucRefClk10M"},
{
"widget": "select", "label": "Питание BUC", "name": "bucPowering",
"values": [
{"label": "Выкл", "value": "0"},
{"label": "24В", "value": "24"}
]
}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки LNB"},
{"widget": "checkbox", "label": "Подача опоры 10МГц", "name": "lnbRefClk10M"},
{
"widget": "select", "label": "Питание LNB", "name": "lnbPowering",
"values": [
{"label": "Выкл", "value": "0"},
{"label": "13В", "value": "13"},
{"label": "18В", "value": "18"},
{"label": "24В", "value": "24"}
]
}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Сервисные настройки"},
{"widget": "checkbox", "label": "Подача опоры 10МГц на 'Выход 10МГц'", "name": "srvRefClk10M"},
{"widget": "checkbox", "label": "Автозапуск BUC и LNB при включении", "name": "bucLnbAutoStart"}
]
}
]
}
],
"dpdi": [
{"widget": "h2", "label": "Настройки DPDI"},
{
"widget": "settings-container",
"childs": [
{
"widget": "select", "label": "Метод расчета задержки", "name": "dpdiIsPositional",
"values": [
{"label": "Позиционированием", "value": "true"},
{"label": "Окном задержки", "value": "false"}
]
},
{"widget": "h2", "label": "Настройки позиционирования", "v_show": "paramDpdi.dpdiIsPositional === true"},
{"widget": "number", "label": "Широта станции", "name": "dpdiPositionStationLatitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "number", "label": "Долгота станции", "name": "dpdiPositionStationLongitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "number", "label": "Подспутниковая точка", "name": "dpdiPositionSatelliteLongitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "number", "label": "Задержка до спутника, мс", "name": "dpdiDelay", "v_show": "paramDpdi.dpdiIsPositional === false", "min": 0, "step": 0.1, "max": 400}]
},
{"widget": "submit"}
],
"network": [
{"widget": "h2", "label": "Настройки сети"},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки интерфейса управления"},
{"widget": "ip-address", "label": "Интерфейс управления (/24)", "name": "netManagementIp"},
{
"widget": "select", "label": "Режим сети", "name": "netIsL2",
"values": [{"label": "Маршрутизатор", "value": "false"}, {"label": "Коммутатор", "value": "true"}]
},
{"widget": "ip-address", "label": "Интерфейс данных (/24)", "name": "netDataIp", "v_show": "paramNetwork.netIsL2 === false"},
{"widget": "number", "label": "MTU интерфейса данных", "name": "netDataMtu", "min": 1500, "step": 1, "max": 2000},
{"widget": "text", "label": "Имя веб-сервера", "name": "netServerName"}
]
}
]
},
"tabs": [ "tabs": [
{"name": "monitoring", "desc": "Мониторинг"}, {
{"name": "setup", "desc": "Настройки"}, "name": "monitoring",
{"name": "admin", "desc": "Администрирование"} "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": { "scpc": {
"modem_name": "RCSM-101", "modem_name": "RCSM-101",
"dangerousParamGroups": { "groupsList": ["rxtx"],
"buclnb": "Применение неправильных настроек может вывести из строя оборудование! Продолжить?",
"network": "Применение этих настроек может сделать модем недоступным! Продолжить?"
},
"params": {
"rxtx": [
{"widget": "h2", "label": "Настройки приема/передачи"},
{
"widget": "settings-container",
"childs": [
{
"widget": "select", "label": "Режим работы", "name": "isCinC",
"values": [{"label": "SCPC", "value": "false"}, {"label": "CinC", "value": "true"}]
}
]
},
{
"widget": "flex-container",
"childs": [
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки передатчика"},
{"widget": "checkbox", "label": "Включить передатчик", "name": "txEn"},
{"widget": "checkbox", "label": "Автоматический запуск передатчика", "name": "txAutoStart"},
{
"widget": "select", "label": "Режим работы модулятора", "name": "txModulatorIsTest",
"values": [{"label": "Нормальный", "value": "false"}, {"label": "Тест (CW)", "value": "true"}]
},
{
"widget": "select", "label": "Входные данные", "name": "txIsTestInput",
"values": [{"label": "Ethernet", "value": "false"}, {"label": "Тест", "value": "true"}]
},
{"widget": "h3", "label": "Параметры передачи"},
{"widget": "number-int", "label": "Центральная частота, КГц", "name": "txCentralFreq", "min": 950000, "max": 6000000, "step": 0.01},
{"widget": "number-int", "label": "Символьная скорость, Бод", "name": "txBaudrate", "min": 200000, "max": 54000000},
{
"widget": "select", "label": "Roll-off", "name": "txRolloff",
"values": [{"label": "0.02", "value": "2"}, {"label": "0.05", "value": "5"}, {"label": "0.10", "value": "10"}, {"label": "0.15", "value": "15"}, {"label": "0.20", "value": "20"}, {"label": "0.25", "value": "25"}]
},
{
"widget": "select", "label": "Номер последовательности Голда", "name": "txGoldan",
"values": [{"label": "0", "value": "0"}, {"label": "1", "value": "1"}]
},
{"widget": "number", "label": "Ослабление, дБ", "name": "txAttenuation", "max": 0, "min": -40, "step": 0.25}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Режим работы DVB-S2"},
{"widget": "number", "label": "Период служебных пакетов, сек", "name": "dvbServicePacketPeriod", "min": 0, "step": 1, "max": 60},
{
"widget": "select", "label": "Режим модуляции", "name": "dvbIsAcm",
"values": [{"label": "CCM", "value": "false"}, {"label": "ACM", "value": "true"}]
},
{
"widget": "select", "label": "Размер кадра", "name": "txFrameSizeNormal",
"values": [{"label": "normal", "value": "true"}, {"label": "short", "value": "false"}]
},
{"widget": "modulation-modcod", "label": "Модуляция", "name": "dvbCcm", "v_show": "paramRxtx.dvbIsAcm === false"},
{"widget": "modulation-speed", "label": "Скорость кода", "name": "dvbCcm", "v_show": "paramRxtx.dvbIsAcm === false"},
{"widget": "watch-expr", "label": "Расчетная скорость", "expr": "calcInterfaceSpeedKb(paramRxtx.txBaudrate, paramRxtx.dvbCcmModulation, paramRxtx.dvbCcmSpeed, paramRxtx.txFrameSizeNormal)", "v_show": "paramRxtx.dvbIsAcm === false"},
{"widget": "watch-expr", "label": "Текущий модкод", "expr": "statTx.modcod", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "modulation-modcod", "label": "Модуляция (мин. режим)", "name": "dvbAcmMin", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "modulation-speed", "label": "Скорость кода (мин. режим)", "name": "dvbAcmMin", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "watch-expr", "label": "Расчетная скорость (мин. режим)", "expr": "calcInterfaceSpeedKb(paramRxtx.txBaudrate, paramRxtx.dvbAcmMinModulation, paramRxtx.dvbAcmMinSpeed, paramRxtx.txFrameSizeNormal)", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "modulation-modcod", "label": "Модуляция (макс. режим)", "name": "dvbAcmMax", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "modulation-speed", "label": "Скорость кода (макс. режим)", "name": "dvbAcmMax", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "watch-expr", "label": "Расчетная скорость (макс. режим)", "expr": "calcInterfaceSpeedKb(paramRxtx.txBaudrate, paramRxtx.dvbAcmMaxModulation, paramRxtx.dvbAcmMaxSpeed, paramRxtx.txFrameSizeNormal)", "v_show": "paramRxtx.dvbIsAcm === true"},
{"widget": "number", "label": "Запас ОСШ, дБ", "name": "dvbSnrReserve", "min": 0, "step": 0.01, "max": 10}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Авто-регулировка мощности"},
{"widget": "checkbox", "label": "Авто-регулировка мощности", "name": "aupcEn"},
{"widget": "number", "label": "Минимальное ослабление, дБ", "name": "aupcMinAttenuation", "min": 0, "step": 0.1, "max": 10},
{"widget": "number", "label": "Максимальное ослабление, дБ", "name": "aupcMaxAttenuation", "min": 0, "step": 0.1, "max": 10},
{"widget": "number", "label": "Требуемое ОСШ", "name": "aupcRequiredSnr", "min": 0, "step": 0.01, "max": 10}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки приемника"},
{
"widget": "select", "label": "Режим управления усилением", "name": "rxAgcEn",
"values": [{"label": "РРУ", "value": "false"}, {"label": "АРУ", "value": "true"}]
},
{"widget": "number", "label": "Усиление, дБ", "name": "rxManualGain", "min": -40, "step": 0.01, "max": 40, "v_show": "paramRxtx.rxAgcEn === false"},
{"widget": "watch-expr", "label": "Текущее усиление", "expr": "paramRxtx.rxManualGain", "v_show": "paramRxtx.rxAgcEn === true"},
{"widget": "checkbox", "label": "Инверсия спектра", "name": "rxSpectrumInversion"},
{"widget": "number-int", "label": "Центральная частота, КГц", "name": "rxCentralFreq", "min": 950000, "max": 6000000, "step": 0.01},
{"widget": "number-int", "label": "Символьная скорость, Бод", "name": "rxBaudrate", "min": 200000, "max": 54000000},
{
"widget": "select", "label": "Roll-off", "name": "rxRolloff",
"values": [{"label": "0.02", "value": "2"}, {"label": "0.05", "value": "5"}, {"label": "0.10", "value": "10"}, {"label": "0.15", "value": "15"}, {"label": "0.20", "value": "20"}, {"label": "0.25", "value": "25"}]
},
{
"widget": "select", "label": "Номер последовательности Голда", "name": "rxGoldan",
"values": [{"label": "0", "value": "0"}, {"label": "1", "value": "1"}]
}
]
}
]
}
],
"dpdi": [
{"widget": "h2", "label": "Настройки режима CinC", "v_show": "paramRxtx.isCinC"},
{
"widget": "settings-container", "v_show": "paramRxtx.isCinC",
"childs": [
{
"widget": "select", "label": "Метод расчета задержки", "name": "dpdiIsPositional",
"values": [
{"label": "Позиционированием", "value": "true"},
{"label": "Окном задержки", "value": "false"}
]
},
{"widget": "number", "label": "Полоса поиска, КГц ±", "name": "dpdiSearchBandwidth", "min": 0, "step": 1, "max": 100},
{"widget": "h2", "label": "Настройки позиционирования", "v_show": "paramDpdi.dpdiIsPositional === true"},
{"widget": "number", "label": "Широта станции", "name": "dpdiPositionStationLatitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "number", "label": "Долгота станции", "name": "dpdiPositionStationLongitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "number", "label": "Подспутниковая точка", "name": "dpdiPositionSatelliteLongitude", "v_show": "paramDpdi.dpdiIsPositional === true", "min": -180, "step": 0.000001, "max": 180},
{"widget": "h2", "label": "Задержка до спутника", "v_show": "paramDpdi.dpdiIsPositional === false"},
{"widget": "number", "label": "от, мс", "name": "dpdiDelayMin", "v_show": "paramDpdi.dpdiIsPositional === false", "min": 0, "step": 0.1, "max": 400},
{"widget": "number", "label": "до, мс", "name": "dpdiDelayMax", "v_show": "paramDpdi.dpdiIsPositional === false", "min": 0, "step": 0.1, "max": 400}]
},
{"widget": "submit", "v_show": "paramRxtx.isCinC"}
],
"buclnb": [
{"widget": "h2", "label": "Настройки питания и опорного генератора"},
{
"widget": "flex-container",
"childs": [
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки BUC"},
{"widget": "checkbox", "label": "Подача опоры 10МГц", "name": "bucRefClk10M"},
{
"widget": "select", "label": "Питание BUC", "name": "bucPowering",
"values": [
{"label": "Выкл", "value": "0"},
{"label": "24В", "value": "24"},
{"label": "48В", "value": "48"}
]
}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки LNB"},
{"widget": "checkbox", "label": "Подача опоры 10МГц", "name": "lnbRefClk10M"},
{
"widget": "select", "label": "Питание LNB", "name": "lnbPowering",
"values": [
{"label": "Выкл", "value": "0"},
{"label": "13В", "value": "13"},
{"label": "18В", "value": "18"},
{"label": "24В", "value": "24"}
]
}
]
},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Сервисные настройки"},
{"widget": "checkbox", "label": "Подача опоры 10МГц на 'Выход 10МГц'", "name": "srvRefClk10M"},
{"widget": "checkbox", "label": "Автозапуск BUC и LNB при включении", "name": "bucLnbAutoStart"}
]
}
]
}
],
"tcpaccel": [
{"widget": "h2", "label": "Настройки TCP-акселерации"},
{
"widget": "settings-container",
"childs": [
{"widget": "checkbox", "label": "Активировать акселерацию", "name": "accelEn"},
{"widget": "number", "label": "Максимальное количество соединений", "name": "accelMaxConnections", "min": 0, "step": 1, "max": 4000}
]
}
],
"network": [
{"widget": "h2", "label": "Настройки сети"},
{
"widget": "settings-container",
"childs": [
{"widget": "h3", "label": "Настройки интерфейса управления"},
{"widget": "ip-address", "label": "Интерфейс управления (/24)", "name": "netManagementIp"},
{
"widget": "select", "label": "Режим сети", "name": "netIsL2",
"values": [{"label": "Маршрутизатор", "value": "false"}, {"label": "Коммутатор", "value": "true"}]
},
{"widget": "ip-address", "label": "Интерфейс данных (/24)", "name": "netDataIp", "v_show": "paramNetwork.netIsL2 === false"},
{"widget": "number", "label": "MTU интерфейса данных", "name": "netDataMtu", "min": 1500, "step": 1, "max": 2000},
{"widget": "text", "label": "Имя веб-сервера", "name": "netServerName"}
]
}
]
},
"tabs": [ "tabs": [
{"name": "monitoring", "desc": "Мониторинг"}, {
{"name": "setup", "desc": "Настройки"}, "name": "monitoring",
{"name": "qos", "desc": "QoS"}, "desc": "Мониторинг"
{"name": "admin", "desc": "Администрирование"} },
{
"name": "setup",
"desc": "Настройки"
},
{
"name": "qos",
"desc": "QoS"
},
{
"name": "admin",
"desc": "Администрирование"
}
] ]
} }
} }

View File

@ -1,99 +1,22 @@
import json import json
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
import sys import sys
import os
with open('render-params.json') as f:
GLOBAL_CONFIG = json.load(f)
def extract_param_names(mc):
result = []
def helper_extract(widget):
if 'childs' in widget:
r = []
for child in widget['childs']:
r += helper_extract(child)
return r
elif 'name' in widget:
copy_fields = {"widget": widget['widget'], "name": widget['name']}
if 'min' in widget:
copy_fields['min'] = widget['min']
if 'max' in widget:
copy_fields['max'] = widget['max']
if 'step' in widget:
copy_fields['step'] = widget['step']
match widget['widget']:
case 'select': return [{"initValue": widget['values'][0]['value']} | copy_fields]
case 'checkbox': return [{"initValue": 'false'} | copy_fields]
case 'number': return [{"initValue": widget['min'] if widget['min'] else '0'} | copy_fields]
case 'number-int': return [{"initValue": "0"} | copy_fields]
case 'modulation-modcod': return [copy_fields | {"name": widget['name'] + "Modulation", "initValue": '"QPSK"'}]
case 'modulation-speed': return [copy_fields | {"name": widget['name'] + "Speed", "initValue": '"1/4"'}]
case 'watch': return []
return [{"initValue": 'null'} | copy_fields]
return []
for cat in mc['params']:
ws = []
for w in mc['params'][cat]:
ws += helper_extract(w)
# ws.sort(key=lambda k: k['name'])
result.append({
"group": cat,
"params": ws
})
return result
def add_submit_widgets(params):
def find_submit(w):
if w['widget'] == 'submit':
return True
if 'childs' in w:
for c in w['childs']:
if find_submit(c):
return True
return False
for group in params:
wid_found = False
for wid in params[group]:
if find_submit(wid):
wid_found = True
break
if wid_found:
continue
params[group].append({"widget": "submit"})
def extract_param_groups(mc):
return [k for k in mc['params']]
def build_modem_env(modem): def build_modem_env(modem):
if modem not in GLOBAL_CONFIG['modem_types']: 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!") raise RuntimeError(f"Modem '{modem}' is not exist in config!")
mc = GLOBAL_CONFIG['modem_types'][modem] mc = config['modem_types'][modem]
add_submit_widgets(mc['params'])
return { return {
"modem": modem,
"modem_name": mc['modem_name'], "modem_name": mc['modem_name'],
"header_tabs": mc['tabs'], "header_tabs": mc['tabs'],
"tab_names_array": [t['name'] for t in mc['tabs']], "js_tabs_array": str([t['name'] for t in mc['tabs']]),
"params": mc["params"], "params": {"groupsList": mc["groupsList"]} | config["params"]
"dangerousParamGroups": mc["dangerousParamGroups"] if 'dangerousParamGroups' in mc else {},
"paramGroups": extract_param_names(mc),
"paramGroupsList": extract_param_groups(mc),
} }
@ -109,8 +32,8 @@ def render_modem(modem):
if __name__ == '__main__': if __name__ == '__main__':
for mt in GLOBAL_CONFIG['modem_types']: if len(sys.argv) != 2:
print(f'Generating {mt} modem...') print(f"Usage: {sys.argv[0]} <scpc|tdma>")
render_modem(mt)
os.system(f'cp -u main-{mt}.html ../static') render_modem(sys.argv[1])

View File

@ -1,74 +0,0 @@
async settingsUploadUpdate() {
if (!this.uploadFw.filename) {
alert('Выберите файл для загрузки');
return;
}
async function readFileAsArrayBuffer(fileName) {
return new Promise((resolve, reject) => {
if (!fileName) { reject(`Файл не выбран`); return }
const reader = new FileReader();
reader.onload = (e) => { resolve(reader.result) }
reader.onerror = (e) => { reject(e) }
reader.readAsArrayBuffer(fileName)
})
}
try {
this.submitStatus.firmwareUpload = true
this.uploadFw.progress = 0
const blob = await readFileAsArrayBuffer(this.uploadFw.filename)
const xhr = new XMLHttpRequest();
await new Promise((resolve) => {
xhr.upload.addEventListener("progress", (event) => {
if (event.lengthComputable) {
this.uploadFw.progress = Math.round((event.loaded / event.total) * 1000) / 10;
}
});
xhr.addEventListener("loadend", () => {
this.uploadFw.progress = 100
const rep = JSON.parse(xhr.responseText);
this.uploadFw.sha256 = rep['sha256']
resolve(xhr.readyState === 4 && xhr.status === 200);
});
xhr.open("PUT", "/api/firmwareUpdate", true);
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.send(blob);
});
} catch (e) {
alert(`Ошибка загрузки файла: ${e}`);
}
this.submitStatus.firmwareUpload = false
},
async settingsPerformFirmwareUpgrade() {
if (this.submitStatus.firmwareUpgrade) { return }
this.submitStatus.firmwareUpgrade = true
try {
await fetch('/api/doFirmwareUpgrade', { method: 'POST' })
} catch (e) {
console.log("failed to perform upgrade firmware: ", e)
}
this.submitStatus.firmwareUpgrade = false
},
{% if modem == 'tdma' %}
async settingsPerformFirmwareUpgradeOta() {
if (this.submitStatus.firmwareUpgradeOta) { return }
this.submitStatus.firmwareUpgradeOta = true
try {
await fetch('/api/doFirmwareUpgrade?ota=1', { method: 'POST' })
} catch (e) {
console.log("failed to perform upgrade firmware: ", e)
}
this.submitStatus.firmwareUpgradeOta = false
},
{% endif %}
doModemReboot() {
if (this.submitStatus.modemReboot !== null) {
return
}
this.submitStatus.modemReboot = 30
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
},

View File

@ -1,41 +0,0 @@
{% from 'common/widgets.j2' import build_widget %}
<div class="tabs-body-item" v-if="activeTab === 'admin' && settingFetchComplete">
{% if 'network' in params %}
{% for w in params['network'] %}{{ build_widget('network', w) | indent(12, true) }}{% endfor %}
{% endif %}
{% raw %}
<h2>Система</h2>
<div class="settings-set-container statistics-container">
<table>
<tbody>
<tr><th>Версия ПО</th><td>{{ about.firmwareVersion }}</td></tr>
<tr><th>ID модема</th><td>{{ about.modemUid }}</td></tr>
<tr><th>Серийный номер</th><td>{{ about.modemSn }}</td></tr>
<tr><th>MAC интерфейса управления</th><td>{{ about.macManagement }}</td></tr>
<tr><th>MAC интерфейса данных</th><td>{{ about.macData }}</td></tr>
</tbody>
</table>
<div>
<button class="dangerous-button" @click="doModemReboot()">Перезагрузить модем <span class="submit-spinner" v-show="submitStatus.modemReboot !== null"></span></button>
</div>
<div>
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
</div>
</div>
<h2>Обновление ПО</h2>
<div class="settings-set-container statistics-container">
<h3>Ручное обновление</h3>
<label>
<span>Файл {{ this.uploadFw.progress !== null ? `(${this.uploadFw.progress}%)` : '' }}</span>
<input type="file" accept="application/zip" @change="(e) => { this.uploadFw.filename = e.target.files[0] }">
<span v-if="uploadFw.sha256 !== null">SHA256: {{ uploadFw.sha256 }}</span>
</label>
<button class="action-button" @click="settingsUploadUpdate()">Загрузить<span class="submit-spinner" v-show="submitStatus.firmwareUpload"></span></button>
<button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button>
{% endraw %}{% if modem == 'tdma' %}
<h3 v-show="statDevice.upgradePercent >= 100">Обновление "по воздуху"</h3>
<button class="dangerous-button" v-show="statDevice.upgradePercent >= 100" @click="settingsPerformFirmwareUpgradeOta()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgradeOta"></span></button>
{% endif %}
</div>
</div>

View File

@ -1,7 +0,0 @@
{% for g in paramGroups %}
param{{ g['group'] | title }}: {
{% for p in g['params'] %}
{{ p['name'] }}: {{ p['initValue'] }},
{% endfor %}
},
{% endfor %}

View File

@ -1,30 +0,0 @@
{% from 'common/widgets.j2' import build_getter_js, build_setter_js %}
{% for g in paramGroups %}
settingsSubmit{{ g['group'] | title }}() {
if (this.submitStatus.{{ g['group'] }}) { return }
{% if g['group'] in dangerousParamGroups %}
{ if (!confirm("{{ dangerousParamGroups[g['group']] }}")) return }
{% endif %}
let query = {
{% for p in g['params'] %}
"{{ p['name'] }}": {{ build_getter_js(g['group'], p) }},
{% endfor %}
}
this.submitStatus.{{ g['group'] }} = true
fetch('/api/set/{{ g["group"] }}', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query), credentials: 'same-origin' })
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.update{{ g['group'] | title }}Settings(vals) })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.{{ g['group'] }} = false })
},
{% endfor %}
{% for g in paramGroups %}
update{{ g['group'] | title }}Settings(vals) {
this.submitStatus.{{ g['group'] }} = false
{% for p in g['params'] %}
{{ build_setter_js(g['group'], p, "vals[\"settings\"][\"" ~ p['name'] ~ "\"]") }}
{% endfor %}
},
{% endfor %}

View File

@ -1,50 +0,0 @@
statRx: {
// индикаторы
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: '?',
},
statTx: {
// состояние
state: '?',
// прочие поля
{% if modem == 'scpc' %}
snr: '?', modcod: '?', frameSizeNormal: '?', isPilots: '?', speedOnTxKbit: '?', speedOnIifKbit: '?'
{% else %}
modcod: '?', speedOnTxKbit: '?', speedOnIifKbit: '?', centerFreq: '?', symSpeed: '?'
{% endif %}
},
{% if modem == 'scpc' %}
statCinc: {
occ: '?',
correlator: null,
correlatorFails: '?',
freqErr: '?', freqErrAcc: '?',
channelDelay: '?'
},
{% endif %}
statDevice: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0
{% if modem == 'tdma' %},
upgradeStatus: "", upgradePercent: 0, upgradeImage: ""
{% endif %}
},
statOs: {uptime: '?', load1: '?', load5: '?', load15: '?', totalram: '?', freeram: '?'},

View File

@ -1,111 +0,0 @@
updateStatistics(vals) {
function modcodToStr(modcod) {
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
const modcods = [
"DUMMY",
"QPSK 1/4", "QPSK 1/3", "QPSK 2/5", "QPSK 1/2", "QPSK 3/5", "QPSK 2/3", "QPSK 3/4", "QPSK 4/5", "QPSK 5/6", "QPSK 8/9", "QPSK 9/10",
"8PSK 3/5", "8PSK 2/3", "8PSK 3/4", "8PSK 5/6", "8PSK 8/9", "8PSK 9/10",
"16APSK 2/3", "16APSK 3/4", "16APSK 4/5", "16APSK 5/6", "16APSK 8/9", "16APSK 9/10",
"32APSK 3/4", "32APSK 4/5", "32APSK 5/6", "32APSK 8/9", "32APSK 9/10",
]
if (typeof modcod != "number") {
return "?";
}
if (modcod < 0 || modcod >= modcods.length) {
return `? (${modcod})`
}
return modcods[modcod]
}
this.lastUpdateTime = new Date();
this.initState = vals["mainState"]["initState"]
{% if modem == 'scpc' %}
this.isCinC = vals["mainState"]["isCinC"]
{% endif %}
this.statRx.state = vals["mainState"]["rx.state"]
this.statRx.sym_sync_lock = vals["mainState"]["rx.sym_sync_lock"]
this.statRx.freq_search_lock = vals["mainState"]["rx.freq_search_lock"]
this.statRx.afc_lock = vals["mainState"]["rx.afc_lock"]
this.statRx.pkt_sync = vals["mainState"]["rx.pkt_sync"]
this.statRx.snr = vals["mainState"]["rx.snr"]
this.statRx.rssi = vals["mainState"]["rx.rssi"]
this.statRx.modcod = modcodToStr(vals["mainState"]["rx.modcod"])
this.statRx.frameSizeNormal = vals["mainState"]["rx.frameSizeNormal"]
this.statRx.isPilots = vals["mainState"]["rx.isPilots"]
this.statRx.symError = vals["mainState"]["rx.symError"]
this.statRx.freqErr = vals["mainState"]["rx.freqErr"]
this.statRx.freqErrAcc = vals["mainState"]["rx.freqErrAcc"]
this.statRx.inputSignalLevel = vals["mainState"]["rx.inputSignalLevel"]
this.statRx.pllError = vals["mainState"]["rx.pllError"]
this.statRx.speedOnRxKbit = vals["mainState"]["rx.speedOnRxKbit"]
this.statRx.speedOnIifKbit = vals["mainState"]["rx.speedOnIifKbit"]
this.statRx.packetsOk = vals["mainState"]["rx.packetsOk"]
this.statRx.packetsBad = vals["mainState"]["rx.packetsBad"]
this.statRx.packetsDummy = vals["mainState"]["rx.packetsDummy"]
{% if modem == 'scpc' %}
this.statTx.state = vals["mainState"]["tx.state"]
this.statTx.snr = vals["mainState"]["tx.snr"]
this.statTx.modcod = modcodToStr(vals["mainState"]["tx.modcod"])
this.statTx.frameSizeNormal = vals["mainState"]["tx.frameSizeNormal"]
this.statTx.isPilots = vals["mainState"]["tx.isPilots"]
this.statTx.speedOnTxKbit = vals["mainState"]["tx.speedOnTxKbit"]
this.statTx.speedOnIifKbit = vals["mainState"]["tx.speedOnIifKbit"]
this.statCinc.occ = vals["mainState"]["cinc.occ"]
this.statCinc.correlator = vals["mainState"]["cinc.correlator"]
this.statCinc.correlatorFails = vals["mainState"]["cinc.correlatorFails"]
this.statCinc.freqErr = vals["mainState"]["cinc.freqErr"]
this.statCinc.freqErrAcc = vals["mainState"]["cinc.freqErrAcc"]
this.statCinc.channelDelay = vals["mainState"]["cinc.channelDelay"]
{% else %}
this.statTx.state = vals["mainState"]["tx.state"]
this.statTx.modcod = modcodToStr(vals["mainState"]["tx.modcod"])
this.statTx.speedOnTxKbit = vals["mainState"]["tx.speedOnTxKbit"]
this.statTx.speedOnIifKbit = vals["mainState"]["tx.speedOnIifKbit"]
this.statTx.centerFreq = vals["mainState"]["tx.centerFreq"]
this.statTx.symSpeed = vals["mainState"]["tx.symSpeed"]
{% endif %}
this.statDevice.adrv = vals["mainState"]["device.adrv"]
this.statDevice.zynq = vals["mainState"]["device.zynq"]
this.statDevice.fpga = vals["mainState"]["device.fpga"]
{% if modem == 'tdma' %}
this.statDevice.upgradeStatus = vals["mainState"]["device.upgradeStatus"]
this.statDevice.upgradePercent = vals["mainState"]["device.upgradePercent"]
this.statDevice.upgradeImage = vals["mainState"]["device.upgradeImage"]
{% endif %}
this.testState = vals["mainState"]["testState"]
// аптайм приходит в секундах, надо преобразовать его в человеко-читаемый вид
let uptime = vals["sysinfo"]["uptime"]
if (uptime) {
let secs = uptime % 60; uptime = Math.floor(uptime / 60)
let mins = uptime % 60; uptime = Math.floor(uptime / 60)
let hours = uptime % 24
uptime = Math.floor( uptime / 24)
let res = `${hours}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
if (uptime > 0) { res = `${uptime} дней, ` + res }
this.statOs.uptime = res
} else {
this.statOs.uptime = '?'
}
this.statOs.load1 = vals["sysinfo"]["load1min"]
this.statOs.load5 = vals["sysinfo"]["load5min"]
this.statOs.load15 = vals["sysinfo"]["load15min"]
this.statOs.totalram = vals["sysinfo"]["totalram"]
this.statOs.freeram = vals["sysinfo"]["freeram"]
},
resetPacketsStatistics() {
fetch('/api/resetPacketStatistics', {
method: 'POST', credentials: 'same-origin'
}).then(() => {
this.statRx.packetsOk = 0
this.statRx.packetsBad = 0
this.statRx.packetsDummy = 0
})
},

View File

@ -1,84 +0,0 @@
{% raw %}
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container statistics-container">
<h2>Статистика приема</h2>
<table>
<tbody>
<tr><th>Прием</th><td><span :class="{ indicator_bad: statRx.state === false, indicator_good: statRx.state === true, indicator: true }"></span></td></tr>
<tr><th>Захват символьной</th><td><span :class="{ indicator_bad: statRx.sym_sync_lock === false, indicator_good: statRx.sym_sync_lock === true, indicator: true }"></span></td></tr>
<tr><th>Захват ФАПЧ</th><td><span :class="{ indicator_bad: statRx.afc_lock === false, indicator_good: statRx.afc_lock === true, indicator: true }"></span></td></tr>
<tr><th>Захват поиска по частоте</th><td><span :class="{ indicator_bad: statRx.freq_search_lock === false, indicator_good: statRx.freq_search_lock === true, indicator: true }"></span></td></tr>
<tr><th>Захват пакетной синхр.</th><td><span :class="{ indicator_bad: statRx.pkt_sync === false, indicator_good: statRx.pkt_sync === true, indicator: true }"></span></td></tr>
<tr><th>SNR/RSSI</th><td>{{ statRx.snr }} / {{ statRx.rssi }}</td></tr>
<tr><th>Modcod</th><td>{{ statRx.modcod }}</td></tr>
<tr><th>Размер кадра</th><td>{{ statRx.frameSizeNormal ? 'normal' : 'short' }}</td></tr>
<tr><th>Пилот-символы</th><td>{{ statRx.isPilots ? 'pilots' : 'no pilots' }}</td></tr>
<tr><th>Символьная ошибка</th><td>{{ statRx.symError }}</td></tr>
<tr><th>Грубая/точная част. ошибка, Гц</th><td>{{ statRx.freqErr }} / {{ statRx.freqErrAcc }}</td></tr>
<tr><th>Ур. входного сигнала</th><td>{{ statRx.inputSignalLevel }}</td></tr>
<tr><th>Ошибка ФАПЧ</th><td>{{ statRx.pllError }}</td></tr>
<tr><th>Инф. скорость на приеме</th><td>{{ statRx.speedOnRxKbit }} кбит/с</td></tr>
<tr><th>Инф. скорость на интерфейсе</th><td>{{ statRx.speedOnIifKbit }} кбит/с</td></tr>
<tr><td colspan="2" style="padding-top: 1em; text-align: center">Статистика пакетов</td></tr>
<tr><th>Качественных пакетов</th><td>{{ statRx.packetsOk }}</td></tr>
<tr><th>Поврежденных пакетов</th><td>{{ statRx.packetsBad }}</td></tr>
<tr><th>DUMMY</th><td>{{ statRx.packetsDummy }}</td></tr>
</tbody>
</table>
<button class="action-button" @click="resetPacketsStatistics()"> Сброс статистики </button>
</div>
<div class="settings-set-container statistics-container">
<h2>Статистика передачи</h2>{% endraw %}{% if modem == 'scpc' %}{% raw %}
<table>
<tbody>
<tr><th>Передача</th><td><span :class="{ indicator_bad: statTx.state === false, indicator_good: statTx.state === true, indicator: true }"></span></td></tr>
<tr><th>ОСШ дальнего приема</th><td>{{ statTx.snr }}</td></tr>
<tr><th>Modcod</th><td>{{ statTx.modcod }}</td></tr>
<tr><th>Размер кадра</th><td>{{ statTx.frameSizeNormal ? 'normal' : 'short' }}</td></tr>
<tr><th>Пилот-символы</th><td>{{ statTx.isPilots ? 'pilots' : 'no pilots' }}</td></tr>
<tr><th>Инф. скорость на передаче</th><td>{{ statTx.speedOnTxKbit }} кбит/с</td></tr>
<tr><th>Инф. скорость на интерфейсе</th><td>{{ statTx.speedOnIifKbit }} кбит/с</td></tr>
</tbody>
</table>{% endraw %}{% else %}{% raw %}
<table>
<tbody>
<tr><th>Передача</th><td><span :class="{ indicator_bad: statTx.state === false, indicator_good: statTx.state === true, indicator: true }"></span></td></tr>
<tr><th>Modcod</th><td>{{ statTx.modcod }}</td></tr>
<tr><th>Инф. скорость на передаче</th><td>{{ statTx.speedOnTxKbit }} кбит/с</td></tr>
<tr><th>Инф. скорость на интерфейсе</th><td>{{ statTx.speedOnIifKbit }} кбит/с</td></tr>
<tr><th>Центральная частота</th><td>{{ statTx.centerFreq }} кГц</td></tr>
<tr><th>Символьная скорость</th><td>{{ statTx.symSpeed }} ksymb</td></tr>
</tbody>
</table>{% endraw %}{% endif %}{% raw %}
</div>{% endraw %}{% if modem == 'scpc' %}{% raw %}
<div class="settings-set-container statistics-container" v-if="paramRxtx.isCinC === true">
<h2>Статистика режима CinC</h2>
<table>
<tbody>
<tr><th>ОСС</th><td>{{ statCinc.occ }}</td></tr>
<tr><th>Захват коррелятора</th><td><span :class="{ indicator_bad: statCinc.correlator === false, indicator_good: statCinc.correlator === true, indicator: true }"></span></td></tr>
<tr><th>Кол-во срывов коррелятора</th><td>{{ statCinc.correlatorFails }}</td></tr>
<tr><th>Грубая/точная част. ошибка, Гц</th><td>{{ statCinc.freqErr }} / {{ statCinc.freqErrAcc }}</td></tr>
<tr><th>Задержка в канале, мс</th><td>{{ statCinc.channelDelay }}</td></tr>
</tbody>
</table>
</div>{% endraw %}{% endif %}{% raw %}
<div class="settings-set-container statistics-container">
<h2>Состояние устройства</h2>
<table>
<tbody>
<tr><th>Температура ADRV</th><td>{{ statDevice.adrv }} °C</td></tr>
<tr><th>Температура ZYNQ</th><td>{{ statDevice.zynq }} °C</td></tr>
<tr><th>Температура FPGA</th><td>{{ statDevice.fpga }} °C</td></tr>
<tr><th>Время работы устройства</th><td>{{ statOs.uptime }}</td></tr>
<tr><th>Средняя загрузка ЦП (1/5/15 мин.)</th><td>{{ statOs.load1 }}% {{ statOs.load5 }}% {{ statOs.load15 }}%</td></tr>
<tr><th>ОЗУ всего/свободно</th><td>{{ statOs.totalram }}МБ/{{ statOs.freeram }}МБ</td></tr>{% endraw %}{% if modem == 'tdma' %}{% raw %}
<tr><td colspan="2" style="padding-top: 1em; text-align: center">Статус обновления</td></tr>
<tr><th>Статус</th><td>{{ statDevice.upgradeStatus }}</td></tr>
<tr><th>Прогресс</th><td>{{ statDevice.upgradePercent }}%</td></tr>
<tr><th>Имя образа</th><td><code>{{ statDevice.upgradeImage }}</code></td></tr>{% endraw %}{% endif %}{% raw %}
</tbody>
</table>
</div>
</div>
{% endraw %}

View File

@ -1,8 +0,0 @@
submitStatusQos: false,
paramQos: {
en: false,
rt1: [],
rt2: [],
rt3: [],
cd: [],
},

View File

@ -1,222 +0,0 @@
settingsSubmitQoS() {
if (this.submitStatusQos) { return }
this.submitStatusQos = true
function _translateQosClass(trafficClass, qc) {
let res = {
cir: qc['cir'],
description: qc['description'],
filters: []
}
if (trafficClass === 'cd') {
res.pir = qc.pir
}
if (!qc.isEnabled) {
res.disabled = true
}
for (const fi in qc.filters) {
let filter = {}
if (qc['filters'][fi].vlan !== "") { filter['vlan'] = qc['filters'][fi].vlan }
if (qc['filters'][fi].proto.length > 0) {
let tmp = "";
for (let pid = 0; pid < qc['filters'][fi].proto.length; pid++) {
if (pid !== 0) { tmp += ',' }
tmp += qc['filters'][fi].proto[pid]
}
filter['proto'] = tmp
}
if (qc['filters'][fi].sport !== "") { filter['sport'] = qc['filters'][fi].sport }
if (qc['filters'][fi].dport !== "") { filter['dport'] = qc['filters'][fi].dport }
if (qc['filters'][fi].ip_src !== "") { filter['ip_src'] = qc['filters'][fi].ip_src }
if (qc['filters'][fi].ip_dest !== "") { filter['ip_dest'] = qc['filters'][fi].ip_dest }
if (qc['filters'][fi].dscp !== "") { filter['dscp'] = qc['filters'][fi].dscp }
if (Object.keys(filter).length === 0) { continue }
if (!qc.filters[fi].isEnabled) { filter['disabled'] = true }
res.filters.push(filter)
}
if (res.filters.length === 0) {
// автоматическое выключение класса, если правил нет
res.disabled = true
}
return res
}
let query = {
"en": this.paramQos.en,
"rt1": [],
"rt2": [],
"rt3": [],
"cd": []
}
for (let i = 0; i < this.paramQos.rt1.length; i++) { query.rt1.push(_translateQosClass('rt', this.paramQos.rt1[i])) }
for (let i = 0; i < this.paramQos.rt2.length; i++) { query.rt2.push(_translateQosClass('rt', this.paramQos.rt2[i])) }
for (let i = 0; i < this.paramQos.rt3.length; i++) { query.rt3.push(_translateQosClass('rt', this.paramQos.rt3[i])) }
for (let i = 0; i < this.paramQos.cd.length; i++) { query.cd.push(_translateQosClass('rt', this.paramQos.cd[i])) }
//console.log(query)
fetch('/api/set/qos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(query), credentials: 'same-origin'
}).then(async (resp) => {
this.submitStatusQos = false
if (resp['error']) { throw new Error(resp['error']) }
this.updateQosSettings(await resp.json())
}).catch((reason) => {
this.submitStatusQos = false
alert(`Ошибка при применении настроек: ${reason}`)
})
},
updateQosSettings(vals) {
this.submitStatusQos = false
this.paramQos.en = vals["settings"]["qosEnabled"]
const qosProfile = vals["settings"]["qosProfile"]
if (qosProfile !== null && qosProfile !== undefined) {
this.paramQos.rt1 = [] // .splice(0, this.paramQos.rt1.length)
this.paramQos.rt2 = [] // .splice(0, this.paramQos.rt2.length)
this.paramQos.rt3 = [] // .splice(0, this.paramQos.rt3.length)
this.paramQos.cd = [] // .splice(0, this.paramQos.cd.length)
for (let trafficClass in qosProfile) {
if (['rt1', 'rt2', 'rt3', 'cd'].indexOf(trafficClass) < 0) {
continue
}
if (Array.isArray(qosProfile[trafficClass])) {
for (let i = 0; i < qosProfile[trafficClass].length; i++) {
const qc = qosProfile[trafficClass][i]
let result = {
isEnabled: !qc.hasOwnProperty('disabled'),
cir: qc['cir'],
pir: 0,
description: qc['description'],
filters: []
}
if (trafficClass === 'cd') {
if (qc['pir']) {
result.pir = qc['pir']
}
}
for (let fi = 0; fi < qc['filters'].length; fi++) {
result.filters.push({
isEnabled: !qc['filters'][fi].hasOwnProperty('disabled'),
vlan: qc['filters'][fi].hasOwnProperty('vlan') ? qc['filters'][fi]['vlan'] : '',
proto: qc['filters'][fi].hasOwnProperty('proto') ? qc['filters'][fi]['proto'].split(',') : [],
sport: qc['filters'][fi].hasOwnProperty('sport') ? qc['filters'][fi]['sport'] : '',
dport: qc['filters'][fi].hasOwnProperty('dport') ? qc['filters'][fi]['dport'] : '',
ip_src: qc['filters'][fi].hasOwnProperty('ip_src') ? qc['filters'][fi]['ip_src'] : '',
ip_dest: qc['filters'][fi].hasOwnProperty('ip_dest') ? qc['filters'][fi]['ip_dest'] : '',
dscp: qc['filters'][fi].hasOwnProperty('dscp') ? qc['filters'][fi]['dscp'] : ''
})
}
switch (trafficClass) {
case 'rt1': this.paramQos.rt1.push(result); break
case 'rt2': this.paramQos.rt2.push(result); break
case 'rt3': this.paramQos.rt3.push(result); break
case 'cd': this.paramQos.cd.push(result); break
}
}
}
}
}
},
qosAddClass(name) {
let res = {
isEnabled: true,
cir: 0,
pir: 0,
description: "",
filters: []
}
switch (name) {
case 'rt1': this.paramQos.rt1.push(res); break
case 'rt2': this.paramQos.rt2.push(res); break
case 'rt3': this.paramQos.rt3.push(res); break
case 'cd': this.paramQos.cd.push(res); break
}
},
qosClassAddRule(name, index) {
let rule = {
isEnabled: true,
vlan: "",
proto: [],
sport: "",
dport: "",
ip_src: "",
ip_dest: "",
dscp: ""
}
switch (name) {
case 'rt1': this.paramQos.rt1[index].filters.push(rule); break
case 'rt2': this.paramQos.rt2[index].filters.push(rule); break
case 'rt3': this.paramQos.rt3[index].filters.push(rule); break
case 'cd': this.paramQos.cd[index].filters.push(rule); break
}
},
qosDelClass(name, index) {
switch (name) {
case 'rt1': this.paramQos.rt1.splice(index, 1); break
case 'rt2': this.paramQos.rt2.splice(index, 1); break
case 'rt3': this.paramQos.rt3.splice(index, 1); break
case 'cd': this.paramQos.cd.splice(index, 1); break
}
},
qosDelFilter(name, index, filterIndex) {
switch (name) {
case 'rt1': this.paramQos.rt1[index].filters.splice(filterIndex, 1); break
case 'rt2': this.paramQos.rt2[index].filters.splice(filterIndex, 1); break
case 'rt3': this.paramQos.rt3[index].filters.splice(filterIndex, 1); break
case 'cd': this.paramQos.cd[index].filters.splice(filterIndex, 1); break
}
},
qosGenerateRuleDescription(filter) {
// попытка 1: просто отобразить все фильтры
let result = ""
let isFirst = true;
for (const key in filter) {
if (key === "isEnabled" || !filter[key] || (key === "proto" && filter['proto'].length === 0)) {
continue
}
if (isFirst) {
isFirst = false;
} else {
result += '; '
}
result += `${key}: ${filter[key]}`
}
if (result === "") {
return "пустой"
}
const maxResultLen = 60
if (result.length > maxResultLen) {
// попытка 2, отобразить что вообще в этом фильтре использовалось
result = ""
isFirst = true;
for (const key in filter) {
if (key === "isEnabled" || !filter[key] || (key === "proto" && filter['proto'].length === 0)) {
continue
}
if (isFirst) {
isFirst = false;
} else {
result += ', '
}
result += `${key}`
}
}
return result
},

View File

@ -1,107 +0,0 @@
{% from 'common/widgets.j2' import build_widget %}
{% raw %}
<div class="tabs-body-item" v-if="activeTab === 'qos' && settingFetchComplete">
<h2>Настройки QoS</h2>
<div class="settings-set-container">
<label>
<span>Активировать QoS</span>
<span class="toggle-input"><input type="checkbox" v-model="paramQos.en" /><span class="slider"></span></span>
</label>
</div>
<div v-for="classesGroup in ['rt1', 'rt2', 'rt3', 'cd']">
<h3>Классы {{ classesGroup.toUpperCase() }} <button class="action-button" @click="qosAddClass(classesGroup)"> + </button></h3>
<details v-for="(qosClass, index) in paramQos[classesGroup]" :key="index" class="settings-set-container">
<summary>
<span v-if="classesGroup === 'cd'">#{{ index }} CIR={{ qosClass.cir }}кбит, PIR={{ qosClass.pir }}кбит {{ qosClass.description }}</span>
<span v-if="classesGroup !== 'cd'">#{{ index }} CBR={{ qosClass.cir }}кбит {{ qosClass.description }}</span>
<span class="summary-actions">
<label>
<span class="toggle-input">
<input type="checkbox" v-model="qosClass.isEnabled" />
<span class="slider"></span>
</span>
</label>
</span>
</summary>
<label>
<span v-if="classesGroup === 'cd'">CIR</span> <span v-if="classesGroup !== 'cd'">CBR</span>
<input v-model="qosClass.cir" type="number"/>
</label>
<label v-if="classesGroup === 'cd'">
<span>PIR</span>
<input v-model="qosClass.pir" type="number"/>
</label>
<label>
<span>Описание</span>
<input v-model="qosClass.description"/>
</label>
<h3>Фильтры ({{ qosClass.filters.length }})</h3>
<div>
<button class="action-button" @click="qosClassAddRule(classesGroup, index)">Добавить правило</button>
</div>
<details v-for="(filter, filterIndex) in qosClass.filters" :key="filterIndex" class="settings-set-container">
<summary>
<span>#{{ filterIndex }} {{ qosGenerateRuleDescription(filter) }}</span>
<span class="summary-actions">
<label>
<span class="toggle-input">
<input type="checkbox" v-model="filter.isEnabled" />
<span class="slider"></span>
</span>
</label>
<button class="dangerous-button" @click="qosDelFilter(classesGroup, index, filterIndex)">Del</button>
</span>
</summary>
<label>
<span>VLAN ID</span>
<!-- singleVlanExpr: (([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4}))-->
<!-- expr: ^(((single,)+single)|single)$-->
<input v-model="filter.vlan" type="text" pattern="^((((([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})),)+(([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})))|(([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})))$">
</label>
<div>
<span>Протокол L3</span>
<label class="l3-proto-label"><span>AH:</span><input type="checkbox" value="ah" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>COMP:</span><input type="checkbox" value="comp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>DCCP:</span><input type="checkbox" value="dccp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>ESP:</span><input type="checkbox" value="esp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>ICMP:</span><input type="checkbox" value="icmp" v-model="filter.proto"></label>
<!-- <label class="l3-proto-label"><span>ICMPV6:</span><input type="checkbox" value="icmpv6" v-model="filter.proto"></label>-->
<label class="l3-proto-label"><span>SCTP:</span><input type="checkbox" value="sctp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>TCP:</span><input type="checkbox" value="tcp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>UDP:</span><input type="checkbox" value="udp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>UDPLITE:</span><input type="checkbox" value="udplite" v-model="filter.proto"></label>
</div>
<label>
<span>Порт источника</span>
<input v-model="filter.sport" type="text" pattern="^((((([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})),)+(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))|(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))$">
</label>
<label>
<span>Порт назначения</span>
<input v-model="filter.dport" type="text" pattern="^((((([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})),)+(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))|(([0-9]{1,5}-[0-9]{1,5})|([0-9]{1,5})))$">
</label>
<label>
<span>IP источника</span>
<input v-model="filter.ip_src" type="text">
</label>
<label>
<span>IP назначения</span>
<input v-model="filter.ip_dest" type="text">
</label>
<label>
<span>Метка IP.DSCP</span>
<input v-model="filter.dscp" type="text">
</label>
</details>
<div>
<button class="dangerous-button" @click="qosDelClass(classesGroup, index)">Удалить класс QoS</button>
</div>
</details>
</div>
<button class="action-button" @click="settingsSubmitQoS()">Применить <span class="submit-spinner" v-show="submitStatusQos"></span></button>
{% endraw %}{% if 'tcpaccel' in params %}
{% for w in params['tcpaccel'] %}{{ build_widget('tcpaccel', w) | indent(12, true) }}{% endfor %}
{% endif %}
</div>

View File

@ -1,34 +0,0 @@
{% if 'rxtx' in params and modem == 'scpc' %}
calcRequiredSnr(frameSizeNormal, modulation, speed) {
const snrValues = [
{fs: true, mod: 'qpsk', speed: '1/4', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/3', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '2/5', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/2', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '3/5', snr: 3.1}, {fs: true, mod: 'qpsk', speed: '2/3', snr: 3.8}, {fs: true, mod: 'qpsk', speed: '3/4', snr: 4.5}, {fs: true, mod: 'qpsk', speed: '4/5', snr: 5.2}, {fs: true, mod: 'qpsk', speed: '5/6', snr: 5.5}, {fs: true, mod: 'qpsk', speed: '8/9', snr: 6.4}, {fs: true, mod: 'qpsk', speed: '9/10', snr: 6.7},
{fs: true, mod: '8psk', speed: '3/5', snr: 7.4}, {fs: true, mod: '8psk', speed: '2/3', snr: 8.4}, {fs: true, mod: '8psk', speed: '3/4', snr: 8.7}, {fs: true, mod: '8psk', speed: '5/6', snr: 10}, {fs: true, mod: '8psk', speed: '8/9', snr: 10.9}, {fs: true, mod: '8psk', speed: '9/10', snr: 11.1},
{fs: true, mod: '16apsk', speed: '2/3', snr: 11.2}, {fs: true, mod: '16apsk', speed: '3/4', snr: 11.3}, {fs: true, mod: '16apsk', speed: '4/5', snr: 12.4}, {fs: true, mod: '16apsk', speed: '5/6', snr: 12.7}, {fs: true, mod: '16apsk', speed: '8/9', snr: 13.1}, {fs: true, mod: '16apsk', speed: '9/10', snr: 13.9},
{fs: true, mod: '32apsk', speed: '3/4', snr: 14.5}, {fs: true, mod: '32apsk', speed: '4/5', snr: 14.7}, {fs: true, mod: '32apsk', speed: '5/6', snr: 14.9}, {fs: true, mod: '32apsk', speed: '8/9', snr: 16.1}, {fs: true, mod: '32apsk', speed: '9/10', snr: 16.6},
{fs: false, mod: 'qpsk', speed: '1/4', snr: 4.1}, {fs: false, mod: 'qpsk', speed: '1/3', snr: 4.1}, {fs: false, mod: 'qpsk', speed: '2/5', snr: 4.1}, {fs: false, mod: 'qpsk', speed: '1/2', snr: 4.1}, {fs: false, mod: 'qpsk', speed: '3/5', snr: 4.6}, {fs: false, mod: 'qpsk', speed: '2/3', snr: 5.1}, {fs: false, mod: 'qpsk', speed: '3/4', snr: 5.3}, {fs: false, mod: 'qpsk', speed: '4/5', snr: 6.8}, {fs: false, mod: 'qpsk', speed: '5/6', snr: 7.8}, {fs: false, mod: 'qpsk', speed: '8/9', snr: 8.5},
{fs: false, mod: '8psk', speed: '3/5', snr: 9.5}, {fs: false, mod: '8psk', speed: '2/3', snr: 9.9}, {fs: false, mod: '8psk', speed: '3/4', snr: 10.5}, {fs: false, mod: '8psk', speed: '5/6', snr: 11.1}, {fs: false, mod: '8psk', speed: '8/9', snr: 11.3},
{fs: false, mod: '16apsk', speed: '2/3', snr: 11.6}, {fs: false, mod: '16apsk', speed: '3/4', snr: 11.8}, {fs: false, mod: '16apsk', speed: '4/5', snr: 12}, {fs: false, mod: '16apsk', speed: '5/6', snr: 12.3}, {fs: false, mod: '16apsk', speed: '8/9', snr: 13.4},
{fs: false, mod: '32apsk', speed: '3/4', snr: 14.1}, {fs: false, mod: '32apsk', speed: '4/5', snr: 14.7}, {fs: false, mod: '32apsk', speed: '5/6', snr: 15.3}, {fs: false, mod: '32apsk', speed: '8/9', snr: 16.5},
]
for (let i = 0; i < snrValues.length; i++) {
if (snrValues[i].fs === frameSizeNormal && snrValues[i].mod === modulation && snrValues[i].speed === speed) { return snrValues[i].snr }
}
return '?'
},
calcInterfaceSpeedKb(baud, modulation, speed, frameSizeNormal) {
const mBaud = parseInt(baud.replace(/[^0-9]/g, ''))
const mMod = Math.max(2, ['', '', 'qpsk', '8psk', '16apsk', '32apsk'].indexOf(modulation))
const speedVals = {'1/4': 0.25, '1/3': 0.333, '2/5': 0.4, '1/2': 0.5, '3/5': 0.6, '2/3': 0.666, '3/4': 0.75, '4/5': 0.8, '5/6': 0.833, '8/9': 0.888, '9/10': 0.9}
const mSpeed = speed in speedVals ? speedVals[speed] : 1
const result = (mBaud * mMod * mSpeed) / 1024
const calcSnr = this.calcRequiredSnr(frameSizeNormal, modulation, speed)
let snr;
if (isNaN(calcSnr)) { snr = `ОСШ=?` } else { snr=`ОСШ=${calcSnr}` }
if (result > 1024) {
return toLocaleStringWithSpaces(result / 1024) + ' Мбит/с; ' + snr
} else {
return toLocaleStringWithSpaces(result) + ' кбит/с; ' + snr
}
},
{% endif %}

View File

@ -1,8 +0,0 @@
{% from 'common/widgets.j2' import build_widget %}
<div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete">
{% for cat in ['rxtx', 'dpdi', 'buclnb'] %}
{% if cat in params %}
{% for w in params[cat] %}{{ build_widget(cat, w) | indent(12, true) }}{% endfor %}
{% endif %}
{% endfor %}
</div>

View File

@ -1,99 +0,0 @@
{% macro build_widget_checkbox(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<span class="toggle-input"><input type="checkbox" v-model="param{{ param_group | title }}.{{ widget.name }}" /><span class="slider"></span></span>
</label>{% endmacro %}
{# https://ru.stackoverflow.com/questions/1241064 #}
{% macro build_widget_number(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}><span>{{ widget.label }}</span><input type="number" v-model="param{{ param_group | title }}.{{ widget.name }}"{% if widget['min'] %} min="{{ widget['min'] }}"{% endif %}{% if widget['max'] %} max="{{ widget['max'] }}"{% endif %}{% if widget['step'] %} step="{{ widget['step'] }}"{% endif %}/></label>{% endmacro %}
{% macro js_build_number_number_validator(widget) %}{{ '{' }}{% if widget['min'] %}min:{{ widget['min'] }},{% endif %}{% if widget['max'] %}max:{{ widget['max'] }},{% endif %}{% if widget['step'] %}step:{{ widget['step'] }}{% endif %}{{ '}' }}{% endmacro %}
{% macro build_widget_number_int(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<input type="text" v-model.lazy="param{{ param_group | title }}.{{ widget.name }}" @change="e => {{ build_setter(param_group, widget, "inputFormatNumber(e.target.value, " ~ js_build_number_number_validator(widget) ~ ")") }}"/>
</label>{% endmacro %}
{% macro build_widget_select(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<select v-model="param{{ param_group | title }}.{{ widget.name }}">
{% for opt in widget['values'] %} <option :value="{{ opt.value }}">{{ opt.label }}</option>
{% endfor %}
</select>
</label>{% endmacro %}
{% macro build_widget_watch(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}><span>{{ widget.label }}</span><input type="text" readonly v-model="{{ widget.model }}"/></label>{% endmacro %}
{% macro build_widget_watch_expr(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span><span>{{ '{{ ' ~ widget.expr ~ ' }}' }}</span>
</label>{% endmacro %}
{% macro build_widget_modulation_modcod(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<select v-model="param{{ param_group | title }}.{{ widget.name }}Modulation" @change="param{{ param_group | title }}.{{ widget.name }}Speed = correctModcodSpeed(param{{ param_group | title }}.{{ widget.name }}Modulation, param{{ param_group | title }}.{{ widget.name }}Speed)">
<option :value="'qpsk'">QPSK</option>
<option :value="'8psk'">8PSK</option>
<option :value="'16apsk'">16APSK</option>
<option :value="'32apsk'">32APSK</option>
</select>
</label>{% endmacro %}
{% macro build_widget_modulation_speed(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<select v-model="param{{ param_group | title }}.{{ widget.name }}Speed">
<option v-for="speed in getAvailableModcods(param{{ param_group | title }}.{{ widget.name }}Modulation)" v-bind:value="speed">{{ '{{' }} speed {{ '}}' }}</option>
</select>
</label>{% endmacro %}
{% macro build_widget_flex_container(param_group, widget) %}<div class="tabs-item-flex-container"{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
{% for w in widget.childs %}{{ build_widget(param_group, w) | indent(4, true) }}{% endfor %}
</div>{% endmacro %}
{% macro build_widget_settings_container(param_group, widget) %}<div class="settings-set-container"{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
{% for w in widget.childs %}{{ build_widget(param_group, w) | indent(4, true) }}{% endfor %}
</div>{% endmacro %}
{% macro build_widget_ip_address(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<input v-model="param{{ param_group | title }}.{{ widget.name }}" required type="text" pattern="^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$">
</label>{% endmacro %}
{% macro build_widget_text(param_group, widget) %}<label{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>
<span>{{ widget.label }}</span>
<input v-model="param{{ param_group | title }}.{{ widget.name }}" type="text">
</label>{% endmacro %}
{% macro build_widget(param_group, widget) %}{% if widget.widget == 'flex-container' %}{{ build_widget_flex_container(param_group, widget) }}
{% elif widget.widget == 'settings-container' %}{{ build_widget_settings_container(param_group, widget) }}
{% elif widget.widget == 'h2' %}<h2{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>{{ widget.label }}</h2>
{% elif widget.widget == 'h3' %}<h3{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>{{ widget.label }}</h3>
{% elif widget.widget == 'submit' %}<button class="action-button" @click="settingsSubmit{{ param_group | title }}()"{% if widget.v_show %} v-show="{{ widget.v_show }}"{% endif %}>Сохранить <span class="submit-spinner" v-show="submitStatus.{{ param_group }}"></span></button>
{% elif widget.widget == 'checkbox' %}{{ build_widget_checkbox(param_group, widget) }}
{% elif widget.widget == 'number' %}{{ build_widget_number(param_group, widget) }}
{% elif widget.widget == 'number-int' %}{{ build_widget_number_int(param_group, widget) }}
{% elif widget.widget == 'watch' %}{{ build_widget_watch(param_group, widget) }}
{% elif widget.widget == 'watch-expr' %}{{ build_widget_watch_expr(param_group, widget) }}
{% elif widget.widget == 'select' %}{{ build_widget_select(param_group, widget) }}
{% elif widget.widget == 'modulation-modcod' %}{{ build_widget_modulation_modcod(param_group, widget) }}
{% elif widget.widget == 'modulation-speed' %}{{ build_widget_modulation_speed(param_group, widget) }}
{% elif widget.widget == 'ip-address' %}{{ build_widget_ip_address(param_group, widget) }}
{% elif widget.widget == 'text' %}{{ build_widget_text(param_group, widget) }}
{% else %}<p>Widget '{{ widget.widget }}' not defined!</p><p>{{ widget }}</p>
{% endif %}
{% endmacro %}
{% macro build_getter_js(param_group, widget) %}{% if widget.widget in ['flex-container', 'settings-container', 'h2', 'h3', 'submit', 'watch', 'watch-expr'] %}null{%
elif widget.widget in ['checkbox', 'number', 'select', 'ip-address', 'modulation-modcod', 'modulation-speed', 'text'] %}this.param{{ param_group | title }}.{{ widget.name }}{%
elif widget.widget == 'number-int' %}parseFloat(this.param{{ param_group | title }}.{{ widget.name }}.replace(/[^0-9,.]/g, '').replace(',', '.')){%
else %}<p>Widget '{{ widget.widget }}' not defined!</p><p>{{ widget }}</p>{% endif %}{% endmacro %}
{% macro build_setter_js(param_group, widget, expr) %}{% if widget.widget in ['flex-container', 'settings-container', 'h2', 'h3', 'submit', 'watch', 'watch-expr'] %}null{%
elif widget.widget in ['checkbox', 'number', 'select', 'ip-address', 'modulation-modcod', 'modulation-speed', 'text'] %}this.param{{ param_group | title }}.{{ widget.name }} = {{ expr }}{%
elif widget.widget == 'number-int' %}this.param{{ param_group | title }}.{{ widget.name }} = this.inputFormatNumber({{ expr }}, {{ js_build_number_number_validator(widget) }}){%
else %}<p>Widget '{{ widget.widget }}' not defined!</p><p>{{ widget }}</p>{% endif %}{% endmacro %}
{% macro build_setter(param_group, widget, expr) %}{% if widget.widget in ['flex-container', 'settings-container', 'h2', 'h3', 'submit', 'watch', 'watch-expr'] %}null{%
elif widget.widget in ['checkbox', 'number', 'select', 'ip-address', 'modulation-modcod', 'modulation-speed', 'text'] %}param{{ param_group | title }}.{{ widget.name }} = {{ expr }}{%
elif widget.widget == 'number-int' %}param{{ param_group | title }}.{{ widget.name }} = inputFormatNumber({{ expr }}, {{ js_build_number_number_validator(widget) }}){%
else %}<p>Widget '{{ widget.widget }}' not defined!</p><p>{{ widget }}</p>{% endif %}{% endmacro %}

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,15 +1,8 @@
#include "jwt.h" #include "jwt.h"
#include <random> #include <random>
#include "utils.h" #include "utils.h"
#include <sys/time.h>
static int64_t milliseconds() {
timeval tv{};
gettimeofday(&tv,nullptr);
return ((tv.tv_sec * 1000000l) + tv.tv_usec) / 1000;
}
std::string http::auth::jwt::secretKey; std::string http::auth::jwt::secretKey;
void http::auth::jwt::generateSecretKey() { void http::auth::jwt::generateSecretKey() {
@ -23,45 +16,37 @@ void http::auth::jwt::generateSecretKey() {
} }
} }
// payload, signature
static std::pair<std::string, std::string> parseJwtFromCookie(const std::string& input) {
std::string val1, val2;
size_t dotPos = input.find('.');
// Если точка найдена
if (dotPos != std::string::npos) {
val1 = input.substr(0, dotPos);
// Если в val1 есть еще точки, нужно найти последнюю точку
size_t lastDotPos = val1.find_last_of('.');
if (lastDotPos != std::string::npos) {
val1 = val1.substr(0, lastDotPos + 1);
}
val2 = input.substr(dotPos + 1);
} else {
// Точка не найдена, val1 - вся строка
val1 = input;
}
return std::make_pair(http::utils::b64Decode(val1), val2);
}
http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie) { http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie) {
auto pc = utils::parseCookies(cookie); auto pc = utils::parseCookies(cookie);
Jwt t; Jwt t;
if (pc.find("auth") != pc.end()) { if (pc.find("auth") != pc.end()) {
const auto auth = pc.at("auth"); auto tmp = parseJwtFromCookie(pc.at("auth"));
std::string::size_type firstDot = std::string::npos; t.payload = tmp.first;
std::string::size_type secondDot = std::string::npos; t.signature = tmp.second;
for (std::string::size_type i = 0; i < auth.size(); i++) {
if (auth[i] == '.') {
if (firstDot == std::string::npos) { firstDot = i; }
else if (secondDot == std::string::npos) { secondDot = i; }
else {
// так быть не должно
return t;
}
}
}
if (firstDot == std::string::npos || secondDot == std::string::npos || secondDot - firstDot == 0) {
// так тоже быть не должно
return t;
}
try {
t.payload = auth.substr(0, firstDot);
t.signature = auth.substr(secondDot + 1);
t.lastUpdate = std::stol(auth.substr(firstDot + 1, secondDot - firstDot - 1));
// теперь проверим, что сигнатура верная, только тогда декодируем строку юзера
auto realSignature = utils::sha256(t.payload + std::to_string(t.lastUpdate) + secretKey);
if (t.signature != realSignature) {
t.payload.clear();
} else {
t.payload = utils::b64Decode(t.payload);
}
} catch (std::exception& e) {
t.payload.clear();
t.lastUpdate = 0;
t.signature.clear();
}
} }
return t; return t;
@ -70,7 +55,6 @@ http::auth::jwt::Jwt http::auth::jwt::Jwt::fromCookies(const std::string &cookie
http::auth::jwt::Jwt http::auth::jwt::Jwt::fromUser(const std::string &user) { http::auth::jwt::Jwt http::auth::jwt::Jwt::fromUser(const std::string &user) {
Jwt t; Jwt t;
t.payload = user; t.payload = user;
t.lastUpdate = milliseconds();
return t; return t;
} }
@ -79,40 +63,18 @@ bool http::auth::jwt::Jwt::isValid() {
return false; return false;
} }
// проверка сигнатуры не нужна, она была на стадии парсинга куки auto realSignature = utils::sha256(this->payload + secretKey);
// auto realSignature = utils::sha256(utils::b64Encode(this->payload) + std::to_string(this->lastUpdate) + secretKey); return signature == realSignature;
// if (signature != realSignature) {
// return false;
// }
const auto currTime = milliseconds();
return currTime <= lastUpdate + SESSION_LIVE_MS && currTime >= lastUpdate;
} }
std::string http::auth::jwt::Jwt::getUsername() { std::string http::auth::jwt::Jwt::getUsername() {
return payload; return payload;
} }
std::string http::auth::jwt::Jwt::asCookie(bool isSecure) { std::string http::auth::jwt::Jwt::asCookie() {
this->lastUpdate = milliseconds(); signature = utils::sha256(this->payload + secretKey);
const auto uTime = std::to_string(this->lastUpdate); auto val = utils::b64Encode(payload) + "." + signature;
const auto encodedPayload = utils::b64Encode(payload); return "auth=" + val + ";Path=/; Max-Age=86400; HttpOnly; SameSite=Lax";
signature = utils::sha256(encodedPayload + uTime + secretKey);
const auto val = encodedPayload + "." + uTime + "." + signature;
std::string cookie = "auth=";
cookie += val;
cookie += ";Path=/; Max-Age=";
cookie += std::to_string(SESSION_LIVE_MS / 1000);
if (isSecure) {
cookie += "; Secure";
}
cookie += "; HttpOnly; SameSite=Lax";
return cookie;
}
bool http::auth::jwt::Jwt::needUpdate() const {
return milliseconds() >= lastUpdate + SESSION_UPDATE_THRESHOLD;
} }
http::auth::jwt::Jwt::~Jwt() = default; http::auth::jwt::Jwt::~Jwt() = default;

View File

@ -6,10 +6,7 @@
namespace http::auth::jwt { namespace http::auth::jwt {
extern std::string secretKey; extern std::string secretKey;
constexpr const char* EMPTY_AUTH_COOKIE = "auth=;Path=/; Max-Age=86400; HttpOnly; SameSite=Lax"; constexpr const char* EMPTY_AUTH_COOKIE = "auth=;Path=/; Max-Age=86400; HttpOnly; SameSite=Lax";;
constexpr int64_t SESSION_LIVE_MS = 24 * 60 * 60 * 1000; // 24 часа
constexpr int64_t SESSION_UPDATE_THRESHOLD = 10 * 60 * 1000; // 10 минут
void generateSecretKey(); void generateSecretKey();
@ -20,7 +17,6 @@ namespace http::auth::jwt {
*/ */
class Jwt { class Jwt {
std::string payload; std::string payload;
int64_t lastUpdate = 0;
std::string signature; std::string signature;
public: public:
static Jwt fromCookies(const std::string& cookie); static Jwt fromCookies(const std::string& cookie);
@ -30,9 +26,7 @@ namespace http::auth::jwt {
std::string getUsername(); std::string getUsername();
std::string asCookie(bool isSecure = false); std::string asCookie();
bool needUpdate() const;
~Jwt(); ~Jwt();
}; };

View File

@ -44,12 +44,12 @@ http::auth::User::~User() = default;
http::auth::AuthProvider::AuthProvider() = default; http::auth::AuthProvider::AuthProvider() = default;
std::shared_ptr<http::auth::User> http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep) { std::shared_ptr<http::auth::User> http::auth::AuthProvider::doAuth(const std::string &username, const std::string &password, server::Reply &rep) {
for (const auto& u: users) { for (const auto& u: users) {
if (u->username == username) { if (u->username == username) {
if (u->checkPassword(password)) { if (u->checkPassword(password)) {
auto t = jwt::Jwt::fromUser(u->username); auto t = jwt::Jwt::fromUser(u->username);
rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie(req.isSecure)}); rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie()});
return u; return u;
} }
BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::doAuth(): Failed to login " << username << ", password: " << password << " (incorrect password)"; BOOST_LOG_TRIVIAL(warning) << "http::auth::AuthProvider::doAuth(): Failed to login " << username << ", password: " << password << " (incorrect password)";
@ -60,17 +60,13 @@ std::shared_ptr<http::auth::User> http::auth::AuthProvider::doAuth(const std::st
return nullptr; return nullptr;
} }
std::shared_ptr<http::auth::User> http::auth::AuthProvider::getSession(const server::Request &req, server::Reply &rep) { std::shared_ptr<http::auth::User> http::auth::AuthProvider::getSession(const server::Request &req) {
auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie")); auto t = jwt::Jwt::fromCookies(req.getHeaderValue("cookie"));
if (t.isValid()) { if (t.isValid()) {
const auto name = t.getUsername(); const auto name = t.getUsername();
// токен валидный, ищем юзера // токен валидный, ищем юзера
for (auto& u: users) { for (auto& u: users) {
if (u->username == name) { if (u->username == name) {
// на всякий случай тут проверяем, что токен пора обновлять
if (t.needUpdate()) {
rep.headers.push_back({.name = "Set-Cookie", .value = t.asCookie(req.isSecure)});
}
return u; return u;
} }
} }
@ -88,7 +84,7 @@ http::auth::AuthRequiredResource::AuthRequiredResource(const std::string &path,
BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {} BasicResource(path), provider_(provider), generator_(std::move(generator)), perms(perms) {}
void http::auth::AuthRequiredResource::handle(const server::Request &req, server::Reply &rep) { void http::auth::AuthRequiredResource::handle(const server::Request &req, server::Reply &rep) {
if (auto user = this->provider_.getSession(req, rep)) { if (auto user = this->provider_.getSession(req)) {
if (user->checkPremisions(this->perms)) { if (user->checkPremisions(this->perms)) {
this->generator_(req, rep); this->generator_(req, rep);
return; return;

View File

@ -51,7 +51,6 @@ namespace http::auth {
static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc static constexpr uint32_t EDIT_SETTINGS = 0x0010; // редактирование настроек, установка параметров модулятора/демодулятора/dma/cinc
static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки static constexpr uint32_t UPDATE_FIRMWARE = 0x0020; // обновление прошивки
static constexpr uint32_t SETUP_QOS = 0x0040; // управление профилем QoS static constexpr uint32_t SETUP_QOS = 0x0040; // управление профилем QoS
static constexpr uint32_t DEVELOPER = 0x0080; // использование функций для разработчиков
/** /**
* Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть. * Проверить, что у пользователя есть нужное право. Если это суперпользователь, то у него по умолчанию все права есть.
@ -71,23 +70,20 @@ namespace http::auth {
* @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя! * @note Класс устанавливает заголовок 'Set-Cookie' в ответе, и этот заголовок должен дойти до пользователя!
*/ */
class AuthProvider { class AuthProvider {
void updateSessionHook();
public: public:
AuthProvider(); AuthProvider();
/** /**
* Авторизовать пользователя. * Авторизовать пользователя.
* *
* @param req
* @param rep * @param rep
* @return true, в случае успешной авторизации * @return true, в случае успешной авторизации
*/ */
std::shared_ptr<http::auth::User> doAuth(const std::string &username, const std::string &password, const server::Request &req, server::Reply &rep); std::shared_ptr<http::auth::User> doAuth(const std::string &username, const std::string &password, server::Reply &rep);
std::vector<std::shared_ptr<User>> users; std::vector<std::shared_ptr<User>> users;
std::shared_ptr<User> getSession(const server::Request &req, server::Reply &rep); std::shared_ptr<User> getSession(const server::Request &req);
~AuthProvider(); ~AuthProvider();
}; };

View File

@ -113,20 +113,7 @@ std::map<std::string, std::string> http::utils::parseCookies(const std::string&
if (equalPos == std::string::npos) { if (equalPos == std::string::npos) {
continue; // Неверный формат Cookie continue; // Неверный формат Cookie
} }
size_t startIndex = 0; std::string name = cookie.substr(0, equalPos);
while (startIndex < cookie.size()) {
if (cookie[startIndex] == '=') {
// некорректная кука, состоит только из пробелов, так что на этом обработку и закончим
return cookies;
}
if (cookie[startIndex] == ' ') {
startIndex++;
} else {
break;
}
}
std::string name = cookie.substr(startIndex, equalPos - startIndex);
std::string value = cookie.substr(equalPos + 1); std::string value = cookie.substr(equalPos + 1);
// Удаляем пробелы с начала и конца значения Cookie // Удаляем пробелы с начала и конца значения Cookie

View File

@ -17,7 +17,6 @@
#include <fstream> #include <fstream>
#include "terminal_api_driver.h" #include "terminal_api_driver.h"
#include "version.h"
#include "auth/resources.h" #include "auth/resources.h"
#include "auth/jwt.h" #include "auth/jwt.h"
#include "auth/utils.h" #include "auth/utils.h"
@ -27,7 +26,25 @@ namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
constexpr const char* REBOOT_COMMAND = "web-action reboot"; constexpr const char* REBOOT_COMMAND = "web-action reboot";
constexpr const char* UPGRADE_COMMAND = "web-action upgrade"; constexpr const char* UPGRADE_COMMAND = "web-action upgrade";
constexpr const char* FIRMWARE_LOCATION = "/tmp/firmware.zip";
static std::vector<char> loadFile(const std::string& path) {
std::ifstream is(path, std::ios::in | std::ios::binary);
if (!is) {
throw std::runtime_error("File not found");
}
std::vector<char> content;
for (;;) {
char buf[512];
auto len = is.read(buf, sizeof(buf)).gcount();
if (len <= 0) {
break;
}
content.insert(content.end(), buf, buf + len);
}
return content;
}
namespace mime_types = http::server::mime_types; namespace mime_types = http::server::mime_types;
@ -40,18 +57,23 @@ void init_logging() {
log::register_simple_formatter_factory<log::trivial::severity_level, char>("Severity"); log::register_simple_formatter_factory<log::trivial::severity_level, char>("Severity");
#ifdef USE_DEBUG // #ifdef USE_DEBUG
log::add_console_log(std::clog, keywords::format = "%TimeStamp%: [%Severity%] %Message%"); // log::add_console_log(std::clog, keywords::format = "%TimeStamp%: [%Severity%] %Message% [%ThreadID%]");
#else // #else
log::add_file_log( // log::add_file_log(
keywords::file_name = "http_server_%N.log", // keywords::file_name = "/home/root/manager_orlik_%N.log",
keywords::rotation_size = 10 * 1024 * 1024, // keywords::rotation_size = 10 * 1024 * 1024,
keywords::time_based_rotation = log::sinks::file::rotation_at_time_point(0, 0, 0), // keywords::time_based_rotation = log::sinks::file::rotation_at_time_point(0, 0, 0),
keywords::format = "%TimeStamp%: [%Severity%] %Message%", // keywords::format = expressions::format("%1% [%2%] [%3%] <%4%> [%5%]")
keywords::open_mode = std::ios_base::app, // % expressions::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d, %H:%M:%S.%f")
keywords::auto_flush = true // % expressions::format_named_scope("Scope", keywords::format = "%n (%f:%l)")
); // % expressions::attr<log::trivial::severity_level>("Severity")
#endif // % expressions::message % expressions::attr<attributes::current_thread_id::value_type>("ThreadID"),
// keywords::open_mode = std::ios_base::app,
// keywords::auto_flush = true
// );
// #endif
log::add_console_log(std::clog, keywords::format = "%TimeStamp%: [%Severity%] %Message% [%ThreadID%]");
log::core::get()->set_filter(log::trivial::severity >= log::trivial::info); log::core::get()->set_filter(log::trivial::severity >= log::trivial::info);
log::add_common_attributes(); log::add_common_attributes();
@ -65,7 +87,7 @@ class ServerResources {
bool upgradeOrRebootRunning = false; bool upgradeOrRebootRunning = false;
static void onUploadFirmware(const http::server::Request& req) { static void onUploadFirmware(const http::server::Request& req) {
std::ofstream f(FIRMWARE_LOCATION, std::ios::binary); std::ofstream f("/tmp/firmware.zip", std::ios::binary);
if (f.is_open()) { if (f.is_open()) {
f.write(req.payload.data(), static_cast<long>(req.payload.size())); f.write(req.payload.data(), static_cast<long>(req.payload.size()));
@ -78,27 +100,11 @@ class ServerResources {
void doTerminalUpgrade() const { void doTerminalUpgrade() const {
api->executeInApi([](TSID sid) { api->executeInApi([](TSID sid) {
CP_SetDmaDebug(sid, "begin_save_config", ""); CP_SetDmaDebug(sid, "begin_save_config", "");
std::string cmd(UPGRADE_COMMAND); system(UPGRADE_COMMAND);
cmd += " ";
cmd += FIRMWARE_LOCATION;
system(cmd.c_str());
CP_SetDmaDebug(sid, "save_config", ""); CP_SetDmaDebug(sid, "save_config", "");
}); });
} }
#ifdef MODEM_IS_TDMA
void doTerminalUpgradeOta() const {
api->executeInApi([&](TSID sid) {
CP_SetDmaDebug(sid, "begin_save_config", "");
std::string cmd(UPGRADE_COMMAND);
cmd += " ";
cmd += api->getOtaFileLocation();
system(cmd.c_str());
CP_SetDmaDebug(sid, "save_config", "");
});
}
#endif
public: public:
#if defined(MODEM_IS_TDMA) #if defined(MODEM_IS_TDMA)
static constexpr const char* INDEX_HTML = "/main-tdma.html"; static constexpr const char* INDEX_HTML = "/main-tdma.html";
@ -108,7 +114,6 @@ public:
#error "Modem type not defined!" #error "Modem type not defined!"
#endif #endif
static constexpr const char* LOGIN_HTML = "/login.html"; static constexpr const char* LOGIN_HTML = "/login.html";
static constexpr const char* DEV_HTML = "/dev.html";
// картинки, их даже можно кешировать // картинки, их даже можно кешировать
static constexpr const char* FAVICON_ICO = "/favicon.ico"; static constexpr const char* FAVICON_ICO = "/favicon.ico";
@ -123,34 +128,20 @@ public:
explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) { explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
api->startDaemon(); api->startDaemon();
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER));
http::auth::User::WATCH_STATISTICS |
http::auth::User::RESET_PACKET_STATISTICS |
http::auth::User::WATCH_SETTINGS |
http::auth::User::EDIT_SETTINGS |
http::auth::User::UPDATE_FIRMWARE |
http::auth::User::SETUP_QOS));
// пароль fuckyou123
auth.users.emplace_back(std::make_shared<http::auth::User>("developer", "10628cfc434fb87f31d675d37e0402c2d824cfe8393aff7a61ee57aaa7d909c3", http::auth::User::SUPERUSER));
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true); sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
#ifdef USE_DEBUG
sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true); sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true);
#else
sf->registerFile(staticFilesPath + "/js/vue.prod.js", VUE_JS, mime_types::javascript, true);
#endif
sf->registerFile(staticFilesPath + STYLE_CSS, STYLE_CSS, mime_types::text_css, 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 + FIELDS_CSS, FIELDS_CSS, mime_types::text_css, true);
sf->registerFile(staticFilesPath + INDEX_HTML, INDEX_HTML, mime_types::text_html, false); sf->registerFile(staticFilesPath + INDEX_HTML, INDEX_HTML, mime_types::text_html, false);
sf->registerFile(staticFilesPath + DEV_HTML, DEV_HTML, mime_types::text_html, false);
sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true); sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true);
sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true); sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true);
} }
void registerResources(http::server::Server& s) { void registerResources(http::server::Server& s) {
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/", [this](const auto& req, auto& rep) {
auto user = auth.getSession(req, rep); auto user = auth.getSession(req);
if (user == nullptr) { if (user == nullptr) {
http::server::httpRedirect(rep, "/login"); http::server::httpRedirect(rep, "/login");
} else { } else {
@ -160,7 +151,7 @@ public:
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/login", [this](const auto& req, auto& rep) {
if (req.method == "GET") { if (req.method == "GET") {
auto user = auth.getSession(req, rep); auto user = auth.getSession(req);
if (user == nullptr) { if (user == nullptr) {
sf->serve(LOGIN_HTML, rep); sf->serve(LOGIN_HTML, rep);
} else { } else {
@ -175,7 +166,7 @@ public:
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
read_json(is, pt); read_json(is, pt);
auto u = auth.doAuth(pt.get<std::string>("username"), pt.get<std::string>("password"), req, rep); auto u = auth.doAuth(pt.get<std::string>("username"), pt.get<std::string>("password"), rep);
if (u == nullptr) { if (u == nullptr) {
throw std::runtime_error("invalid session"); throw std::runtime_error("invalid session");
} }
@ -208,10 +199,10 @@ public:
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) {
if (req.method != "GET") { if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"mainState":)"; std::string result = R"({"mainState":)";
result += api->loadTerminalState(); result += api->loadTerminalState();
@ -224,10 +215,10 @@ public:
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) { 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") { if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"settings":)"; std::string result = R"({"settings":)";
result += api->loadSettings(); result += api->loadSettings();
@ -238,10 +229,10 @@ public:
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) { 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") { if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const auto result = api->loadFirmwareVersion(); const auto result = api->loadFirmwareVersion();
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
@ -250,11 +241,11 @@ public:
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) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
api->resetPacketStatistics(); api->resetPacketStatistics();
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok")"; const std::string result = R"({"status":"ok")";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
@ -263,10 +254,10 @@ public:
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) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try { try {
@ -283,18 +274,18 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/qos): Can't set QoS settings: " << e.what(); BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/qos): Can't set QoS settings: " << e.what();
const std::string result = R"({"status": "error", "error": )" + api_driver::buildEscapedString(e.what()) + "}"; const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} }
})); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/buclnb", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try { try {
@ -310,19 +301,19 @@ public:
result += "}"; result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/buclnb): Can't set BUC LNB settings: " << e.what(); BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/bucLnb): Can't set settings: " << e.what();
const std::string result = R"({"status": "error", "error": )" + api_driver::buildEscapedString(e.what()) + "}"; const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} }
})); }));
#ifdef MODEM_IS_SCPC
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/dpdi", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try { try {
@ -331,26 +322,26 @@ public:
boost::property_tree::ptree pt; boost::property_tree::ptree pt;
read_json(ss, pt); read_json(ss, pt);
api->setDpdiSettings(pt); api->setCincSettings(pt);
std::string result = R"({"status":"ok","settings":)"; std::string result = R"({"status":"ok","settings":)";
result += api->loadSettings(); result += api->loadSettings();
result += "}"; result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/dpdi): Can't set DPDI settings: " << e.what(); BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/cinc): Can't set CinC settings: " << e.what();
const std::string result = R"({"status": "error", "error": )" + api_driver::buildEscapedString(e.what()) + "}"; const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} }
})); }));
#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) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try { try {
@ -367,7 +358,7 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what(); BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
const std::string result = R"({"status": "error", "error": )" + api_driver::buildEscapedString(e.what()) + "}"; const std::string result = R"({"status":"error"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} }
})); }));
@ -375,10 +366,10 @@ public:
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) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
try { try {
@ -394,8 +385,36 @@ public:
result += "}"; result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/network): Can't set network settings: " << e.what(); BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what();
const std::string result = R"({"status": "error", "error": )" + api_driver::buildEscapedString(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()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
} }
})); }));
@ -403,21 +422,21 @@ public:
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [this](const auto& req, auto& rep) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok"})"; const std::string result = R"({"status":"ok"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
this->upgradeOrRebootRunning = true; this->upgradeOrRebootRunning = true;
system(REBOOT_COMMAND); system(REBOOT_COMMAND);
})); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok"})"; const std::string result = R"({"status":"ok"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
@ -428,12 +447,12 @@ public:
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) { 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") { if (req.method != "PUT") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
this->upgradeOrRebootRunning = true; this->upgradeOrRebootRunning = true;
onUploadFirmware(req); onUploadFirmware(req);
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"status":"ok","fwsize":)"; std::string result = R"({"status":"ok","fwsize":)";
result += std::to_string(req.payload.size()); result += std::to_string(req.payload.size());
@ -447,18 +466,9 @@ public:
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) { 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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
return;
} }
this->upgradeOrRebootRunning = true; this->upgradeOrRebootRunning = true;
#ifdef MODEM_IS_TDMA
if (req.url->params.find("ota") != req.url->params.end()) {
doTerminalUpgradeOta();
} else {
doTerminalUpgrade();
}
#else
doTerminalUpgrade(); doTerminalUpgrade();
#endif
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear(); rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
@ -466,86 +476,18 @@ public:
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
})); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) { 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); boost::ignore_unused(req);
sf->serve(DEV_HTML, rep); sf->serve(INTERNET_JPG, rep);
})); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/cpapicall", this->auth, http::auth::User::DEVELOPER, [this](const http::server::Request& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/fetchParams", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
return;
}
if (req.url->params.find("f") == req.url->params.end()) {
http::server::stockReply(http::server::bad_request, rep);
return;
}
const auto func = req.url->params["f"];
std::string result = R"({"status":"ok"})";
if (func == "SetDmaDebug") {
if (req.url->params.find("param") == req.url->params.end()) { http::server::stockReply(http::server::bad_request, rep); return; }
if (req.url->params.find("value") == req.url->params.end()) { http::server::stockReply(http::server::bad_request, rep); return; }
this->api->executeInApi([&](auto sid) {
CP_SetDmaDebug(sid, req.url->params["param"].c_str(), req.url->params["value"]);
});
} else if (func == "GetDmaDebug") {
if (req.url->params.find("param") == req.url->params.end()) { http::server::stockReply(http::server::bad_request, rep); return; }
this->api->executeInApi([&](auto sid) {
std::string tmp{};
CP_GetDmaDebug(sid, req.url->params["param"].c_str(), &tmp);
result = R"({"status":"ok","result":)";
result += api_driver::buildEscapedString(tmp);
result += "}";
});
} else {
http::server::stockReply(http::server::not_implemented, rep);
return;
}
rep.status = http::server::ok;
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); std::string result = R"({"status":"ok","fwsize":)";
result += std::to_string(req.payload.size());
result += R"(,"sha256":")";
result += "\"}";
})); }));
#ifdef MODEM_IS_SCPC
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/settings", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
std::string result;
if (req.method == "GET") {
result = R"({"status":"ok","logstat":)";
result += this->api->getLoggingStatisticsSettings();
result += "}";
} else if (req.method == "POST") {
std::stringstream ss;
ss.str(std::string(req.payload.begin(), req.payload.end()));
boost::property_tree::ptree pt;
read_json(ss, pt);
api->setLoggingStatisticsSettings(pt);
result = R"({"status":"ok","logstat":)";
result += this->api->getLoggingStatisticsSettings();
result += "}";
} else {
http::server::stockReply(http::server::bad_request, rep);
return;
}
rep.status = http::server::ok;
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/logs.csv", this->auth, http::auth::User::DEVELOPER, [this](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
return;
}
rep.status = http::server::ok;
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::text_plain)});
rep.content.clear();
http::resource::loadFile("/tmp/weblog-statistics.csv", rep.content);
}));
#endif
} }
~ServerResources() = default; ~ServerResources() = default;
@ -565,38 +507,15 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
if (strcmp(argv[1], "nossl") != 0 && strcmp(argv[1], "ssl") != 0) {
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;
return 1;
}
int serverPort;
try {
size_t idx = 0;
serverPort = std::stoi(std::string(argv[3]), &idx);
if (serverPort < 0 || serverPort > 0xffff) {
throw std::invalid_argument("Out of range");
}
if (idx != strlen(argv[3])) {
throw std::invalid_argument("Invalid number");
}
} catch (std::exception& e) {
std::cerr << "Wrong server port `" << argv[3] << "`: " << e.what() << std::endl;
return 1;
}
init_logging(); init_logging();
boost::log::core::get()->add_thread_attribute("Scope", boost::log::attributes::named_scope()); boost::log::core::get()->add_thread_attribute("Scope", boost::log::attributes::named_scope());
#ifdef USE_DEBUG #ifdef USE_DEBUG
BOOST_LOG_TRIVIAL(info) << "Starting DEBUG " << argv[0]; BOOST_LOG_TRIVIAL(info) << "Starting DEBUG " << argv[0];
#else #else
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE " << argv[0]; BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
#endif #endif
BOOST_LOG_TRIVIAL(info) << ("Build time: " PROJECT_BUILD_TIME);
BOOST_LOG_TRIVIAL(info) << ("Git version: " PROJECT_GIT_REVISION);
#ifdef USE_DEBUG #ifdef USE_DEBUG
http::auth::jwt::secretKey = "^}u'ZKyQ%;+:lnh^GS7!=G~nRK?7[{``"; 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; BOOST_LOG_TRIVIAL(info) << "DEBUG build use pre-created key " << http::auth::jwt::secretKey;
@ -613,14 +532,14 @@ int main(int argc, char *argv[]) {
std::unique_ptr<http::server::Server> s; std::unique_ptr<http::server::Server> s;
if (strcmp(argv[1], "nossl") == 0) { if (strcmp(argv[1], "nossl") == 0) {
BOOST_LOG_TRIVIAL(info) << "Run server on " << argv[2] << ":" << serverPort; s = std::make_unique<http::server::Server>(argv[2], argv[3]);
s = std::make_unique<http::server::Server>(argv[2], serverPort);
resources.registerResources(*s); resources.registerResources(*s);
s->run(); s->run();
} else {
std::vector<char> cert; http::resource::loadFile("cert.pem", cert); } else if (strcmp(argv[1], "ssl") == 0) {
std::vector<char> key; http::resource::loadFile("key.pem", key); const auto cert = loadFile("cert.pem");
std::vector<char> dh; http::resource::loadFile("dh.pem", dh); const auto key = loadFile("key.pem");
const auto dh = loadFile("dh.pem");
auto ctx = std::make_shared<ssl::context>(ssl::context::tlsv12); auto ctx = std::make_shared<ssl::context>(ssl::context::tlsv12);
@ -634,10 +553,12 @@ int main(int argc, char *argv[]) {
ctx->use_private_key(boost::asio::buffer(key), ssl::context::file_format::pem); ctx->use_private_key(boost::asio::buffer(key), ssl::context::file_format::pem);
ctx->use_tmp_dh(boost::asio::buffer(dh)); ctx->use_tmp_dh(boost::asio::buffer(dh));
BOOST_LOG_TRIVIAL(info) << "Run server on " << argv[2] << ":" << serverPort; s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
s = std::make_unique<http::server::Server>(argv[2], serverPort, ctx);
resources.registerResources(*s); resources.registerResources(*s);
s->run(); s->run();
} else {
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;
return 1;
} }
} catch (std::exception &e) { } catch (std::exception &e) {
BOOST_LOG_TRIVIAL(error) << e.what() << std::endl; BOOST_LOG_TRIVIAL(error) << e.what() << std::endl;

View File

@ -3,13 +3,13 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/beast.hpp> #include <boost/beast.hpp>
#include "server.hpp" #include "server.hpp"
#include "version.h"
namespace http::server { namespace http::server {
const char* SERVER_HEADER_VALUE = "TerminalWebServer " PROJECT_GIT_REVISION; const char* SERVER_HEADER_VALUE = "TerminalWebServer v0.1";
Connection::Connection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler) Connection::Connection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler)
: socket_(std::move(socket)), connection_manager_(manager), request_handler_(std::move(handler)), request_(false), reply_() { : socket_(std::move(socket)), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() {
} }
void Connection::start() { void Connection::start() {
@ -51,12 +51,11 @@ namespace http::server {
void Connection::doWrite() { void Connection::doWrite() {
reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE});
reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())});
if (request_.httpVersionMajor == 1) { if (request_.http_version_major == 1) {
reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); reply_.headers.push_back({.name = "Connection", .value = "keep-alive"});
} }
const auto ep = socket_.remote_endpoint(); BOOST_LOG_TRIVIAL(info) << "HTTP query " << reply_.status << " " << request_.method << " " << request_.queryUri;
BOOST_LOG_TRIVIAL(info) << "HTTP query " << ep.address().to_string() << ":" << ep.port() << " " << reply_.status << " " << request_.method << " " << request_.queryUri;
auto self(shared_from_this()); auto self(shared_from_this());
async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
@ -72,7 +71,7 @@ namespace http::server {
} }
SslConnection::SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr<boost::asio::ssl::context>& ctx): SslConnection::SslConnection(boost::asio::ip::tcp::socket socket, ConnectionManager &manager, request_handler handler, const std::shared_ptr<boost::asio::ssl::context>& ctx):
stream_(std::move(socket), *ctx), connection_manager_(manager), request_handler_(std::move(handler)), request_(true), reply_() { stream_(std::move(socket), *ctx), connection_manager_(manager), request_handler_(std::move(handler)), request_(), reply_() {
} }
void SslConnection::start() { void SslConnection::start() {
@ -89,7 +88,6 @@ namespace http::server {
void SslConnection::stop() { void SslConnection::stop() {
try { try {
stream_.next_layer().socket().close();
stream_.shutdown(); stream_.shutdown();
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(warning) << "SslConnection::stop(): Can't shutdown ssl socket: " << e.what(); BOOST_LOG_TRIVIAL(warning) << "SslConnection::stop(): Can't shutdown ssl socket: " << e.what();
@ -127,12 +125,11 @@ namespace http::server {
void SslConnection::doWrite() { void SslConnection::doWrite() {
reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE});
reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())});
if (request_.httpVersionMajor == 1) { if (request_.http_version_major == 1) {
reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); reply_.headers.push_back({.name = "Connection", .value = "keep-alive"});
} }
const auto ep = stream_.next_layer().socket().remote_endpoint(); BOOST_LOG_TRIVIAL(info) << "HTTPS query " << reply_.status << " " << request_.method << " " << request_.queryUri;
BOOST_LOG_TRIVIAL(info) << "HTTPS query " << ep.address().to_string() << ":" << ep.port() << " " << reply_.status << " " << request_.method << " " << request_.queryUri;
auto self(shared_from_this()); auto self(shared_from_this());
async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {

View File

@ -21,7 +21,7 @@ namespace http::server {
/// A request received from a client. /// A request received from a client.
class Request { class Request {
public: public:
Request(bool secure); Request();
void reset(); void reset();
std::string getHeaderValue(const std::string& headerName) const; std::string getHeaderValue(const std::string& headerName) const;
@ -29,10 +29,9 @@ namespace http::server {
std::string method; std::string method;
std::string queryUri; std::string queryUri;
std::unique_ptr<Url> url; std::unique_ptr<Url> url;
bool isKeepAlive{}; bool is_keep_alive{};
const bool isSecure; int http_version_major{};
int httpVersionMajor{}; int http_version_minor{};
int httpVersionMinor{};
std::vector<header> headers; std::vector<header> headers;
std::vector<char> payload; std::vector<char> payload;

View File

@ -51,7 +51,7 @@ namespace http::server {
Url::~Url() = default; Url::~Url() = default;
Request::Request(bool secure): isSecure(secure) {} Request::Request() = default;
void Request::reset() { void Request::reset() {
method = ""; method = "";
@ -59,9 +59,9 @@ namespace http::server {
if (url != nullptr) { if (url != nullptr) {
url.reset(nullptr); url.reset(nullptr);
} }
isKeepAlive = false; is_keep_alive = false;
httpVersionMajor = 0; http_version_major = 0;
httpVersionMinor = 0; http_version_minor = 0;
headers.clear(); headers.clear();
payload.clear(); payload.clear();
} }
@ -151,8 +151,8 @@ namespace http::server {
} }
case http_version_slash: case http_version_slash:
if (input == '/') { if (input == '/') {
req.httpVersionMajor = 0; req.http_version_major = 0;
req.httpVersionMinor = 0; req.http_version_minor = 0;
state_ = http_version_major_start; state_ = http_version_major_start;
return indeterminate; return indeterminate;
} else { } else {
@ -160,7 +160,7 @@ namespace http::server {
} }
case http_version_major_start: case http_version_major_start:
if (is_digit(input)) { if (is_digit(input)) {
req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; req.http_version_major = req.http_version_major * 10 + input - '0';
state_ = http_version_major; state_ = http_version_major;
return indeterminate; return indeterminate;
} else { } else {
@ -171,14 +171,14 @@ namespace http::server {
state_ = http_version_minor_start; state_ = http_version_minor_start;
return indeterminate; return indeterminate;
} else if (is_digit(input)) { } else if (is_digit(input)) {
req.httpVersionMajor = req.httpVersionMajor * 10 + input - '0'; req.http_version_major = req.http_version_major * 10 + input - '0';
return indeterminate; return indeterminate;
} else { } else {
return bad; return bad;
} }
case http_version_minor_start: case http_version_minor_start:
if (is_digit(input)) { if (is_digit(input)) {
req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; req.http_version_minor = req.http_version_minor * 10 + input - '0';
state_ = http_version_minor; state_ = http_version_minor;
return indeterminate; return indeterminate;
} else { } else {
@ -189,7 +189,7 @@ namespace http::server {
state_ = expecting_newline_1; state_ = expecting_newline_1;
return indeterminate; return indeterminate;
} else if (is_digit(input)) { } else if (is_digit(input)) {
req.httpVersionMinor = req.httpVersionMinor * 10 + input - '0'; req.http_version_minor = req.http_version_minor * 10 + input - '0';
return indeterminate; return indeterminate;
} else { } else {
return bad; return bad;

View File

@ -3,10 +3,10 @@
#include <fstream> #include <fstream>
#include <utility> #include <utility>
void http::resource::loadFile(const std::string& path, std::vector<char>& content) { static void loadFile(const std::string& path, std::vector<char>& content) {
std::ifstream is(path, std::ios::in | std::ios::binary); std::ifstream is(path, std::ios::in | std::ios::binary);
if (!is) { if (!is) {
throw std::runtime_error("File not found " + path); throw std::runtime_error("File not found");
} }
content.clear(); content.clear();

View File

@ -62,8 +62,6 @@ namespace http::resource {
~GenericResource() override; ~GenericResource() override;
}; };
void loadFile(const std::string& path, std::vector<char>& content);
} }
#endif //RESOURCE_H #endif //RESOURCE_H

View File

@ -23,7 +23,7 @@ namespace http::server {
connections_.clear(); connections_.clear();
} }
Server::Server(const std::string &address, int port) Server::Server(const std::string &address, const std::string &port)
: io_context_(1), signals_(io_context_), acceptor_(io_context_) { : io_context_(1), signals_(io_context_), acceptor_(io_context_) {
// Register to handle the signals that indicate when the server should exit. // Register to handle the signals that indicate when the server should exit.
// It is safe to register for the same signal multiple times in a program, // It is safe to register for the same signal multiple times in a program,
@ -39,7 +39,7 @@ namespace http::server {
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
boost::asio::ip::tcp::resolver resolver(io_context_); boost::asio::ip::tcp::resolver resolver(io_context_);
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint endpoint =
*resolver.resolve(address, std::to_string(port)).begin(); *resolver.resolve(address, port).begin();
acceptor_.open(endpoint.protocol()); acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor_.bind(endpoint); acceptor_.bind(endpoint);
@ -48,7 +48,7 @@ namespace http::server {
doAccept(); doAccept();
} }
Server::Server(const std::string &address, int port, std::shared_ptr<boost::asio::ssl::context> ctx): Server::Server(const std::string &address, const std::string &port, std::shared_ptr<boost::asio::ssl::context> ctx):
ssl_ctx(std::move(ctx)), io_context_(1), signals_(io_context_), acceptor_(io_context_) { ssl_ctx(std::move(ctx)), io_context_(1), signals_(io_context_), acceptor_(io_context_) {
// Register to handle the signals that indicate when the server should exit. // Register to handle the signals that indicate when the server should exit.
// It is safe to register for the same signal multiple times in a program, // It is safe to register for the same signal multiple times in a program,
@ -63,7 +63,7 @@ namespace http::server {
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
boost::asio::ip::tcp::resolver resolver(io_context_); boost::asio::ip::tcp::resolver resolver(io_context_);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, std::to_string(port)).begin(); boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(address, port).begin();
acceptor_.open(endpoint.protocol()); acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor_.bind(endpoint); acceptor_.bind(endpoint);

View File

@ -44,8 +44,8 @@ namespace http::server {
Server &operator=(const Server &) = delete; Server &operator=(const Server &) = delete;
/// Construct the server to listen on the specified TCP address and port /// Construct the server to listen on the specified TCP address and port
explicit Server(const std::string &address, int port); explicit Server(const std::string &address, const std::string &port);
explicit Server(const std::string &address, int port, std::shared_ptr<boost::asio::ssl::context> ctx); explicit Server(const std::string &address, const std::string &port, std::shared_ptr<boost::asio::ssl::context> ctx);
std::vector<std::unique_ptr<resource::BasicResource>> resources; std::vector<std::unique_ptr<resource::BasicResource>> resources;

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
#ifndef TERMINAL_API_DRIVER_H #ifndef TERMINAL_API_DRIVER_H
#define TERMINAL_API_DRIVER_H #define TERMINAL_API_DRIVER_H
#include <deque>
#include <memory> #include <memory>
#include <string> #include <string>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
@ -9,12 +8,10 @@
namespace api_driver { namespace api_driver {
constexpr int CACHE_STATISTICS_UPDATE_MS = 500; ///< время обновления кеша статистики модулятора/демодулятора constexpr int CACHE_STATISTICS_UPDATE_MS = 500;
constexpr int CACHE_DEV_STATE_UPDATE_MS = 3000; ///< время обновления статуса устройства (обновления по воздуху, температуры)
constexpr int CACHE_SETTINGS_UPDATE_MS = 5000; constexpr int CACHE_SETTINGS_UPDATE_MS = 5000;
constexpr int CACHE_QOS_UPDATE_MS = 5000; constexpr int CACHE_QOS_UPDATE_MS = 5000;
class StatisticsLogger;
class TerminalApiDaemon; class TerminalApiDaemon;
/** /**
@ -45,51 +42,33 @@ namespace api_driver {
std::string loadFirmwareVersion() const; std::string loadFirmwareVersion() const;
/** /**
* Установить настройки RX/TX, readback можно получить используя loadTerminalState * Установить настройки RX/TX, readback можно получить используя loadTerminalState
*/ */
void setRxTxSettings(boost::property_tree::ptree &pt); void setRxTxSettings(boost::property_tree::ptree &pt);
#ifdef MODEM_IS_SCPC
/** /**
* Установить настройки DPDI, readback можно получить используя loadTerminalState. * Установить настройки CinC, readback можно получить используя loadTerminalState.
* @note Для TDMA и SCPC модемов эти настройки доступны */
*/ void setCincSettings(boost::property_tree::ptree &pt);
void setDpdiSettings(boost::property_tree::ptree &pt); #endif
/** /**
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState. * Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
*/ */
void setBucLnbSettings(boost::property_tree::ptree &pt); void setBucLnbSettings(boost::property_tree::ptree &pt);
/** /**
* Установить настройки QoS, readback можно получить используя loadTerminalState. * Установить настройки QoS, readback можно получить используя loadTerminalState.
*/ */
void setQosSettings(boost::property_tree::ptree &pt); void setQosSettings(boost::property_tree::ptree &pt);
void setNetworkSettings(boost::property_tree::ptree &pt); void setNetworkSettings(boost::property_tree::ptree & pt);
void setDebugSendSettings(boost::property_tree::ptree & pt);
void resetDefaultSettings(); void resetDefaultSettings();
void executeInApi(const std::function<void(TSID sid)> &callback); void executeInApi(const std::function<void(TSID sid)>& callback);
#ifdef MODEM_IS_SCPC
std::string getLoggingStatisticsSettings();
void setLoggingStatisticsSettings(boost::property_tree::ptree &pt);
/**
* Получить статистику в формате json. Выход будет дописан в вектор
* @param jsonOut вектор, куда должен быть записан результат. Данные будут дописаны к существующим, формат: []
* @param timeStart
* @param timeEnd
* @param ordering
* @param maxItems
*/
void readLoggingStatistics(std::vector<uint8_t>& out, int timeStart = -1, int timeEnd = -1, bool ordering = false, int maxItems = -1);
#endif
#ifdef MODEM_IS_TDMA
std::string getOtaFileLocation() const;
#endif
static std::string loadSysInfo(); static std::string loadSysInfo();
@ -98,13 +77,6 @@ namespace api_driver {
private: private:
std::unique_ptr<TerminalApiDaemon> daemon; std::unique_ptr<TerminalApiDaemon> daemon;
}; };
/**
* Функция для создания экранированной строки (для json)
* @param source исходная строка (например, {123"})
* @return {"123\""}
*/
std::string buildEscapedString(const std::string &source);
} }
#endif //TERMINAL_API_DRIVER_H #endif //TERMINAL_API_DRIVER_H

View File

@ -1,12 +0,0 @@
#ifndef VERSION_H
#define VERSION_H
#ifndef PROJECT_GIT_REVISION
#warning "PROJECT_GIT_REVISION should be defined!"
#define PROJECT_GIT_REVISION "no-git"
#endif
#define PROJECT_BUILD_TIME __TIMESTAMP__ " compiler version " __VERSION__
#endif //VERSION_H

View File

@ -2,224 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Title</title>
<title>RSCM-101</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/fields.css">
<style>
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 10;
background: var(--bg-selected);
}
body { /* значение по-умолчанию */ --header-height: 60px; }
#content {
padding-top: var(--header-height);
}
.l3-proto-label > * {
display: inline-block;
}
.l3-proto-label input[type=checkbox] {
width: auto;
}
.
</style>
</head> </head>
<body> <body>
<div id="app" hidden>
<header>
<div class="tabs-header">
<span style="font-weight:bold">RSCM-101</span>
<a href="/#monitoring" class="tabs-btn">Мониторинг</a>
<a href="/#setup" class="tabs-btn">Настройки</a>
<a href="/#qos" class="tabs-btn">QoS</a>
<a href="/#admin" class="tabs-btn">Администрирование</a>
<a class="tabs-btn active">Разработчикам</a>
<a href="/logout" class="tabs-btn">Выход</a>
</div>
</header>
<div id="content">
<div class="settings-set-container">
<h2>Настройки записи пакетов в IPS</h2>
<label>
<span>Активировать запись</span>
<span class="toggle-input"><input type="checkbox" v-model="params.loggingIps" /><span class="slider"></span></span>
</label>
<button class="action-button" @click="settingsSubmitLoggingIps()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingIps"></span></button>
</div>
<div class="settings-set-container" v-if="settingFetchComplete">
<h2>Настройки журналирования статистики</h2>
<label>
<span>Активировать запись</span>
<span class="toggle-input"><input type="checkbox" v-model="params.loggingStatistics.en" /><span class="slider"></span></span>
</label>
<label>
<span>Период сбора статистики</span>
<select v-model="params.loggingStatistics.logPeriodMs">
<option :value="30000">30s</option>
<option :value="10000">10s</option>
<option :value="5000">5s</option>
<option :value="2000">2s</option>
<option :value="1000">1s</option>
<option :value="500">500ms</option>
<option :value="250">250ms (!)</option>
<option :value="50">FULL SPEED (!)</option>
</select>
</label>
<label>
<span>Время хранения</span>
<select v-model="params.loggingStatistics.maxAgeMs">
<option :value="60000">1 мин</option>
<option :value="300000">5 мин</option>
<option :value="900000">15 мин</option>
<option :value="1800000">30 мин</option>
<option :value="3600000">1 час</option>
<option :value="7200000">2 часа</option>
<option :value="21600000">6 часов</option>
<option :value="43200000">12 часов</option>
<option :value="86400000">24 часа</option>
<option :value="172800000">48 часов</option>
<option :value="604800000">7 суток</option>
</select>
</label>
<button class="action-button" @click="settingsSubmitLoggingStatistics()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingStatistics"></span></button>
</div>
<div class="settings-set-container">
<h2>Просмотр логов</h2>
<button class="action-button" @click="logView()">Обновить <span class="submit-spinner" v-show="submitStatus.logView"></span></button>
<a href="/dev/logs.csv" class="action-button" download>Скачать</a>
<div v-if="logsTable.headers.length != 0" style="overflow-x: auto;">
<table>
<tbody>
<tr>
<th v-for="h in logsTable.headers">{{ h }}</th>
</tr>
<tr v-for="r in logsTable.rows">
<td v-for="value in r">{{ value }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="/js/vue.js?v=3.5.13"></script>
<script>
// для обновления высоты хидера
function updateHeaderHeight() { const header = document.querySelector('header'); document.body.style.setProperty('--header-height', `${header.offsetHeight}px`); }
window.addEventListener('load', updateHeaderHeight); window.addEventListener('resize', updateHeaderHeight);
const app = Vue.createApp({
data() {
return {
// false - означает что статистика не отправляется, true - отправляется
submitStatus: {
loggingIps: false,
loggingStatistics: false,
logView: false
},
params: {
loggingIps: false,
loggingStatistics: {
en: false,
logPeriodMs: 1000,
maxAgeMs: 0
}
},
logsTable: {
headers: [],
rows: []
},
settingFetchComplete: false
}
},
methods: {
settingsSubmitLoggingIps() {
if (this.submitStatus.loggingIps) { return }
this.submitStatus.loggingIps = true
fetch(`/dev/cpapicall?f=SetDmaDebug&param=log_bool&value=${this.params.loggingIps}`, {method: 'POST', headers: {'Content-Type': 'application/json'}, credentials: 'same-origin' })
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.loggingIps = false })
},
settingsSubmitLoggingStatistics() {
function modcodToStr(modcod) {
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
const modcods = [
"DUMMY",
"QPSK 1/4", "QPSK 1/3", "QPSK 2/5", "QPSK 1/2", "QPSK 3/5", "QPSK 2/3", "QPSK 3/4", "QPSK 4/5", "QPSK 5/6", "QPSK 8/9", "QPSK 9/10",
"8PSK 3/5", "8PSK 2/3", "8PSK 3/4", "8PSK 5/6", "8PSK 8/9", "8PSK 9/10",
"16APSK 2/3", "16APSK 3/4", "16APSK 4/5", "16APSK 5/6", "16APSK 8/9", "16APSK 9/10",
"32APSK 3/4", "32APSK 4/5", "32APSK 5/6", "32APSK 8/9", "32APSK 9/10",
]
if (typeof modcod != "number") {
return "?";
}
if (modcod < 0 || modcod >= modcods.length) {
return `? (${modcod})`
}
return modcods[modcod]
}
if (this.submitStatus.loggingStatistics) { return }
let query = {
"en": this.params.loggingStatistics.en,
"logPeriodMs": this.params.loggingStatistics.logPeriodMs,
"maxAgeMs": this.params.loggingStatistics.maxAgeMs,
}
this.submitStatus.loggingStatistics = true
fetch('/dev/settings', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query), credentials: 'same-origin' })
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateLoggingStatisticsSettings(vals) })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.loggingStatistics = false })
},
logView() {
if (this.submitStatus.logView) { return }
this.submitStatus.logView = true
fetch(`/dev/logs.csv`, {method: 'GET', credentials: 'same-origin' })
.then(async (resp) => {
let logfileContent = await resp.text()
const lines = logfileContent.trim().split(/\r\n|\n/)
// Первая строка содержит заголовки
this.logsTable.headers = lines.shift().split('\t')
// Остальные строки содержат данные
this.logsTable.rows = lines.map(line => line.split('\t'))
})
.catch((reason) => { alert(`Ошибка при чтении логов: ${reason}`) })
.finally(() => { this.submitStatus.logView = false })
},
updateLoggingStatisticsSettings(vals) {
this.params.loggingStatistics.en = vals['logstat']['en']
this.params.loggingStatistics.logPeriodMs = vals['logstat']['logPeriodMs']
this.params.loggingStatistics.maxAgeMs = vals['logstat']['maxAgeMs']
}
},
mounted() {
const doFetchSettings = async () => {
let d = await fetch("/dev/settings")
let vals = await d.json()
this.settingFetchComplete = true
this.updateLoggingStatisticsSettings(vals)
}
doFetchSettings().then(() => {})
document.getElementById("app").removeAttribute("hidden")
}
});
app.mount('#app')
</script>
</body> </body>
</html> </html>

View File

@ -84,14 +84,14 @@
padding: 1em; padding: 1em;
} }
.statistics-container th { .settings-set-container th {
text-align: left; text-align: left;
font-weight: normal; font-weight: normal;
padding-right: 1em;
}
.settings-set-container td {
min-width: 10em;
} }
th { padding: 0.2em 1em 0.2em 0.2em; }
.statistics-container td { min-width: 10em; }
td { padding: 0.2em; }
.tabs-item-flex-container h2 { .tabs-item-flex-container h2 {
margin-top: 0; margin-top: 0;
} }
@ -134,10 +134,9 @@ select * {
color: var(--text-color); color: var(--text-color);
} }
.settings-set-container th, .settings-set-container td { .settings-set-container tr > * {
border-bottom: solid 1px var(--bg-element); border-bottom: solid 1px var(--text-color2);
} }
.settings-set-container table { border-collapse: collapse; }
.settings-set-container tr:hover { .settings-set-container tr:hover {
background: var(--bg-selected); background: var(--bg-selected);

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -111,8 +111,7 @@
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify(requestData), body: JSON.stringify(requestData)
credentials: 'same-origin'
}).then(response => { }).then(response => {
// Обработка ответа сервера // Обработка ответа сервера
response.json().then((value) => { response.json().then((value) => {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff