Compare commits

...

37 Commits

Author SHA1 Message Date
479200df9e изменил формат аптайма 2025-01-09 12:49:59 +03:00
20cf08e2a1 добавил получение sysinfo и вывод аптайма в веб-морде 2025-01-09 12:02:42 +03:00
ab654a754c патч для переподключения к API в случае ошибок 2025-01-09 11:28:35 +03:00
8990fed8f0 зачатки страницы для разработчиков 2024-12-28 16:42:14 +03:00
be6c8023c5 добавил защиту от двойного обновления прошивки, добавил настройки TCP-акселерации в веб (они пока не работает из-за API) 2024-12-28 16:41:58 +03:00
24cb1061a7 добавил отключенную скорость кода 3/5 2024-12-28 11:20:37 +03:00
77ba05e407 косметические исправления: увеличил шрифт, убрал жирный шрифт в таблицах 2024-12-25 11:05:22 +03:00
f3454897d8 исправление полей в SCPC модеме 2024-12-24 17:21:04 +03:00
dc4e37eb8a Merge branch 'refs/heads/dev-scpc-new-rolloff' 2024-12-23 13:57:59 +03:00
522abee794 косметические исправления страниц 2024-12-23 12:52:52 +03:00
8278e4119f косметические исправления страниц 2024-12-23 09:48:02 +03:00
0a3a282d0f некоторые нововведения в front-generator 2024-12-23 09:40:43 +03:00
3d97824ee7 фикс модкода CCM 2024-12-23 09:39:50 +03:00
55fa498dc1 обновил поле rolloff 2024-12-16 09:39:39 +03:00
f5c5caa31c добавил зачатки front-generator, исправил мелкий баг QoS 2024-12-16 09:30:16 +03:00
833374a80e сделал API статической библиотекой 2024-11-29 17:52:25 +03:00
b67011b9a3 добавил получение статуса из API вместе со статистикой 2024-11-29 17:37:25 +03:00
91e9c0301e фикс: билд SCPC модема 2024-11-29 15:12:27 +03:00
98dcc06a6a логгирование ошибок API (установка параметров) 2024-11-29 15:04:38 +03:00
e0aacfe8aa логгирование ошибок API на всех периодических запросах 2024-11-29 14:11:36 +03:00
3e4ffc8281 изменил логику подключения к API 2024-11-29 13:36:39 +03:00
1f8ea04f43 добавил динамическую линковку для boost::log 2024-11-28 17:43:41 +03:00
ad6d734f4a скрыл ржачную картинку 2024-11-28 16:58:57 +03:00
6d79d60eb1 исправил надпись 2024-11-28 16:56:34 +03:00
572a2583f0 логика работы TDMA 2024-11-28 16:56:20 +03:00
925fec6dda добавил фронт для TDMA 2024-11-28 14:57:29 +03:00
5f3d5791da исправление замечаний от Кости 2024-11-27 11:20:55 +03:00
c05a9cff7a добавил повторные попытки подключения к API, если не удалось подключиться 2024-11-15 15:49:20 +03:00
dff0ba1cd3 фикс: центральная частота 2024-11-15 15:37:08 +03:00
43f35da9a2 фикс: цвета в темной теме 2024-11-15 15:22:56 +03:00
ac04c0545b фикс: версия ПО и прочее не показывалось в вебке 2024-11-15 15:12:19 +03:00
3e46f82c0e фикс: билда релизной версии 2024-11-15 14:30:02 +03:00
1536914888 фикс: не использовался путь для статических файлов + немного поменял цвета 2024-11-15 14:26:30 +03:00
1a80e9d455 фикс: не использовался путь для статических файлов 2024-11-15 14:15:10 +03:00
e2618e0300 обновление vue js 2024-11-15 13:50:14 +03:00
1d73547eae cleanup + изменение цветов темы 2024-11-15 13:45:25 +03:00
4a27a46c27 фикс работы с синхронизацией потоков 2024-11-15 10:55:52 +03:00
23 changed files with 4213 additions and 743 deletions

View File

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

View File

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

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 %}

View File

@@ -3,10 +3,10 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RSCM-101</title> <title>{{ modem_name }}</title>
<link rel="stylesheet" type="text/css" href="/style.css"> <link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/fields.css"> <link rel="stylesheet" type="text/css" href="/fields.css">
<style> <style>{% raw %}
header { header {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -31,27 +31,27 @@
.l3-proto-label input[type=checkbox] { .l3-proto-label input[type=checkbox] {
width: auto; width: auto;
} }
{% endraw %}
</style> </style>
</head> </head>
<body> <body>
<div id="app" hidden> <div id="app" hidden>
<header> <header>{% raw %}
<span class="nav-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === false, indicator_good: stat_rx.state === true, indicator: true }"></span></span> <span class="nav-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === false, indicator_good: stat_rx.state === true, indicator: true }"></span></span>
<span class="nav-bar-element">Передача: <span :class="{ indicator_good: stat_tx.state === true, indicator: true }"></span></span> <span class="nav-bar-element">Передача: <span :class="{ indicator_good: stat_tx.state === true, indicator: true }"></span></span>
<span class="nav-bar-element">Тест: <span :class="{ indicator_good: (param.general.isTestInputData === true || param.general.modulatorMode === 'test'), indicator: true }"></span></span> <span class="nav-bar-element">Тест: <span :class="{ indicator_good: (param.general.isTestInputData === true || param.general.modulatorMode === 'test'), indicator: true }"></span></span>
<!-- Последнее обновление: {{ lastUpdateTime }}--> <!-- Последнее обновление: {{ lastUpdateTime }}-->
<span :class="{ value_bad: initState !== 'Успешная инициализация системы' }">{{ initState }}</span> <span :class="{ value_bad: initState !== 'Успешная инициализация системы' }">{{ initState }}</span>
{% endraw %}
<div class="tabs-header"> <div class="tabs-header">
<span style="font-weight:bold">RSCM-101</span> <span style="font-weight:bold">{{ modem_name }}</span>
<a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a> {% for tab in header_tabs %}
<a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a> <a href="#{{ tab.name }}" class="tabs-btn" @click="activeTab = '{{ tab.name }}'" :class="{{ '{' }} active: activeTab === '{{ tab.name }}' {{ '}' }}">{{ tab.description }}</a>
<a href="#qos" class="tabs-btn" @click="activeTab = 'qos'" :class="{ active: activeTab === 'qos' }">QoS</a> {% endfor %}
<a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
<a href="/logout" class="tabs-btn">Выход</a>
</div> </div>
</header> </header>
{% raw %}
<div id="content"> <div id="content">
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'"> <div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container"> <div class="settings-set-container">
@@ -116,12 +116,13 @@
<table> <table>
<tbody> <tbody>
<tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr> <tr><th>Температура ADRV</th><td>{{ stat_device.adrv }} °C</td></tr>
<tr><th>Температура ZYNQ <span hidden>ULTRASUCK</span></th><td>{{ stat_device.zynq }} °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>Температура FPGA</th><td>{{ stat_device.fpga }} °C</td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete"> <div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete">
<h2>Настройки приема/передачи</h2> <h2>Настройки приема/передачи</h2>
<div class="settings-set-container"> <div class="settings-set-container">
@@ -167,7 +168,7 @@
<h3>Параметры передачи</h3> <h3>Параметры передачи</h3>
<label> <label>
<span>Центральная частота, КГц</span> <span>Центральная частота, КГц</span>
<input v-model="param.tx.centerFreq" type="number"/> <input v-model="param.tx.centerFreq" type="number" step="0.01"/>
</label> </label>
<label> <label>
<span>Символьная скорость, Бод</span> <span>Символьная скорость, Бод</span>
@@ -219,7 +220,7 @@
<label v-show="param.dvbs2.mode === 'ccm'"> <label v-show="param.dvbs2.mode === 'ccm'">
<span>Модуляция</span> <span>Модуляция</span>
<select v-model="param.dvbs2.ccm_modulation"> <select v-model="param.dvbs2.ccm_modulation" @change="param.dvbs2.ccm_speed = correctModcodSpeed(param.dvbs2.ccm_modulation, param.dvbs2.ccm_speed)">
<option value="qpsk">QPSK</option> <option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option> <option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option> <option value="16apsk">16APSK</option>
@@ -240,7 +241,7 @@
<label v-show="param.dvbs2.mode === 'acm'"> <label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (макс. режим)</span> <span>Модуляция (макс. режим)</span>
<select v-model="param.dvbs2.acm_maxModulation"> <select v-model="param.dvbs2.acm_maxModulation" @change="param.dvbs2.acm_maxSpeed = correctModcodSpeed(param.dvbs2.acm_maxModulation, param.dvbs2.acm_maxSpeed)">
<option value="qpsk">QPSK</option> <option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option> <option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option> <option value="16apsk">16APSK</option>
@@ -255,7 +256,7 @@
</label> </label>
<label v-show="param.dvbs2.mode === 'acm'"> <label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (мин. режим)</span> <span>Модуляция (мин. режим)</span>
<select v-model="param.dvbs2.acm_minModulation"> <select v-model="param.dvbs2.acm_minModulation" @change="param.dvbs2.acm_minSpeed = correctModcodSpeed(param.dvbs2.acm_minModulation, param.dvbs2.acm_minSpeed)">
<option value="qpsk">QPSK</option> <option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option> <option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option> <option value="16apsk">16APSK</option>
@@ -316,7 +317,7 @@
</label> </label>
<label> <label>
<span>Центральная частота, кГц</span> <span>Центральная частота, кГц</span>
<input v-model="param.rx.centerFreq" type="number"/> <input v-model="param.rx.centerFreq" type="number" step="0.01"/>
</label> </label>
<label> <label>
<span>Символьная скорость, Бод</span> <span>Символьная скорость, Бод</span>
@@ -441,8 +442,7 @@
</div> </div>
<template> <template>
<div v-for="classesGroup in ['rt1', 'rt2', 'rt3', 'cd']"> <div v-for="classesGroup in ['rt1', 'rt2', 'rt3', 'cd']">
<h3>Классы {{ classesGroup.toUpperCase() }}</h3> <h3>Классы {{ classesGroup.toUpperCase() }} <button class="action-button" @click="qosAddClass(classesGroup)"> + </button></h3>
<button class="action-button" @click="qosAddClass(classesGroup)">Добавить класс {{ classesGroup.toUpperCase() }}</button>
<details v-for="(qosClass, index) in param.qos[classesGroup]" :key="index" class="settings-set-container"> <details v-for="(qosClass, index) in param.qos[classesGroup]" :key="index" class="settings-set-container">
<summary> <summary>
<span v-if="classesGroup === 'cd'">#{{ index }} CIR={{ qosClass.cir }}кбит, PIR={{ qosClass.pir }}кбит {{ qosClass.description }}</span> <span v-if="classesGroup === 'cd'">#{{ index }} CIR={{ qosClass.cir }}кбит, PIR={{ qosClass.pir }}кбит {{ qosClass.description }}</span>
@@ -592,7 +592,7 @@
<input v-model="param.debugSend.receiverIp" required type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}"> <input v-model="param.debugSend.receiverIp" required type="text" pattern="^([0-9]{1,3}\.){3}[0-9]{1,3}">
</label> </label>
<label> <label>
<span>Порт для CinC</span> <span>Порт для данных</span>
<input v-model="param.debugSend.portCinC" type="number" min="0" max="65535"> <input v-model="param.debugSend.portCinC" type="number" min="0" max="65535">
</label> </label>
<label> <label>
@@ -617,7 +617,7 @@
</tbody> </tbody>
</table> </table>
<div> <div>
<button class="dangerous-button" onclick="fetch('/api/reboot', { method: 'POST' }).then((r) => { window.location.reload(); })">Перезагрузить модем</button> <button class="dangerous-button" @click="doModemReboot()">Перезагрузить модем <span class="submit-spinner" v-show="submitStatus.modemReboot !== null"></span></button>
</div> </div>
<div> <div>
<button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button> <button class="dangerous-button" onclick="fetch('/api/resetSettings', { method: 'POST' }).then((r) => { window.location.reload(); })">Сбросить модем до заводских настроек</button>
@@ -632,349 +632,23 @@
<button class="action-button" @click="settingsUploadUpdate()">Загрузить<span class="submit-spinner" v-show="submitStatus.firmwareUpload"></span></button> <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> <button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО <span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button>
</div> </div>
<div hidden>
<p>
Эти настройки пока недоступны, но скоро разработчик это поправит. А пока смотри на крокодила, или купи разработчику банку <span style="text-decoration: line-through;">пива</span> колы для ускорения процесса)
</p>
<div><img loading="lazy" src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil"></div>
<div><video preload="auto" controls style="max-width: 100%"><source src="/vid/video_2024-11-06_15-49-35.mp4" type="video/mp4" /></video></div>
</div>
</div> </div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p> <p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div> </div>
{% endraw %}
</div> </div>
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
<script src="/js/vue.js"></script> <script src="/js/vue.js"></script>
<script> <script>
function updateHeaderHeight() {
const header = document.querySelector('header');
document.body.style.setProperty('--header-height', `${header.offsetHeight}px`);
}
window.addEventListener('load', updateHeaderHeight);
window.addEventListener('resize', updateHeaderHeight);
// const router = useRouter(); // const router = useRouter();
const availableTabs = ['monitoring', 'setup', 'qos', 'admin'] const availableTabs = {{ js_tabs_array }}
const defaultTab = availableTabs[0] const defaultTab = availableTabs[0]
function getCurrentTab() { {% include 'default-js.js' %}
const sl = window.location.hash.slice(1) {% raw %}
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/1': 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 12 // минимальная скорость
}
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' }
}
const app = new Vue({ const app = new Vue({
el: '#app', el: '#app',
data: { data: {
isCinC: false, {% endraw %}{% include 'vue-data.js' %}{% raw %}
// false - означает что статистика не отправляется, true - отправляется
submitStatus: {
rxTx: false,
cinc: false,
bucLnb: false,
qos: false,
network: false,
debugSend: false,
tcpAccel: false,
firmwareUpload: false,
firmwareUpgrade: false
},
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
}, },
methods: { methods: {
getAvailableModcods(modulation) { getAvailableModcods(modulation) {
@@ -1092,7 +766,7 @@
body: JSON.stringify(query) body: JSON.stringify(query)
}).then(async (resp) => { }).then(async (resp) => {
this.submitStatus.rxTx = false this.submitStatus.rxTx = false
this.updateCincSettings(await resp.json()) this.updateRxTxSettings(await resp.json())
}).catch((reason) => { }).catch((reason) => {
this.submitStatus.rxTx = false this.submitStatus.rxTx = false
alert(`Ошибка при применении настроек: ${reason}`) alert(`Ошибка при применении настроек: ${reason}`)
@@ -1611,17 +1285,49 @@
} }
return result return result
},
correctModcodSpeed(modulation, speed) {
const mod = modulation.toLowerCase()
const available = {
"qpsk": ['1/4', '1/3', '2/5', '1/2', '3/5', '2/3', '3/4', '4/5', '5/6', '8/9', '9/10'],
"8psk": ['2/3', '3/4', '5/6', '8/9', '9/10'],
"16apsk": ['2/3', '3/4', '4/5', '5/6', '8/9', '9/10'],
"32apsk": ['3/4', '4/5', '5/6', '8/9', '9/10']
}
if (mod in available) {
if (available[mod].indexOf(speed) >= 0) {
return speed
}
return available[mod][0]
}
return ""
},
doModemReboot() {
if (this.submitStatus.modemReboot !== null) {
return
}
this.submitStatus.modemReboot = 30
fetch('/api/reboot', { method: 'POST' }).then((r) => {})
} }
}, },
mounted() { mounted() {
const doFetchStatistics = async () => { const doFetchStatistics = async () => {
try { if (this.submitStatus.modemReboot !== null) {
let d = await fetch("/api/get/statistics") this.initState = `Перезагрузка модема... Осталось ${this.submitStatus.modemReboot} сек`
this.updateStatistics(await d.json()) this.submitStatus.modemReboot--
} catch (e) { if (this.submitStatus.modemReboot <= 0) {
this.initState = "Ошибка обновления статистики" window.location.reload()
}
} else {
try {
let d = await fetch("/api/get/statistics")
this.updateStatistics(await d.json())
} catch (e) {
this.initState = "Ошибка обновления статистики"
}
} }
setTimeout(() => { setTimeout(() => {
doFetchStatistics() doFetchStatistics()
}, 1000) }, 1000)
@@ -1629,7 +1335,8 @@
const doFetchAbout = async () => { const doFetchAbout = async () => {
try { try {
let d = await fetch("/api/get/aboutFirmware") const fr = await fetch("/api/get/aboutFirmware")
const d = await fr.json()
this.about.firmwareVersion = d["fw.version"] this.about.firmwareVersion = d["fw.version"]
this.about.modemUid = d["fw.modemId"] this.about.modemUid = d["fw.modemId"]
this.about.modemSn = d["fw.modemSn"] this.about.modemSn = d["fw.modemSn"]
@@ -1648,6 +1355,7 @@
document.getElementById("app").removeAttribute("hidden") document.getElementById("app").removeAttribute("hidden")
} }
}) })
//{% endraw %}
</script> </script>
</body> </body>
</html> </html>

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; std::unique_ptr<api_driver::ApiDriver> api;
http::auth::AuthProvider auth{}; http::auth::AuthProvider auth{};
bool upgradeOrRebootRunning = false;
static void onUploadFirmware(const http::server::Request& req) { static void onUploadFirmware(const http::server::Request& req) {
std::ofstream f("/tmp/firmware.zip", std::ios::binary); std::ofstream f("/tmp/firmware.zip", std::ios::binary);
@@ -104,35 +106,37 @@ class ServerResources {
} }
public: public:
static constexpr const char* INDEX_HTML = "static/main.html"; #if defined(MODEM_IS_TDMA)
static constexpr const char* LOGIN_HTML = "static/login.html"; static constexpr const char* INDEX_HTML = "/main-tdma.html";
#elif defined(MODEM_IS_SCPC)
static constexpr const char* INDEX_HTML = "/main-scpc.html";
#else
#error "Modem type not defined!"
#endif
static constexpr const char* LOGIN_HTML = "/login.html";
// картинки, их даже можно кешировать // картинки, их даже можно кешировать
static constexpr const char* FAVICON_ICO = "static/favicon.png"; static constexpr const char* FAVICON_ICO = "/favicon.ico";
static constexpr const char* KROKODIL_GIF = "static/krokodil.gif"; static constexpr const char* VUE_JS = "/js/vue.js"; // это тоже можно кешировать
static constexpr const char* VUE_JS = "static/js/vue.js"; // это тоже можно кешировать
// а эти стили нельзя кешировать в отладочной версии // а эти стили нельзя кешировать в отладочной версии
static constexpr const char* STYLE_CSS = "static/style.css"; static constexpr const char* STYLE_CSS = "/style.css";
static constexpr const char* FIELDS_CSS = "static/fields.css"; static constexpr const char* FIELDS_CSS = "/fields.css";
static constexpr const char* KB_MP4 = "static/video_2024-11-06_15-49-35.mp4"; static constexpr const char* INTERNET_JPG = "/internet.jpg";
ServerResources(const ServerResources&) = delete; ServerResources(const ServerResources&) = delete;
ServerResources(): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) { explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
api->startDaemon(); api->startDaemon();
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER)); auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER));
sf->registerFile(FAVICON_ICO, mime_types::image_png, true); sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
sf->registerFile(KROKODIL_GIF, mime_types::image_gif, true); sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true);
sf->registerFile(VUE_JS, mime_types::javascript, true); sf->registerFile(staticFilesPath + STYLE_CSS, STYLE_CSS, mime_types::text_css, true);
sf->registerFile(KB_MP4, mime_types::video_mp4, 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(STYLE_CSS, mime_types::text_css, true); sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true);
sf->registerFile(FIELDS_CSS, mime_types::text_css, true); sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true);
sf->registerFile(INDEX_HTML, mime_types::text_html, false);
sf->registerFile(LOGIN_HTML, mime_types::text_html, true);
} }
void registerResources(http::server::Server& s) { void registerResources(http::server::Server& s) {
@@ -186,12 +190,11 @@ public:
} }
})); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/favicon.ico", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); })); s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FAVICON_ICO, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/images/krokodil_vzryvaetsya_hd.gif", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KROKODIL_GIF, rep); })); s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(STYLE_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/style.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); })); s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FIELDS_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/fields.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); })); s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(VUE_JS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/js/vue.js", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); })); s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(INTERNET_JPG, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(INTERNET_JPG, rep); }));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/vid/video_2024-11-06_15-49-35.mp4", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KB_MP4, rep); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) {
if (req.method != "GET") { if (req.method != "GET") {
@@ -203,6 +206,8 @@ public:
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();
result += R"(,"sysinfo":)";
result += api->loadSysInfo();
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());
})); }));
@@ -301,7 +306,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());
} }
})); }));
#ifdef MODEM_IS_SCPC
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { 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);
@@ -329,7 +334,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());
} }
})); }));
#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);
@@ -414,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") { if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
} }
@@ -423,6 +428,7 @@ public:
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;
system(REBOOT_COMMAND); system(REBOOT_COMMAND);
})); }));
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) { s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
@@ -438,10 +444,11 @@ public:
system(REBOOT_COMMAND); 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") { if (req.method != "PUT") {
http::server::stockReply(http::server::bad_request, rep); http::server::stockReply(http::server::bad_request, rep);
} }
this->upgradeOrRebootRunning = true;
onUploadFirmware(req); onUploadFirmware(req);
rep.status = http::server::ok; rep.status = http::server::ok;
@@ -453,12 +460,14 @@ public:
result += http::utils::sha256(req.payload.data(), req.payload.size()); result += http::utils::sha256(req.payload.data(), req.payload.size());
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());
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) { 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);
} }
this->upgradeOrRebootRunning = true;
doTerminalUpgrade(); doTerminalUpgrade();
rep.status = http::server::ok; rep.status = http::server::ok;
rep.headers.clear(); rep.headers.clear();
@@ -466,6 +475,19 @@ public:
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());
})); }));
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; ~ServerResources() = default;
@@ -479,9 +501,9 @@ int main(int argc, char *argv[]) {
if (argc != 4 && argc != 5) { if (argc != 4 && argc != 5) {
std::cerr << "Usage: http_server <ssl|nossl> <address> <port> [static files directory]\n"; std::cerr << "Usage: http_server <ssl|nossl> <address> <port> [static files directory]\n";
std::cerr << " For IPv4, try:\n"; std::cerr << " For IPv4, try:\n";
std::cerr << " receiver nossl 0.0.0.0 80\n"; std::cerr << " receiver nossl 0.0.0.0 80 .\n";
std::cerr << " For IPv6, try:\n"; std::cerr << " For IPv6, try:\n";
std::cerr << " receiver nossl 0::0 80\n"; std::cerr << " receiver nossl 0::0 80 .\n";
return 1; return 1;
} }
@@ -502,7 +524,9 @@ int main(int argc, char *argv[]) {
BOOST_LOG_TRIVIAL(info) << "Generated new secret key " << http::auth::jwt::secretKey; BOOST_LOG_TRIVIAL(info) << "Generated new secret key " << http::auth::jwt::secretKey;
#endif #endif
ServerResources resources; const std::string staticFilesPath = (argc == 5 ? argv[4]: ".");
BOOST_LOG_TRIVIAL(info) << "Use static files path: " << staticFilesPath << "/";
ServerResources resources(staticFilesPath);
// Initialise the server. // Initialise the server.
std::unique_ptr<http::server::Server> s; std::unique_ptr<http::server::Server> s;

View File

@@ -22,37 +22,42 @@ static void loadFile(const std::string& path, std::vector<char>& content) {
http::resource::BasicResource::BasicResource(std::string path): path(std::move(path)) {} http::resource::BasicResource::BasicResource(std::string path): path(std::move(path)) {}
http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache): path(std::move(path)), type(type), allowCache(allowCache) { http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(const std::string& path, std::string webPath, server::mime_types::Mime type, bool allowCache):
webPath(std::move(webPath)),
#ifdef USE_DEBUG
fsPath(path),
#endif
type(type), allowCache(allowCache) {
#ifdef USE_DEBUG #ifdef USE_DEBUG
if (allowCache) { if (allowCache) {
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path; BOOST_LOG_TRIVIAL(info) << "Load static file " << this->webPath;
loadFile(this->path, this->content); loadFile(path, this->content);
} else { } else {
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->path; BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->webPath;
} }
#else #else
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path; BOOST_LOG_TRIVIAL(info) << "Load static file " << path;
loadFile(this->path, this->content); loadFile(path, this->content);
#endif #endif
} }
http::resource::StaticFileFactory::StaticFileDef::~StaticFileDef() = default; http::resource::StaticFileFactory::StaticFileDef::~StaticFileDef() = default;
http::resource::StaticFileFactory::StaticFileFactory() = default; http::resource::StaticFileFactory::StaticFileFactory() = default;
void http::resource::StaticFileFactory::registerFile(const std::string &path, server::mime_types::Mime type, bool allowCache) { void http::resource::StaticFileFactory::registerFile(const std::string &path, const std::string &webPath, server::mime_types::Mime type, bool allowCache) {
this->files.emplace_back(path, type, allowCache); this->files.emplace_back(path, webPath, type, allowCache);
} }
void http::resource::StaticFileFactory::serve(const std::string &path, server::Reply &rep) { void http::resource::StaticFileFactory::serve(const std::string &path, server::Reply &rep) {
for (auto& f: this->files) { for (auto& f: this->files) {
if (f.path == path) { if (f.webPath == path) {
#ifdef USE_DEBUG #ifdef USE_DEBUG
if (f.allowCache) { if (f.allowCache) {
rep.content.clear(); rep.content.clear();
rep.content.insert(rep.content.end(), f.content.begin(), f.content.end()); rep.content.insert(rep.content.end(), f.content.begin(), f.content.end());
} else { } else {
BOOST_LOG_TRIVIAL(debug) << "Reload file " << path << " (http path: " << path << ")"; BOOST_LOG_TRIVIAL(debug) << "Reload file " << path << " (http path: " << path << ")";
loadFile(f.path, rep.content); loadFile(f.fsPath, rep.content);
} }
#else #else
rep.content.clear(); rep.content.clear();

View File

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

View File

@@ -8,8 +8,9 @@
#include <boost/thread.hpp> #include <boost/thread.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/property_tree/json_parser.hpp> #include <boost/property_tree/json_parser.hpp>
#include <sys/sysinfo.h>
#include "../dependencies/control_system/common/protocol_commands.h"
typedef boost::property_tree::ptree::path_type json_path; typedef boost::property_tree::ptree::path_type json_path;
@@ -69,13 +70,13 @@ public:
std::string managementIp, managementGateway, mode, dataIp; std::string managementIp, managementGateway, mode, dataIp;
unsigned int dataMtu = 1500; unsigned int dataMtu = 1500;
TerminalNetworkSettings() { this->reset(); } TerminalNetworkSettings() = default;
TerminalNetworkSettings(const TerminalNetworkSettings& src) = default; TerminalNetworkSettings(const TerminalNetworkSettings& src) = default;
~TerminalNetworkSettings() = default; ~TerminalNetworkSettings() = default;
TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default; TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default;
void reset() { void loadDefaults() {
managementIp = "0.0.0.0/0"; managementIp = "0.0.0.0/0";
managementGateway = ""; managementGateway = "";
mode = "l2"; mode = "l2";
@@ -95,48 +96,66 @@ public:
TerminalFirmwareVersion& operator= (const TerminalFirmwareVersion& src) = default; TerminalFirmwareVersion& operator= (const TerminalFirmwareVersion& src) = default;
}; };
static std::ostream& operator<<(std::ostream& out, CP_Result result) {
switch (result) {
case OK: out << "OK"; break;
case TIMEOUT: out << "TIMEOUT"; break;
case ERROR: out << "ERROR"; break;
case ABORT: out << "ABORT"; break;
case BUSY: out << "BUSY"; break;
default:
out << static_cast<int>(result);
}
return out;
}
/** /**
* Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки * Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки
*/ */
class api_driver::TerminalApiDaemon { class api_driver::TerminalApiDaemon {
private: private:
CP_Result lastCpError = OK;
void updateFirmwareSettings() { void logCpApiError(const char* desc, CP_Result err) {
std::string version, chip_id, sn, mac0, mac1; if (err != OK) {
std::lock_guard lock(this->cpApiMutex); BOOST_LOG_TRIVIAL(error) << "CP API error in " << desc << ": " << err;
this->lastCpError = err;
CP_GetNetwork(sid, "version", &version);
CP_GetNetwork(sid, "chip_id", &chip_id);
rtrim(chip_id);
CP_GetNetwork(sid, "serial", &sn);
CP_GetNetwork(sid, "mac_eth0", &mac0);
CP_GetNetwork(sid, "mac_eth1", &mac1);
{
std::lock_guard lock2(this->firmwareMutex);
this->firmware.version = version;
this->firmware.modemId = chip_id;
this->firmware.modemSn = sn;
this->firmware.macMang = mac0;
this->firmware.macData = mac1;
} }
} }
void updateStatistics() { void updateState() {
modulator_state modulator{}; modulator_state modulator{};
demodulator_state demodulator{}; demodulator_state demodulator{};
device_state device{}; device_state device{};
#ifdef MODEM_IS_SCPC
CinC_state cinc{};
#endif
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_GetModulatorState(sid, modulator); logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetModulatorState()", CP_GetModulatorState(sid, modulator));
CP_GetDemodulatorState(sid, demodulator); logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDemodulatorState()", CP_GetDemodulatorState(sid, demodulator));
CP_GetDeviceState(sid, device); logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDeviceState()", CP_GetDeviceState(sid, device));
#ifdef MODEM_IS_TDMA
std::string tmpDevState;
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDmaDebug(status_init)", CP_GetDmaDebug(sid, "status_init", &tmpDevState));
#endif
#ifdef MODEM_IS_SCPC
bool isCinC = getIsCinC();
if (isCinC) {
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetCinCState()", CP_GetCinCState(sid, cinc));
}
#endif
{ {
std::lock_guard lock2(this->stateMutex); std::lock_guard lock2(this->stateMutex);
this->modState = modulator; this->modState = modulator;
this->demodState = demodulator; this->demodState = demodulator;
this->devState = device; this->devState = device;
#ifdef MODEM_IS_TDMA
this->deviceInitState = tmpDevState;
#endif
#ifdef MODEM_IS_SCPC
this->cincState = cinc;
#endif
} }
} }
@@ -145,23 +164,29 @@ private:
// uint32_t modulatorModcod; // uint32_t modulatorModcod;
// CP_GetModulatorParams(sid, "modcod", &modulatorModcod); // CP_GetModulatorParams(sid, "modcod", &modulatorModcod);
demodulator_settings demod{}; demodulator_settings demod{};
#ifdef MODEM_IS_SCPC
ACM_parameters_serv_ acm{}; ACM_parameters_serv_ acm{};
DPDI_parmeters dpdi{}; DPDI_parmeters dpdi{};
#endif
buc_lnb_settings bucLnb{}; buc_lnb_settings bucLnb{};
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_GetModulatorSettings(sid, mod); logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetModulatorSettings()", CP_GetModulatorSettings(sid, mod));
CP_GetDemodulatorSettings(sid, demod); logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetDemodulatorSettings()", CP_GetDemodulatorSettings(sid, demod));
CP_GetAcmParams(sid, &acm); #ifdef MODEM_IS_SCPC
CP_GetDpdiParams(sid, &dpdi); logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetAcmParams()", CP_GetAcmParams(sid, &acm));
CP_GetBUC_LNB_settings(sid, bucLnb); logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetDpdiParams()", CP_GetDpdiParams(sid, &dpdi));
#endif
logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetBUC_LNB_settings()", CP_GetBUC_LNB_settings(sid, bucLnb));
{ {
std::lock_guard lock2(this->settingsMutex); std::lock_guard lock2(this->settingsMutex);
this->modSettings = mod; this->modSettings = mod;
this->demodSettings = demod; this->demodSettings = demod;
#ifdef MODEM_IS_SCPC
this->acmSettings = acm; this->acmSettings = acm;
this->dpdiSettings = dpdi; this->dpdiSettings = dpdi;
#endif
this->bucLnbSettings = bucLnb; this->bucLnbSettings = bucLnb;
} }
} }
@@ -170,15 +195,16 @@ private:
TerminalNetworkSettings s; TerminalNetworkSettings s;
std::string tmp; std::string tmp;
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_GetNetwork(sid, "addr", &tmp); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(addr)", CP_GetNetwork(sid, "addr", &tmp));
s.managementIp = tmp + "/"; s.managementIp = tmp + "/";
tmp.clear(); CP_GetNetwork(sid, "mask", &tmp); tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(mask)", CP_GetNetwork(sid, "mask", &tmp));
s.managementIp += std::to_string(calculateSubnetMask(tmp)); s.managementIp += std::to_string(calculateSubnetMask(tmp));
tmp.clear(); CP_GetNetwork(sid, "gateway", &s.managementGateway); s.managementGateway = tmp; tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(gateway)", CP_GetNetwork(sid, "gateway", &s.managementGateway)); s.managementGateway = tmp;
tmp.clear(); CP_GetNetwork(sid, "mode", &tmp); tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(mode)", CP_GetNetwork(sid, "mode", &tmp));
if (tmp == "tun") { if (tmp == "tun") {
s.mode = "l3"; s.mode = "l3";
CP_GetNetwork(sid, "addr_data", &s.dataIp); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(addr_data)", CP_GetNetwork(sid, "addr_data", &s.dataIp));
s.dataIp += "/24";
} else { } else {
s.mode = "l2"; s.mode = "l2";
s.dataIp = "0.0.0.0/24"; s.dataIp = "0.0.0.0/24";
@@ -194,7 +220,7 @@ private:
void updateQos() { void updateQos() {
bool tmp1; std::string tmp2; bool tmp1; std::string tmp2;
std::scoped_lock lock{this->cpApiMutex}; std::scoped_lock lock{this->cpApiMutex};
CP_GetQoSSettings(this->sid, tmp2, tmp1); logCpApiError("api_driver::TerminalApiDaemon::updateQos()->CP_GetQoSSettings()", CP_GetQoSSettings(this->sid, tmp2, tmp1));
{ {
std::lock_guard lock2(this->qosSettingsMutex); std::lock_guard lock2(this->qosSettingsMutex);
this->qosEnabled = tmp1; this->qosEnabled = tmp1;
@@ -202,9 +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() { void run() {
// это демон, который в бесконечном цикле опрашивает API // это демон, который в бесконечном цикле опрашивает API
updateFirmwareSettings(); this->connectToApi();
struct IntervalUpdate_t { struct IntervalUpdate_t {
int64_t lastUpdate; int64_t lastUpdate;
@@ -230,10 +299,10 @@ private:
// обновление статистики // обновление статистики
{.lastUpdate = 0, .periodMs = CACHE_STATISTICS_UPDATE_MS, .callback = [this]() { {.lastUpdate = 0, .periodMs = CACHE_STATISTICS_UPDATE_MS, .callback = [this]() {
try { try {
this->updateStatistics(); this->updateState();
BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateStatistics(): success update!"; BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateState(): success update!";
} catch (std::exception& e) { } catch (std::exception& e) {
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateStatistics(): " << e.what(); BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateState(): " << e.what();
} }
}}, }},
// обновление кеша настроек // обновление кеша настроек
@@ -266,6 +335,12 @@ private:
}; };
while (true) { 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; // минута по-умолчанию int64_t sleepTime = 60000; // минута по-умолчанию
auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count(); auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
for (auto& u: updaters) { for (auto& u: updaters) {
@@ -284,19 +359,22 @@ private:
} }
} }
std::shared_mutex firmwareMutex;
TerminalFirmwareVersion firmware;
std::shared_mutex stateMutex; std::shared_mutex stateMutex;
modulator_state modState{}; modulator_state modState{};
demodulator_state demodState{}; demodulator_state demodState{};
device_state devState{}; device_state devState{};
std::string deviceInitState;
#ifdef MODEM_IS_SCPC
CinC_state cincState{};
#endif
std::shared_mutex settingsMutex; std::shared_mutex settingsMutex;
modulator_settings modSettings{}; modulator_settings modSettings{};
demodulator_settings demodSettings{}; demodulator_settings demodSettings{};
#ifdef MODEM_IS_SCPC
ACM_parameters_serv_ acmSettings{}; ACM_parameters_serv_ acmSettings{};
DPDI_parmeters dpdiSettings{}; DPDI_parmeters dpdiSettings{};
#endif
buc_lnb_settings bucLnbSettings{}; buc_lnb_settings bucLnbSettings{};
std::shared_mutex networkSettingsMutex; std::shared_mutex networkSettingsMutex;
@@ -306,22 +384,48 @@ private:
bool qosEnabled; bool qosEnabled;
std::string qosClassesJson; std::string qosClassesJson;
std::shared_mutex firmwareMutex;
TerminalFirmwareVersion firmware;
public: public:
std::mutex cpApiMutex; std::mutex cpApiMutex;
TSID sid; TSID sid;
boost::thread daemon; boost::thread daemon;
explicit TerminalApiDaemon(TSID sid): sid(sid), daemon([this]() { this->run(); }), qosEnabled(false) { explicit TerminalApiDaemon(): deviceInitState("Not connected to API"), qosEnabled(false), sid(0), daemon([this]() { this->run(); }) {
this->qosClassesJson = DEFAULT_QOS_CLASSES; this->qosClassesJson = DEFAULT_QOS_CLASSES;
} }
std::string getDeviceInitState() {
std::shared_lock lock(this->stateMutex);
return this->deviceInitState;
}
#ifdef MODEM_IS_SCPC
/**
* Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
* @param mod статистика модулятра
* @param demod статистика демодулятора
* @param dev статистика устройства (температуры)
* @param cinc статистика CinC (актуальна только для режима CinC, но в любом случае будет перезаписана)
*/
void getState(modulator_state* mod, demodulator_state* demod, device_state* dev, CinC_state* cinc) {
if (mod != nullptr || demod != nullptr || dev != nullptr) {
std::shared_lock lock(this->stateMutex);
if (mod) { *mod = this->modState; }
if (demod) { *demod = this->demodState; }
if (dev) { *dev = this->devState; }
if (cinc) { *cinc = this->cincState; }
}
}
#else
/** /**
* Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет. * Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
* @param mod статистика модулятра * @param mod статистика модулятра
* @param demod статистика демодулятора * @param demod статистика демодулятора
* @param dev статистика устройства (температуры) * @param dev статистика устройства (температуры)
*/ */
void getStatistics(modulator_state* mod, demodulator_state* demod, device_state* dev) { void getState(modulator_state* mod, demodulator_state* demod, device_state* dev) {
if (mod != nullptr || demod != nullptr || dev != nullptr) { if (mod != nullptr || demod != nullptr || dev != nullptr) {
std::shared_lock lock(this->stateMutex); std::shared_lock lock(this->stateMutex);
if (mod) { *mod = this->modState; } if (mod) { *mod = this->modState; }
@@ -329,10 +433,12 @@ public:
if (dev) { *dev = this->devState; } if (dev) { *dev = this->devState; }
} }
} }
#endif
/** /**
* Получение настроек, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет. * Получение настроек, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
*/ */
#ifdef MODEM_IS_SCPC
void getSettings(modulator_settings* mod, demodulator_settings* demod, ACM_parameters_serv_* acm, DPDI_parmeters* dpdi, buc_lnb_settings* bucLnb) { void getSettings(modulator_settings* mod, demodulator_settings* demod, ACM_parameters_serv_* acm, DPDI_parmeters* dpdi, buc_lnb_settings* bucLnb) {
if (mod || demod || acm || dpdi || bucLnb) { if (mod || demod || acm || dpdi || bucLnb) {
std::shared_lock lock(this->settingsMutex); std::shared_lock lock(this->settingsMutex);
@@ -343,6 +449,23 @@ public:
if (bucLnb) { *bucLnb = this->bucLnbSettings; } if (bucLnb) { *bucLnb = this->bucLnbSettings; }
} }
} }
#else
void getSettings(modulator_settings* mod, demodulator_settings* demod, buc_lnb_settings* bucLnb) {
if (mod || demod || bucLnb) {
std::shared_lock lock(this->settingsMutex);
if (mod) { *mod = this->modSettings; }
if (demod) { *demod = this->demodSettings; }
if (bucLnb) { *bucLnb = this->bucLnbSettings; }
}
}
#endif
#ifdef MODEM_IS_SCPC
bool getIsCinC() {
std::shared_lock lock(this->settingsMutex);
return modSettings.is_cinc;
}
#endif
void getNetworkSettings(TerminalNetworkSettings& dest) { void getNetworkSettings(TerminalNetworkSettings& dest) {
std::shared_lock lock(this->networkSettingsMutex); std::shared_lock lock(this->networkSettingsMutex);
@@ -350,26 +473,22 @@ public:
} }
void getQosSettings(bool& isEnabled, std::string& json) { void getQosSettings(bool& isEnabled, std::string& json) {
std::shared_lock lock(this->settingsMutex); std::shared_lock lock(this->qosSettingsMutex);
isEnabled = this->qosEnabled; isEnabled = this->qosEnabled;
json = this->qosClassesJson; json = this->qosClassesJson;
} }
void getFirmwareVersion(TerminalFirmwareVersion& fw) { #ifdef MODEM_IS_SCPC
std::shared_lock lock(this->settingsMutex);
fw = this->firmware;
}
void setSettingsRxTx(modulator_settings& mod, demodulator_settings& demod, ACM_parameters_serv_& acm, bool readback = true) { void setSettingsRxTx(modulator_settings& mod, demodulator_settings& demod, ACM_parameters_serv_& acm, bool readback = true) {
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetModulatorSettings(this->sid, mod); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetModulatorSettings()", CP_SetModulatorSettings(this->sid, mod));
CP_SetDemodulatorSettings(this->sid, demod); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDemodulatorSettings()", CP_SetDemodulatorSettings(this->sid, demod));
CP_SetAcmParams(this->sid, acm); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetAcmParams()", CP_SetAcmParams(this->sid, acm));
if (readback) { if (readback) {
CP_GetModulatorSettings(this->sid, mod); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetModulatorSettings()", CP_GetModulatorSettings(this->sid, mod));
CP_GetDemodulatorSettings(this->sid, demod); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetDemodulatorSettings()", CP_GetDemodulatorSettings(this->sid, demod));
CP_GetAcmParams(this->sid, &acm); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetAcmParams()", CP_GetAcmParams(this->sid, &acm));
{ {
std::lock_guard lock2{this->settingsMutex}; std::lock_guard lock2{this->settingsMutex};
this->modSettings = mod; this->modSettings = mod;
@@ -377,51 +496,71 @@ public:
this->acmSettings = acm; this->acmSettings = acm;
} }
} }
CP_SetDmaDebug(sid, "save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
} }
#else
void setSettingsRxTx(modulator_settings& mod, demodulator_settings& demod, bool readback = true) {
std::lock_guard lock(this->cpApiMutex);
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetModulatorSettings()", CP_SetModulatorSettings(this->sid, mod));
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDemodulatorSettings()", CP_SetDemodulatorSettings(this->sid, demod));
if (readback) {
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetModulatorSettings()", CP_GetModulatorSettings(this->sid, mod));
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetDemodulatorSettings()", CP_GetDemodulatorSettings(this->sid, demod));
{
std::lock_guard lock2{this->settingsMutex};
this->modSettings = mod;
this->demodSettings = demod;
}
}
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
}
#endif
#ifdef MODEM_IS_SCPC
void setSettingsCinc(DPDI_parmeters& s, bool readback = true) { void setSettingsCinc(DPDI_parmeters& s, bool readback = true) {
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetDpdiParams(sid, s); logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_SetDpdiParams()", CP_SetDpdiParams(sid, s));
if (readback) { if (readback) {
CP_GetDpdiParams(this->sid, &s); logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_GetDpdiParams()", CP_GetDpdiParams(this->sid, &s));
{ {
std::lock_guard lock2{this->settingsMutex}; std::lock_guard lock2{this->settingsMutex};
this->dpdiSettings = s; this->dpdiSettings = s;
} }
} }
CP_SetDmaDebug(sid, "save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
} }
#endif
void setSettingsBucLnb(buc_lnb_settings& bucLnb, bool readback = true) { void setSettingsBucLnb(buc_lnb_settings& bucLnb, bool readback = true) {
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetBUC_LNB_settings(this->sid, bucLnb); logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_SetBUC_LNB_settings()", CP_SetBUC_LNB_settings(this->sid, bucLnb));
if (readback) { if (readback) {
CP_GetBUC_LNB_settings(this->sid, bucLnb); logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_GetBUC_LNB_settings()", CP_GetBUC_LNB_settings(this->sid, bucLnb));
{ {
std::lock_guard lock2{this->settingsMutex}; std::lock_guard lock2{this->settingsMutex};
this->bucLnbSettings = bucLnb; this->bucLnbSettings = bucLnb;
} }
} }
CP_SetDmaDebug(sid, "save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
} }
void setQosSettings(bool enabled, const std::string& str, bool readback = true) { void setQosSettings(bool enabled, const std::string& str, bool readback = true) {
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetQoSSettings(this->sid, str, enabled); logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetQoSSettings()", CP_SetQoSSettings(this->sid, str, enabled));
if (readback) { if (readback) {
bool tmp1; std::string tmp2; bool tmp1; std::string tmp2;
CP_GetQoSSettings(this->sid, tmp2, tmp1); logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_GetQoSSettings()", CP_GetQoSSettings(this->sid, tmp2, tmp1));
{ {
std::lock_guard lock2(this->qosSettingsMutex); std::lock_guard lock2(this->qosSettingsMutex);
this->qosEnabled = tmp1; this->qosEnabled = tmp1;
this->qosClassesJson = tmp2.empty() ? DEFAULT_QOS_CLASSES : tmp2; this->qosClassesJson = tmp2.empty() ? DEFAULT_QOS_CLASSES : tmp2;
} }
} }
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) { void setNetworkSettings(TerminalNetworkSettings& s, bool readback = true) {
@@ -433,30 +572,30 @@ public:
else { throw std::runtime_error("invalid mode"); } else { throw std::runtime_error("invalid mode"); }
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", ""); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetNetwork(sid, "mode", isL2 ? "tap" : "tun"); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(mode)", CP_SetNetwork(sid, "mode", isL2 ? "tap" : "tun"));
CP_SetNetwork(sid, "addr", mang.first.c_str()); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(addr)", CP_SetNetwork(sid, "addr", mang.first.c_str()));
CP_SetNetwork(sid, "mask", mang.second.c_str()); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(mask)", CP_SetNetwork(sid, "mask", mang.second.c_str()));
CP_SetNetwork(sid, "gateway", s.managementGateway.c_str()); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(gateway)", CP_SetNetwork(sid, "gateway", s.managementGateway.c_str()));
if (!isL2) { if (!isL2) {
CP_SetNetwork(sid, "data_addr", data.first.c_str()); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(data_addr)", CP_SetNetwork(sid, "data_addr", data.first.c_str()));
// TODO маска не устанавливается, потому что в API этого нет // TODO маска не устанавливается, потому что в API этого нет
} }
// TODO MTU не устанавливается, потому что в API этого нет // TODO MTU не устанавливается, потому что в API этого нет
if (readback) { if (readback) {
std::string tmp; std::string tmp;
s.reset(); s.loadDefaults();
s.managementIp.clear(); s.managementIp.clear();
CP_GetNetwork(sid, "addr", &s.managementIp); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(addr)", CP_GetNetwork(sid, "addr", &s.managementIp));
CP_GetNetwork(sid, "mask", &tmp); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(mask)", CP_GetNetwork(sid, "mask", &tmp));
s.managementIp += "/"; s.managementIp += "/";
s.managementIp += std::to_string(calculateSubnetMask(tmp)); s.managementIp += std::to_string(calculateSubnetMask(tmp));
CP_GetNetwork(sid, "gateway", &s.managementGateway); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(gateway)", CP_GetNetwork(sid, "gateway", &s.managementGateway));
tmp.clear(); CP_GetNetwork(sid, "mode", &tmp); tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(mode)", CP_GetNetwork(sid, "mode", &tmp));
if (tmp == "tun") { if (tmp == "tun") {
s.mode = "l3"; s.mode = "l3";
CP_GetNetwork(sid, "addr_data", &s.dataIp); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(addr_data)", CP_GetNetwork(sid, "addr_data", &s.dataIp));
} else { } else {
s.mode = "l2"; s.mode = "l2";
s.dataIp = "0.0.0.0/24"; s.dataIp = "0.0.0.0/24";
@@ -467,20 +606,25 @@ public:
this->networkSettings = s; this->networkSettings = s;
} }
} }
CP_SetDmaDebug(sid, "save_config", ""); 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() { void resetPacketStatistics() {
std::string tmp; std::string tmp;
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_GetDmaDebug(sid, "reset_cnt_rx", &tmp); logCpApiError("api_driver::TerminalApiDaemon::resetPacketStatistics()->CP_GetDmaDebug(reset_cnt_rx)", CP_GetDmaDebug(sid, "reset_cnt_rx", &tmp));
} }
void resetDefaultSettings() { void resetDefaultSettings() {
std::lock_guard lock(this->cpApiMutex); std::lock_guard lock(this->cpApiMutex);
CP_SetDmaDebug(sid, "begin_save_config", " "); logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
CP_SetDmaDebug(sid, "default_params", ""); logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(default_params)", CP_SetDmaDebug(sid, "default_params", ""));
CP_SetDmaDebug(sid, "save_config", " "); logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
} }
~TerminalApiDaemon() { ~TerminalApiDaemon() {
@@ -494,14 +638,11 @@ public:
}; };
api_driver::ApiDriver::ApiDriver() { api_driver::ApiDriver::ApiDriver() = default;
CP_Login("admin", "pass", &sid, &access);
CP_GetDmaDebug(sid, "status_init", &deviceInitState);
}
void api_driver::ApiDriver::startDaemon() { void api_driver::ApiDriver::startDaemon() {
if (daemon == nullptr) { if (daemon == nullptr) {
daemon = std::make_unique<TerminalApiDaemon>(this->sid); daemon = std::make_unique<TerminalApiDaemon>();
BOOST_LOG_TRIVIAL(info) << "api_driver::ApiDriver::startDaemon(): API daemon succes started!"; BOOST_LOG_TRIVIAL(info) << "api_driver::ApiDriver::startDaemon(): API daemon succes started!";
} }
} }
@@ -527,7 +668,7 @@ void writeDouble(std::ostream& out, double value, int prec = 2) {
out << std::fixed << std::setprecision(prec) << value; out << std::fixed << std::setprecision(prec) << value;
} }
} }
#ifdef MODEM_IS_SCPC
double translateCoordinates(uint8_t deg, uint8_t min) { double translateCoordinates(uint8_t deg, uint8_t min) {
return static_cast<double>(deg) + static_cast<double>(min) / 60; return static_cast<double>(deg) + static_cast<double>(min) / 60;
} }
@@ -538,6 +679,7 @@ std::tuple<uint8_t, uint8_t> translateCoordinates(double abs) {
auto min = static_cast<uint8_t>(min_double); auto min = static_cast<uint8_t>(min_double);
return std::make_tuple(deg, min); return std::make_tuple(deg, min);
} }
#endif
std::string api_driver::ApiDriver::loadTerminalState() const { std::string api_driver::ApiDriver::loadTerminalState() const {
@@ -546,33 +688,42 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
} }
std::stringstream result; std::stringstream result;
result << "{\n\"initState\":" << buildEscapedString(this->deviceInitState); result << "{\n\"initState\":" << buildEscapedString(daemon->getDeviceInitState());
modulator_state modulator{}; modulator_state modulator{};
demodulator_state demodulator{}; demodulator_state demodulator{};
device_state device{}; device_state device{};
daemon->getStatistics(&modulator, &demodulator, &device); #ifdef MODEM_IS_SCPC
const bool isCinC = this->getIsCinC(); CinC_state cinc{};
daemon->getState(&modulator, &demodulator, &device, &cinc);
const bool isCinC = this->daemon->getIsCinC();
#else
daemon->getState(&modulator, &demodulator, &device);
#endif
#ifdef MODEM_IS_SCPC
result << ",\"isCinC\":" << boolAsStr(isCinC); result << ",\"isCinC\":" << boolAsStr(isCinC);
#endif
// формируем структуру для TX // формируем структуру для TX
result << ",\n\"tx.state\":" << boolAsStr(modulator.is_tx_on); result << ",\n\"tx.state\":" << boolAsStr(modulator.is_tx_on);
result << ",\"tx.modcod\":" << modulator.modcod; result << ",\"tx.modcod\":" << modulator.modcod;
#ifdef MODEM_IS_SCPC
result << ",\"tx.snr\":"; writeDouble(result, modulator.snr_remote); result << ",\"tx.snr\":"; writeDouble(result, modulator.snr_remote);
if (modulator.is_short) { if (modulator.is_short) { result << R"(,"tx.frameSizeNormal":false)"; }
result << R"(,"tx.frameSizeNormal":false)"; else { result << R"(,"tx.frameSizeNormal":true)"; }
} else {
result << R"(,"tx.frameSizeNormal":true)";
}
if (modulator.is_pilots) { if (modulator.is_pilots) { result << R"(,"tx.isPilots":true)"; }
result << R"(,"tx.isPilots":true)"; else { result << R"(,"tx.isPilots":false)"; }
} else { #else
result << R"(,"tx.isPilots":false)"; {
modulator_settings modSet{};
daemon->getSettings(&modSet, nullptr, nullptr);
result << ",\"tx.centerFreq\":"; writeDouble(result, modSet.central_freq_in_kGz);
result << ",\"tx.symSpeed\":"; writeDouble(result, (static_cast<double>(modSet.baudrate) / 1000.0));
} }
#endif
result << ",\"tx.speedOnTxKbit\":"; writeDouble(result, static_cast<double>(modulator.speed_in_bytes_tx) / 128.0); result << ",\"tx.speedOnTxKbit\":"; writeDouble(result, static_cast<double>(modulator.speed_in_bytes_tx) / 128.0);
result << ",\"tx.speedOnIifKbit\":"; writeDouble(result, (static_cast<double>(modulator.speed_in_bytes_tx_iface) / 128.0)); result << ",\"tx.speedOnIifKbit\":"; writeDouble(result, (static_cast<double>(modulator.speed_in_bytes_tx_iface) / 128.0));
@@ -610,13 +761,11 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
result << ",\"rx.packetsBad\":" << demodulator.packet_bad_cnt; result << ",\"rx.packetsBad\":" << demodulator.packet_bad_cnt;
result << ",\"rx.packetsDummy\":" << demodulator.dummy_cnt; result << ",\"rx.packetsDummy\":" << demodulator.dummy_cnt;
#ifdef MODEM_IS_SCPC
// формируем структуру для CinC // формируем структуру для CinC
if (isCinC) { if (isCinC) {
CinC_state state_cinc{};
CP_GetCinCState(sid,state_cinc);
if (modulator.is_tx_on) { if (modulator.is_tx_on) {
if (state_cinc.carrier_lock) { if (cinc.carrier_lock) {
result << R"(,"cinc.correlator":true)"; result << R"(,"cinc.correlator":true)";
} else { } else {
result << R"(,"cinc.correlator":false)"; result << R"(,"cinc.correlator":false)";
@@ -625,14 +774,15 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
result << R"(,"cinc.correlator":null)"; result << R"(,"cinc.correlator":null)";
} }
result << ",\n\"cinc.occ\":"; writeDouble(result, state_cinc.ratio_signal_signal, 3); result << ",\n\"cinc.occ\":"; writeDouble(result, cinc.ratio_signal_signal, 3);
result << ",\"cinc.correlatorFails\":" << state_cinc.cnt_bad_lock; result << ",\"cinc.correlatorFails\":" << cinc.cnt_bad_lock;
result << ",\"cinc.freqErr\":" << state_cinc.freq_error_offset; result << ",\"cinc.freqErr\":" << cinc.freq_error_offset;
result << ",\"cinc.freqErrAcc\":" << state_cinc.freq_fine_estimate; result << ",\"cinc.freqErrAcc\":" << cinc.freq_fine_estimate;
result << ",\"cinc.channelDelay\":" << state_cinc.delay_dpdi; result << ",\"cinc.channelDelay\":" << cinc.delay_dpdi;
} else { } else {
result << R"(,"cinc.correlator":null)"; result << R"(,"cinc.correlator":null)";
} }
#endif
// структура температур девайса // структура температур девайса
result << ",\n\"device.adrv\":"; writeDouble(result, device.adrv_temp, 1); result << ",\n\"device.adrv\":"; writeDouble(result, device.adrv_temp, 1);
@@ -656,14 +806,19 @@ std::string api_driver::ApiDriver::loadSettings() const {
modulator_settings modSettings{}; modulator_settings modSettings{};
demodulator_settings demodSettings{}; demodulator_settings demodSettings{};
ACM_parameters_serv_ acmSettings{};
DPDI_parmeters dpdiSettings{};
buc_lnb_settings bucLnb{}; buc_lnb_settings bucLnb{};
TerminalNetworkSettings network; TerminalNetworkSettings network;
#ifdef MODEM_IS_SCPC
ACM_parameters_serv_ acmSettings{};
DPDI_parmeters dpdiSettings{};
daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb); daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb);
#else
daemon->getSettings(&modSettings, &demodSettings, &bucLnb);
#endif
daemon->getNetworkSettings(network); daemon->getNetworkSettings(network);
std::stringstream result; std::stringstream result;
#ifdef MODEM_IS_SCPC
result << "{\n\"general.isCinC\":" << boolAsStr(modSettings.is_cinc); result << "{\n\"general.isCinC\":" << boolAsStr(modSettings.is_cinc);
result << ",\"general.txEn\":" << boolAsStr(modSettings.tx_is_on); result << ",\"general.txEn\":" << boolAsStr(modSettings.tx_is_on);
result << ",\"general.modulatorMode\":" << (modSettings.is_carrier ? "\"normal\"" : "\"test\""); result << ",\"general.modulatorMode\":" << (modSettings.is_carrier ? "\"normal\"" : "\"test\"");
@@ -674,7 +829,7 @@ std::string api_driver::ApiDriver::loadSettings() const {
result << ",\"tx.cymRate\":" << modSettings.baudrate; result << ",\"tx.cymRate\":" << modSettings.baudrate;
result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3); result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3);
result << ",\"dvbs2.frameSizeNormal\":" << boolAsStr(!(modSettings.modcod_tx & 2)); 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 << ",\"dvbs2.isPilots\":" << "null";
result << ",\n\"dvbs2.isAcm\":" << boolAsStr(acmSettings.enable); result << ",\n\"dvbs2.isAcm\":" << boolAsStr(acmSettings.enable);
@@ -702,6 +857,20 @@ std::string api_driver::ApiDriver::loadSettings() const {
result << ",\"cinc.position.satelliteLongitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_sattelite_grad, dpdiSettings.longitude_sattelite_minute), 6); result << ",\"cinc.position.satelliteLongitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_sattelite_grad, dpdiSettings.longitude_sattelite_minute), 6);
result << ",\"cinc.delayMin\":" << dpdiSettings.min_delay; result << ",\"cinc.delayMin\":" << dpdiSettings.min_delay;
result << ",\"cinc.delayMax\":" << dpdiSettings.max_delay; result << ",\"cinc.delayMax\":" << dpdiSettings.max_delay;
#else
result << "{\n\"tx.txEn\":" << boolAsStr(modSettings.tx_is_on);
result << ",\"tx.isTestInputData\":" << boolAsStr(modSettings.is_test_data);
result << ",\"tx.cymRate\":" << modSettings.baudrate;
result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3);
result << ",\"tx.attenuation\":"; writeDouble(result, modSettings.attenuation);
result << ",\n\"rx.gainMode\":" << (demodSettings.is_aru_on ? "\"auto\"" : "\"manual\"");
result << ",\"rx.manualGain\":"; writeDouble(result, demodSettings.gain);
result << ",\"rx.spectrumInversion\":" << boolAsStr(demodSettings.is_rvt_iq);
result << ",\"rx.rolloff\":" << static_cast<int>(demodSettings.rollof * 100);
result << ",\"rx.cymRate\":" << demodSettings.baudrate;
result << ",\"rx.centerFreq\":"; writeDouble(result, demodSettings.central_freq_in_kGz);
#endif
result << ",\n\"buc.refClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_buc); result << ",\n\"buc.refClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_buc);
switch (bucLnb.buc) { switch (bucLnb.buc) {
@@ -744,9 +913,8 @@ std::string api_driver::ApiDriver::loadFirmwareVersion() const {
return R"({"error": "api daemon not started!"})"; return R"({"error": "api daemon not started!"})";
} }
TerminalFirmwareVersion firmware;
daemon->getFirmwareVersion(firmware);
std::stringstream result; std::stringstream result;
auto firmware = daemon->getFirmware();
result << "{\n\"fw.version\":" << buildEscapedString(firmware.version); result << "{\n\"fw.version\":" << buildEscapedString(firmware.version);
result << ",\"fw.modemId\":" << buildEscapedString(firmware.modemId); result << ",\"fw.modemId\":" << buildEscapedString(firmware.modemId);
result << ",\"fw.modemSn\":" << buildEscapedString(firmware.modemSn); result << ",\"fw.modemSn\":" << buildEscapedString(firmware.modemSn);
@@ -759,9 +927,12 @@ std::string api_driver::ApiDriver::loadFirmwareVersion() const {
void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) { void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
modulator_settings mod{}; modulator_settings mod{};
demodulator_settings demod{}; demodulator_settings demod{};
#ifdef MODEM_IS_SCPC
ACM_parameters_serv_ acm{}; ACM_parameters_serv_ acm{};
#endif
// для модулятора // для модулятора
#ifdef MODEM_IS_SCPC
mod.is_cinc = pt.get<bool>(json_path("general.isCinC", '/')); mod.is_cinc = pt.get<bool>(json_path("general.isCinC", '/'));
mod.tx_is_on = pt.get<bool>(json_path("general.txEn", '/')); mod.tx_is_on = pt.get<bool>(json_path("general.txEn", '/'));
auto tmp = pt.get<std::string>(json_path("general.modulatorMode", '/')); auto tmp = pt.get<std::string>(json_path("general.modulatorMode", '/'));
@@ -775,11 +946,22 @@ void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
mod.baudrate = pt.get<uint32_t>(json_path("tx.cymRate", '/')); mod.baudrate = pt.get<uint32_t>(json_path("tx.cymRate", '/'));
mod.central_freq_in_kGz = pt.get<double>(json_path("tx.centerFreq", '/')); mod.central_freq_in_kGz = pt.get<double>(json_path("tx.centerFreq", '/'));
const bool acmIsShortFrame = pt.get<bool>(json_path("dvbs2.frameSizeNormal", '/')); const bool acmIsShortFrame = !pt.get<bool>(json_path("dvbs2.frameSizeNormal", '/'));
mod.modcod_tx = (pt.get<uint32_t>(json_path("dvbs2.ccm_modcod", '/')) << 2) | (acmIsShortFrame ? 2 : 0); mod.modcod_tx = (pt.get<uint32_t>(json_path("dvbs2.ccm_modcod", '/')) << 2) | (acmIsShortFrame ? 2 : 0);
#else
mod.tx_is_on = pt.get<bool>(json_path("tx.txEn", '/'));
mod.is_test_data = pt.get<bool>(json_path("tx.isTestInputData", '/'));
mod.central_freq_in_kGz = pt.get<double>(json_path("tx.centerFreq", '/'));
mod.baudrate = pt.get<uint32_t>(json_path("tx.cymRate", '/'));
mod.attenuation = pt.get<double>(json_path("tx.attenuation", '/'));
#endif
// демодулятор // демодулятор
#ifdef MODEM_IS_SCPC
tmp = pt.get<std::string>(json_path("rx.gainMode", '/')); tmp = pt.get<std::string>(json_path("rx.gainMode", '/'));
#else
auto tmp = pt.get<std::string>(json_path("rx.gainMode", '/'));
#endif
if (tmp == "auto") { demod.is_aru_on = true; } if (tmp == "auto") { demod.is_aru_on = true; }
else if (tmp == "manual") { demod.is_aru_on = false; } else if (tmp == "manual") { demod.is_aru_on = false; }
else { throw std::runtime_error("api_driver::ApiDriver::setRxTxSettings(): Wrong gain mode: " + tmp); } else { throw std::runtime_error("api_driver::ApiDriver::setRxTxSettings(): Wrong gain mode: " + tmp); }
@@ -789,6 +971,7 @@ void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
demod.rollof = pt.get<double>(json_path("rx.rolloff", '/')) / 100.0; demod.rollof = pt.get<double>(json_path("rx.rolloff", '/')) / 100.0;
demod.central_freq_in_kGz = pt.get<double>(json_path("rx.centerFreq", '/')); demod.central_freq_in_kGz = pt.get<double>(json_path("rx.centerFreq", '/'));
#ifdef MODEM_IS_SCPC
// ACM // ACM
acm.enable = pt.get<bool>(json_path("dvbs2.isAcm", '/')); acm.enable = pt.get<bool>(json_path("dvbs2.isAcm", '/'));
acm.max_modcod = (pt.get<uint32_t>(json_path("dvbs2.acm_maxModcod", '/')) << 2) | (acmIsShortFrame ? 2 : 0); acm.max_modcod = (pt.get<uint32_t>(json_path("dvbs2.acm_maxModcod", '/')) << 2) | (acmIsShortFrame ? 2 : 0);
@@ -801,8 +984,12 @@ void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
acm.snr_treashold = pt.get<double>(json_path("acm.requiredSnr", '/')); // требуемый ОСШ acm.snr_treashold = pt.get<double>(json_path("acm.requiredSnr", '/')); // требуемый ОСШ
daemon->setSettingsRxTx(mod, demod, acm); daemon->setSettingsRxTx(mod, demod, acm);
#else
daemon->setSettingsRxTx(mod, demod);
#endif
} }
#ifdef MODEM_IS_SCPC
void api_driver::ApiDriver::setCincSettings(boost::property_tree::ptree &pt) { void api_driver::ApiDriver::setCincSettings(boost::property_tree::ptree &pt) {
DPDI_parmeters s{}; DPDI_parmeters s{};
@@ -829,8 +1016,11 @@ void api_driver::ApiDriver::setCincSettings(boost::property_tree::ptree &pt) {
s.max_delay = pt.get<uint32_t>(json_path("cinc.delayMax", '/')); s.max_delay = pt.get<uint32_t>(json_path("cinc.delayMax", '/'));
s.min_delay = pt.get<uint32_t>(json_path("cinc.delayMin", '/')); 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); this->daemon->setSettingsCinc(s);
} }
#endif
void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) { void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) {
buc_lnb_settings s{}; buc_lnb_settings s{};
@@ -849,7 +1039,9 @@ void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) {
tmp = pt.get<int>(json_path("buc.powering", '/')); tmp = pt.get<int>(json_path("buc.powering", '/'));
switch (tmp) { switch (tmp) {
case 24: s.buc = voltage_buc::_24V; break; case 24: s.buc = voltage_buc::_24V; break;
#ifdef MODEM_IS_SCPC
case 48: s.buc = voltage_buc::_48V; break; case 48: s.buc = voltage_buc::_48V; break;
#endif
case 0: case 0:
default: default:
s.lnb = voltage_lnb::DISABLE; s.lnb = voltage_lnb::DISABLE;
@@ -901,10 +1093,42 @@ void api_driver::ApiDriver::executeInApi(const std::function<void(TSID sid)>& ca
} }
} }
bool api_driver::ApiDriver::getIsCinC() const { std::string api_driver::ApiDriver::loadSysInfo() {
modulator_settings s{}; std::stringstream result;
daemon->getSettings(&s, nullptr, nullptr, nullptr, nullptr); struct sysinfo info{};
return s.is_cinc; 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; api_driver::ApiDriver::~ApiDriver() = default;

View File

@@ -46,11 +46,12 @@ namespace api_driver {
*/ */
void setRxTxSettings(boost::property_tree::ptree &pt); void setRxTxSettings(boost::property_tree::ptree &pt);
#ifdef MODEM_IS_SCPC
/** /**
* Установить настройки CinC, readback можно получить используя loadTerminalState. * Установить настройки CinC, readback можно получить используя loadTerminalState.
*/ */
void setCincSettings(boost::property_tree::ptree &pt); void setCincSettings(boost::property_tree::ptree &pt);
#endif
/** /**
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState. * Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
*/ */
@@ -69,16 +70,12 @@ namespace api_driver {
void executeInApi(const std::function<void(TSID sid)>& callback); void executeInApi(const std::function<void(TSID sid)>& callback);
static std::string loadSysInfo();
~ApiDriver(); ~ApiDriver();
private: private:
TSID sid{0};
unsigned int access{0};
std::string deviceInitState;
std::unique_ptr<TerminalApiDaemon> daemon; std::unique_ptr<TerminalApiDaemon> daemon;
bool getIsCinC() const;
}; };
} }

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

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

10
static/dev.html Normal file
View File

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

View File

@@ -1,5 +1,5 @@
.tabs-header { .tabs-header {
margin: 0.5em 0; margin: 0.5em 0 3px;
background: var(--brand-bg); background: var(--brand-bg);
} }
.tabs-header > * { .tabs-header > * {
@@ -7,7 +7,6 @@
} }
.tabs-btn { .tabs-btn {
text-decoration: none; text-decoration: none;
font-size: 18px;
border: none; border: none;
padding: 10px 25px; padding: 10px 25px;
text-align: center; text-align: center;
@@ -17,7 +16,7 @@
} }
.tabs-btn.active { .tabs-btn.active {
color: var(--brand-text); color: var(--brand-text);
border-bottom: 3px solid var(--brand-text); border-bottom: 3px solid var(--bg-action);
} }
.tabs-body-item { .tabs-body-item {
padding: 20px 0; padding: 20px 0;
@@ -58,7 +57,7 @@
} }
.action-button { .action-button {
background: var(--brand-bg); background: var(--bg-action);
} }
.nav-bar-element { .nav-bar-element {
@@ -87,6 +86,7 @@
.settings-set-container th { .settings-set-container th {
text-align: left; text-align: left;
font-weight: normal;
padding-right: 1em; padding-right: 1em;
} }
.settings-set-container td { .settings-set-container td {
@@ -113,7 +113,7 @@ label {
.settings-set-container input, .settings-set-container select { .settings-set-container input, .settings-set-container select {
margin-top: 0.5em; margin-top: 0.5em;
border: none; border: none;
border-bottom: solid 2px var(--text-color); border-bottom: solid 2px var(--text-color2);
width: 20em; width: 20em;
box-sizing: border-box; box-sizing: border-box;
} }
@@ -121,7 +121,7 @@ label {
.settings-set-container input:focus { .settings-set-container input:focus {
outline: none; outline: none;
border: none; border: none;
border-bottom: solid 2px var(--brand-text); border-bottom: solid 2px var(--bg-action);
} }
.settings-set-container input:invalid { .settings-set-container input:invalid {
@@ -193,7 +193,7 @@ details > summary {
.toggle-input input[type="checkbox"]:checked + .slider { .toggle-input input[type="checkbox"]:checked + .slider {
left: 25px; left: 25px;
background-color: var(--brand-text); background-color: var(--bg-action);
} }
.toggle-input input[type="checkbox"]:checked + .slider:before { .toggle-input input[type="checkbox"]:checked + .slider:before {

BIN
static/internet.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

View File

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

1700
static/main-scpc.html Normal file

File diff suppressed because it is too large Load Diff

1351
static/main-tdma.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,17 +2,17 @@
body { body {
--text-color: #262626; --text-color: #262626;
--text-color2: #3d3d3d; --text-color2: #3d3d3d;
--text-good: green; --text-good: #0CF500;
--text-bad: red; --text-bad: #F5000C;
--brand-bg: #EDF3FE; --brand-bg: #B3C0D1;
--brand-text: #5488F7; --brand-text: #0146f4;
--bg-color: #FEFEFE; --bg-color: #FEFEFE;
--bg-selected: #F1F1F1; --bg-selected: #F1F1F1;
--bg-element: #a7a7a7; --bg-element: #a7a7a7;
--bg-action: #5181fe; --bg-action: #81a7ff;
--bg-danger: #db2828; --bg-danger: #ff6464;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
@@ -20,8 +20,8 @@ body {
body { body {
--text-color: #eee; --text-color: #eee;
--text-color2: #bbb; --text-color2: #bbb;
--text-good: greenyellow; --text-good: #91FF00;
--text-bad: orangered; --text-bad: #FF1F2A;
--brand-bg: #393E50; --brand-bg: #393E50;
--brand-text: #5F93F3; --brand-text: #5F93F3;
@@ -29,7 +29,8 @@ body {
--bg-color: #2d2c33; --bg-color: #2d2c33;
--bg-selected: #424248; --bg-selected: #424248;
--bg-element: #626268; --bg-element: #626268;
--bg-action: #4a70d5; --bg-action: #3a58af;
--bg-danger: #ac1e1e;
} }
} }
@@ -52,6 +53,13 @@ body {
margin: 0.5em; margin: 0.5em;
} }
/* увеличение размера шрифтов */
* { font-size: large; }
h1 { font-size: xxx-large; }
h2 { font-size: xx-large; }
h3 { font-size: larger; }
/* ========== MAIN STYLES ========== */ /* ========== MAIN STYLES ========== */
.value-good { .value-good {

Binary file not shown.