Compare commits

...

15 Commits

Author SHA1 Message Date
479200df9e изменил формат аптайма 2025-01-09 12:49:59 +03:00
20cf08e2a1 добавил получение sysinfo и вывод аптайма в веб-морде 2025-01-09 12:02:42 +03:00
ab654a754c патч для переподключения к API в случае ошибок 2025-01-09 11:28:35 +03:00
8990fed8f0 зачатки страницы для разработчиков 2024-12-28 16:42:14 +03:00
be6c8023c5 добавил защиту от двойного обновления прошивки, добавил настройки TCP-акселерации в веб (они пока не работает из-за API) 2024-12-28 16:41:58 +03:00
24cb1061a7 добавил отключенную скорость кода 3/5 2024-12-28 11:20:37 +03:00
77ba05e407 косметические исправления: увеличил шрифт, убрал жирный шрифт в таблицах 2024-12-25 11:05:22 +03:00
f3454897d8 исправление полей в SCPC модеме 2024-12-24 17:21:04 +03:00
dc4e37eb8a Merge branch 'refs/heads/dev-scpc-new-rolloff' 2024-12-23 13:57:59 +03:00
522abee794 косметические исправления страниц 2024-12-23 12:52:52 +03:00
8278e4119f косметические исправления страниц 2024-12-23 09:48:02 +03:00
0a3a282d0f некоторые нововведения в front-generator 2024-12-23 09:40:43 +03:00
3d97824ee7 фикс модкода CCM 2024-12-23 09:39:50 +03:00
55fa498dc1 обновил поле rolloff 2024-12-16 09:39:39 +03:00
f5c5caa31c добавил зачатки front-generator, исправил мелкий баг QoS 2024-12-16 09:30:16 +03:00
15 changed files with 2053 additions and 92 deletions

View File

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

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -84,6 +84,8 @@ class ServerResources {
std::unique_ptr<api_driver::ApiDriver> api;
http::auth::AuthProvider auth{};
bool upgradeOrRebootRunning = false;
static void onUploadFirmware(const http::server::Request& req) {
std::ofstream f("/tmp/firmware.zip", std::ios::binary);
@@ -204,6 +206,8 @@ public:
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"mainState":)";
result += api->loadTerminalState();
result += R"(,"sysinfo":)";
result += api->loadSysInfo();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
@@ -415,7 +419,7 @@ public:
}
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/reboot", this->auth, 0, [](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") {
http::server::stockReply(http::server::bad_request, rep);
}
@@ -424,6 +428,7 @@ public:
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok"})";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
this->upgradeOrRebootRunning = true;
system(REBOOT_COMMAND);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
@@ -439,10 +444,11 @@ public:
system(REBOOT_COMMAND);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [](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") {
http::server::stockReply(http::server::bad_request, rep);
}
this->upgradeOrRebootRunning = true;
onUploadFirmware(req);
rep.status = http::server::ok;
@@ -454,12 +460,14 @@ public:
result += http::utils::sha256(req.payload.data(), req.payload.size());
result += "\"}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
this->upgradeOrRebootRunning = false;
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/doFirmwareUpgrade", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
this->upgradeOrRebootRunning = true;
doTerminalUpgrade();
rep.status = http::server::ok;
rep.headers.clear();
@@ -467,6 +475,19 @@ public:
const auto result = api->loadFirmwareVersion();
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
boost::ignore_unused(req);
sf->serve(INTERNET_JPG, rep);
}));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/fetchParams", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"status":"ok","fwsize":)";
result += std::to_string(req.payload.size());
result += R"(,"sha256":")";
result += "\"}";
}));
}
~ServerResources() = default;

View File

@@ -8,6 +8,8 @@
#include <boost/thread.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <sys/sysinfo.h>
typedef boost::property_tree::ptree::path_type json_path;
@@ -107,17 +109,19 @@ static std::ostream& operator<<(std::ostream& out, CP_Result result) {
return out;
}
static void logCpApiError(const char* desc, CP_Result err) {
if (err != OK) {
BOOST_LOG_TRIVIAL(error) << "CP API error in " << desc << ": " << err;
}
}
/**
* Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки
*/
class api_driver::TerminalApiDaemon {
private:
CP_Result lastCpError = OK;
void logCpApiError(const char* desc, CP_Result err) {
if (err != OK) {
BOOST_LOG_TRIVIAL(error) << "CP API error in " << desc << ": " << err;
this->lastCpError = err;
}
}
void updateState() {
modulator_state modulator{};
demodulator_state demodulator{};
@@ -224,38 +228,52 @@ private:
}
}
void connectToApi() {
{
std::lock_guard _lock(this->stateMutex);
this->deviceInitState = "Not connected to API";
}
unsigned int access{};
for (int connectAttempt = 0;; connectAttempt++) {
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::connectToApi(): Try to connect api (attempt " << connectAttempt << ")...";
auto res = CP_Login("admin", "pass", &sid, &access);
logCpApiError(R"(api_driver::TerminalApiDaemon::connectToApi()->CP_Login("admin", "pass"))", res);
if (res != OK) {
boost::this_thread::sleep_for(boost::chrono::duration(boost::chrono::milliseconds(1000)));
} else {
break;
}
}
std::string tmp;
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmp));
{
std::lock_guard _lock(this->stateMutex);
this->deviceInitState = tmp;
}
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::connectToApi(): Success connect!";
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::connectToApi(): API status: " << tmp;
TerminalFirmwareVersion f;
logCpApiError("api_driver::TerminalApiDaemon::connectToApi()->CP_GetNetwork(version)", CP_GetNetwork(sid, "version", &f.version));
logCpApiError("api_driver::TerminalApiDaemon::connectToApi()->CP_GetNetwork(chip_id)", CP_GetNetwork(sid, "chip_id", &f.modemId));
rtrim(f.modemId);
logCpApiError("api_driver::TerminalApiDaemon::connectToApi()->CP_GetNetwork(serial)", CP_GetNetwork(sid, "serial", &f.modemSn));
logCpApiError("api_driver::TerminalApiDaemon::connectToApi()->CP_GetNetwork(mac_eth0)", CP_GetNetwork(sid, "mac_eth0", &f.macMang));
logCpApiError("api_driver::TerminalApiDaemon::connectToApi()->CP_GetNetwork(mac_eth1)", CP_GetNetwork(sid, "mac_eth1", &f.macData));
{
std::lock_guard _lock(this->firmwareMutex);
this->firmware = f;
}
this->lastCpError = OK;
}
void run() {
// это демон, который в бесконечном цикле опрашивает API
{
TSID sid{};
unsigned int access{};
for (int connectAttempt = 0;; connectAttempt++) {
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::run(): Try to connect api (attempt " << connectAttempt << ")...";
auto res = CP_Login("admin", "pass", &sid, &access);
logCpApiError(R"(api_driver::TerminalApiDaemon::run()->CP_Login("admin", "pass"))", res);
if (res != OK) {
boost::this_thread::sleep_for(boost::chrono::duration(boost::chrono::milliseconds(1000)));
} else {
break;
}
}
std::string tmp;
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmp));
{
std::lock_guard _lock(this->stateMutex);
this->deviceInitState = tmp;
}
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::run(): Success connect!";
BOOST_LOG_TRIVIAL(info) << "api_driver::TerminalApiDaemon::run(): API status: " << tmp;
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetNetwork(version)", CP_GetNetwork(sid, "version", &firmware.version));
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetNetwork(chip_id)", CP_GetNetwork(sid, "chip_id", &firmware.modemId));
rtrim(firmware.modemId);
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetNetwork(serial)", CP_GetNetwork(sid, "serial", &firmware.modemSn));
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetNetwork(mac_eth0)", CP_GetNetwork(sid, "mac_eth0", &firmware.macMang));
logCpApiError("api_driver::TerminalApiDaemon::run()->CP_GetNetwork(mac_eth1)", CP_GetNetwork(sid, "mac_eth1", &firmware.macData));
}
this->connectToApi();
struct IntervalUpdate_t {
int64_t lastUpdate;
@@ -317,6 +335,12 @@ private:
};
while (true) {
if (this->lastCpError == ERROR || this->lastCpError == TIMEOUT) {
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::run(): close current daemon session caused error " << this->lastCpError;
CP_Logout(this->sid);
this->connectToApi();
}
int64_t sleepTime = 60000; // минута по-умолчанию
auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
for (auto& u: updaters) {
@@ -360,13 +384,14 @@ private:
bool qosEnabled;
std::string qosClassesJson;
std::shared_mutex firmwareMutex;
TerminalFirmwareVersion firmware;
public:
std::mutex cpApiMutex;
TSID sid;
boost::thread daemon;
TerminalFirmwareVersion firmware;
explicit TerminalApiDaemon(): deviceInitState("Not connected to API"), qosEnabled(false), sid(0), daemon([this]() { this->run(); }) {
this->qosClassesJson = DEFAULT_QOS_CLASSES;
}
@@ -524,7 +549,7 @@ public:
void setQosSettings(bool enabled, const std::string& str, bool readback = true) {
std::lock_guard lock(this->cpApiMutex);
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug()", CP_SetDmaDebug(sid, "begin_save_config", ""));
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetQoSSettings()", CP_SetQoSSettings(this->sid, str, enabled));
if (readback) {
bool tmp1; std::string tmp2;
@@ -535,7 +560,7 @@ public:
this->qosClassesJson = tmp2.empty() ? DEFAULT_QOS_CLASSES : tmp2;
}
}
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug()", CP_SetDmaDebug(sid, "save_config", ""));
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
}
void setNetworkSettings(TerminalNetworkSettings& s, bool readback = true) {
@@ -584,6 +609,11 @@ public:
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
}
TerminalFirmwareVersion getFirmware() {
std::shared_lock lock(this->firmwareMutex);
return firmware;
}
void resetPacketStatistics() {
std::string tmp;
std::lock_guard lock(this->cpApiMutex);
@@ -799,7 +829,7 @@ std::string api_driver::ApiDriver::loadSettings() const {
result << ",\"tx.cymRate\":" << modSettings.baudrate;
result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3);
result << ",\"dvbs2.frameSizeNormal\":" << boolAsStr(!(modSettings.modcod_tx & 2));
result << ",\"dvbs2.ccm_modcod\":" << (modSettings.modcod_tx >> 4);
result << ",\"dvbs2.ccm_modcod\":" << (modSettings.modcod_tx >> 2);
// result << ",\"dvbs2.isPilots\":" << "null";
result << ",\n\"dvbs2.isAcm\":" << boolAsStr(acmSettings.enable);
@@ -884,11 +914,12 @@ std::string api_driver::ApiDriver::loadFirmwareVersion() const {
}
std::stringstream result;
result << "{\n\"fw.version\":" << buildEscapedString(daemon->firmware.version);
result << ",\"fw.modemId\":" << buildEscapedString(daemon->firmware.modemId);
result << ",\"fw.modemSn\":" << buildEscapedString(daemon->firmware.modemSn);
result << ",\"fw.macMang\":" << buildEscapedString(daemon->firmware.macMang);
result << ",\"fw.macData\":" << buildEscapedString(daemon->firmware.macData);
auto firmware = daemon->getFirmware();
result << "{\n\"fw.version\":" << buildEscapedString(firmware.version);
result << ",\"fw.modemId\":" << buildEscapedString(firmware.modemId);
result << ",\"fw.modemSn\":" << buildEscapedString(firmware.modemSn);
result << ",\"fw.macMang\":" << buildEscapedString(firmware.macMang);
result << ",\"fw.macData\":" << buildEscapedString(firmware.macData);
result << "\n}";
return result.str();
}
@@ -985,6 +1016,8 @@ void api_driver::ApiDriver::setCincSettings(boost::property_tree::ptree &pt) {
s.max_delay = pt.get<uint32_t>(json_path("cinc.delayMax", '/'));
s.min_delay = pt.get<uint32_t>(json_path("cinc.delayMin", '/'));
s.freq_offset = pt.get<uint32_t>(json_path("cinc.searchBandwidth", '/'));
this->daemon->setSettingsCinc(s);
}
#endif
@@ -1060,4 +1093,42 @@ void api_driver::ApiDriver::executeInApi(const std::function<void(TSID sid)>& ca
}
}
std::string api_driver::ApiDriver::loadSysInfo() {
std::stringstream result;
struct sysinfo info{};
sysinfo(&info);
struct sysinfo {
long uptime; /* Seconds since boot */
unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
unsigned long totalram; /* Total usable main memory size */
unsigned long freeram; /* Available memory size */
unsigned long sharedram; /* Amount of shared memory */
unsigned long bufferram; /* Memory used by buffers */
unsigned long totalswap; /* Total swap space size */
unsigned long freeswap; /* Swap space still available */
unsigned short procs; /* Number of current processes */
unsigned long totalhigh; /* Total high memory size */
unsigned long freehigh; /* Available high memory size */
unsigned int mem_unit; /* Memory unit size in bytes */
};
result << "{\n\"uptime\":" << info.uptime;
result << ",\"load1min\":" << info.loads[0];
result << ",\"load5min\":" << info.loads[1];
result << ",\"load15min\":" << info.loads[2];
result << ",\"totalram\":" << info.totalram;
result << ",\"freeram\":" << info.freeram;
result << ",\"sharedram\":" << info.sharedram;
result << ",\"bufferram\":" << info.bufferram;
result << ",\"totalswap\":" << info.totalswap;
result << ",\"freeswap\":" << info.freeswap;
result << ",\"procs\":" << static_cast<long>(info.procs);
result << ",\"totalhigh\":" << info.totalhigh;
result << ",\"freehigh\":" << info.freehigh;
result << ",\"mem_unit\":" << info.mem_unit;
result << "\n}";
return result.str();
}
api_driver::ApiDriver::~ApiDriver() = default;

View File

@@ -70,6 +70,8 @@ namespace api_driver {
void executeInApi(const std::function<void(TSID sid)>& callback);
static std::string loadSysInfo();
~ApiDriver();
private:

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

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

10
static/dev.html Normal file
View File

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

View File

@@ -7,7 +7,6 @@
}
.tabs-btn {
text-decoration: none;
font-size: 18px;
border: none;
padding: 10px 25px;
text-align: center;
@@ -87,6 +86,7 @@
.settings-set-container th {
text-align: left;
font-weight: normal;
padding-right: 1em;
}
.settings-set-container td {

View File

@@ -725,30 +725,30 @@
};
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var __assign = function() {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var uid$2 = 0;

View File

@@ -118,6 +118,7 @@
<tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr>
<tr><th>Температура ZYNQ</th><td>{{ stat_device.zynq }} °C</td></tr>
<tr><th>Температура FPGA</th><td>{{ stat_device.fpga }} °C</td></tr>
<tr><th>Uptime</th><td>{{ stat_os.uptime }}</td></tr>
</tbody>
</table>
</div>
@@ -161,7 +162,7 @@
<span>Входные данные</span>
<select v-model="param.general.isTestInputData">
<option :value="false">Ethernet</option>
<option :value="true">Тест (CW)</option>
<option :value="true">Тест</option>
</select>
</label>
<h3>Параметры передачи</h3>
@@ -176,6 +177,7 @@
<label>
<span>Roll-off</span>
<select v-model="param.tx.rolloff">
<option :value="2">0.02</option>
<option :value="5">0.05</option>
<option :value="10">0.10</option>
<option :value="15">0.15</option>
@@ -325,6 +327,7 @@
<label>
<span>Roll-off</span>
<select v-model="param.rx.rolloff">
<option :value="2">0.02</option>
<option :value="5">0.05</option>
<option :value="10">0.10</option>
<option :value="15">0.15</option>
@@ -345,6 +348,10 @@
<option value="delay">Окном задержки</option>
</select>
</label>
<label>
<span>Полоса поиска, кгц ±</span>
<input v-model="param.cinc.searchBandwidth" type="number" min="0" max="100" step="1"/>
</label>
<h3 v-show="param.cinc.mode === 'positional'">Настройки позиционирования</h3>
<label v-show="param.cinc.mode === 'positional'">
@@ -534,8 +541,8 @@
</template>
<button class="action-button" @click="settingsSubmitQoS()">Применить <span class="submit-spinner" v-show="submitStatus.qos"></span></button>
<h2 hidden>Настройки TCP-акселерации</h2>
<div hidden class="settings-set-container">
<h2>Настройки TCP-акселерации</h2>
<div class="settings-set-container">
<label>
<span>Активировать акселерацию</span>
<span class="toggle-input"><input type="checkbox" v-model="param.tcpAccel.en" /><span class="slider"></span></span>
@@ -545,7 +552,7 @@
<input type="number" v-model="param.tcpAccel.maxConnections" min="1" max="10000" />
</label>
</div>
<button hidden class="action-button" @click="settingsSubmitTcpAccel()">Применить <span class="submit-spinner" v-show="submitStatus.tcpAccel"></span></button>
<button class="action-button" @click="settingsSubmitTcpAccel()">Применить <span class="submit-spinner" v-show="submitStatus.tcpAccel"></span></button>
</div>
<div class="tabs-body-item" v-if="activeTab === 'admin' && settingFetchComplete">
<h2>Настройки сети</h2>
@@ -611,13 +618,16 @@
<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.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 hidden> <!-- v-show="submitStatus.modemReboot !== null" -->
<img src="/internet.jpg" loading="lazy" alt="internet"/>
</div>
<div>
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
</div>
@@ -658,15 +668,13 @@
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 3/5",
"QPSK 2/3",
"QPSK 3/4",
"QPSK 4/5",
@@ -674,7 +682,7 @@
"QPSK 8/9",
"QPSK 9/10",
"8PSK 3/5", // отключено
"8PSK 3/5",
"8PSK 2/3",
"8PSK 3/4",
"8PSK 5/6",
@@ -708,7 +716,7 @@
case '1/3': return 2
case '2/5': return 3
case '1/2': return 4
case '3/5': return 5 // отключено
case '3/5': return 5
case '2/3': return 6
case '3/4': return 7
case '4/5': return 8
@@ -719,7 +727,7 @@
}
case '8psk':
switch (speed) {
case '3/5': return 12 // отключено
case '3/5': return 12
case '2/3': return 13
case '3/4': return 14
case '5/6': return 15
@@ -842,6 +850,7 @@
stat_device: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0
},
stat_os: {uptime: '?'},
param: {
general: {
@@ -972,9 +981,9 @@
// NOTE модкоды со скоростью хода 3/5 не работают
switch (modulation) {
case 'qpsk':
return ['1/4', '1/3', '2/5', '1/2', /* '3/5',*/ '2/3', '3/4', '4/5', '5/6', '8/9', '9/10']
return ['1/4', '1/3', '2/5', '1/2', '3/5', '2/3', '3/4', '4/5', '5/6', '8/9', '9/10']
case '8psk':
return [/* '3/5',*/ '2/3', '3/4', '5/6', '8/9', '9/10']
return ['3/5', '2/3', '3/4', '5/6', '8/9', '9/10']
case '16apsk':
return ['2/3', '3/4', '4/5', '5/6', '8/9', '9/10']
case '32apsk':
@@ -1030,6 +1039,20 @@
this.stat_device.fpga = vals["mainState"]["device.fpga"]
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}:${secs}`
if (uptime > 0) { res = `${uptime} days, ` + res }
this.stat_os.uptime = res
} else {
this.stat_os.uptime = '?'
}
},
resetPacketsStatistics() {
@@ -1197,10 +1220,10 @@
"rt3": [],
"cd": []
}
for (let i = 0; i < this.param.qos.rt1.length; i++) { query.rt1.push(_translateQosClass('rt', this.param.qos.rt1[i])) }
for (let i = 0; i < this.param.qos.rt2.length; i++) { query.rt2.push(_translateQosClass('rt', this.param.qos.rt2[i])) }
for (let i = 0; i < this.param.qos.rt3.length; i++) { query.rt3.push(_translateQosClass('rt', this.param.qos.rt3[i])) }
for (let i = 0; i < this.param.qos.cd.length; i++) { query.cd.push(_translateQosClass('rt', this.param.qos.cd[i])) }
for (let i = 0; i < this.param.qos.rt1.length; i++) { query.rt1.push(_translateQosClass('rt1', this.param.qos.rt1[i])) }
for (let i = 0; i < this.param.qos.rt2.length; i++) { query.rt2.push(_translateQosClass('rt2', this.param.qos.rt2[i])) }
for (let i = 0; i < this.param.qos.rt3.length; i++) { query.rt3.push(_translateQosClass('rt3', this.param.qos.rt3[i])) }
for (let i = 0; i < this.param.qos.cd.length; i++) { query.cd.push(_translateQosClass('cd', this.param.qos.cd[i])) }
console.log(query)
fetch('/api/set/qos', {

View File

@@ -105,6 +105,7 @@
<tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr>
<tr><th>Температура ZYNQ</th><td>{{ stat_device.zynq }} °C</td></tr>
<tr><th>Температура FPGA</th><td>{{ stat_device.fpga }} °C</td></tr>
<tr><th>Uptime</th><td>{{ stat_os.uptime }}</td></tr>
</tbody>
</table>
</div>
@@ -125,7 +126,7 @@
<span>Входные данные</span>
<select v-model="param.tx.isTestInputData">
<option :value="false">Ethernet</option>
<option :value="true">Тест (CW)</option>
<option :value="true">Тест</option>
</select>
</label>
<h3>Параметры передачи</h3>
@@ -418,7 +419,7 @@
<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.macManagement }}</td></tr>
<tr><th>MAC интерфейса управления</th><td>{{ about.macData }}</td></tr>
</tbody>
</table>
@@ -641,6 +642,7 @@
stat_device: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0
},
stat_os: {uptime: '?'},
param: {
tx: {
@@ -775,6 +777,20 @@
this.stat_device.fpga = vals["mainState"]["device.fpga"]
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}:${secs}`
if (uptime > 0) { res = `${uptime} days, ` + res }
this.stat_os.uptime = res
} else {
this.stat_os.uptime = '?'
}
},
resetPacketsStatistics() {

View File

@@ -53,6 +53,13 @@ body {
margin: 0.5em;
}
/* увеличение размера шрифтов */
* { font-size: large; }
h1 { font-size: xxx-large; }
h2 { font-size: xx-large; }
h3 { font-size: larger; }
/* ========== MAIN STYLES ========== */
.value-good {