Compare commits
36 Commits
4a27a46c27
...
v1.1-stabl
Author | SHA1 | Date | |
---|---|---|---|
479200df9e | |||
20cf08e2a1 | |||
ab654a754c | |||
8990fed8f0 | |||
be6c8023c5 | |||
24cb1061a7 | |||
77ba05e407 | |||
f3454897d8 | |||
dc4e37eb8a | |||
522abee794 | |||
8278e4119f | |||
0a3a282d0f | |||
3d97824ee7 | |||
55fa498dc1 | |||
f5c5caa31c | |||
833374a80e | |||
b67011b9a3 | |||
91e9c0301e | |||
98dcc06a6a | |||
e0aacfe8aa | |||
3e4ffc8281 | |||
1f8ea04f43 | |||
ad6d734f4a | |||
6d79d60eb1 | |||
572a2583f0 | |||
925fec6dda | |||
5f3d5791da | |||
c05a9cff7a | |||
dff0ba1cd3 | |||
43f35da9a2 | |||
ac04c0545b | |||
3e46f82c0e | |||
1536914888 | |||
1a80e9d455 | |||
e2618e0300 | |||
1d73547eae |
@@ -15,6 +15,16 @@ else()
|
||||
message(FATAL_ERROR "You must set build type \"Debug\" or \"Release\". Another build types not supported!")
|
||||
endif()
|
||||
|
||||
if("${MODEM_TYPE}" STREQUAL "SCPC")
|
||||
add_definitions(-DMODEM_IS_SCPC)
|
||||
message(STATUS "Selected SCPC modem")
|
||||
elseif ("${MODEM_TYPE}" STREQUAL "TDMA")
|
||||
add_definitions(-DMODEM_IS_TDMA)
|
||||
message(STATUS "Selected TDMA modem")
|
||||
else()
|
||||
message(FATAL_ERROR "You must set `MODEM_TYPE` \"SCPC\" or \"TDMA\"!")
|
||||
endif()
|
||||
|
||||
add_compile_options(-Wall -Wextra -Wsign-conversion)
|
||||
|
||||
# максимальный размер тела запроса 200mb
|
||||
@@ -47,6 +57,8 @@ add_executable(terminal-web-server
|
||||
src/auth/utils.h
|
||||
)
|
||||
|
||||
add_definitions(-DBOOST_LOG_DYN_LINK)
|
||||
|
||||
find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} terminal-client-api)
|
||||
|
4
dependencies/control_system/CMakeLists.txt
vendored
4
dependencies/control_system/CMakeLists.txt
vendored
@@ -15,14 +15,14 @@ set(CMAKE_CXX_FLAGS -fPIC)
|
||||
|
||||
set(default_build_type "Release")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -s -DNDEBUG ")
|
||||
message(${CMAKE_CXX_FLAGS})
|
||||
message("CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE})
|
||||
|
||||
message(${CMAKE_CXX_FLAGS})
|
||||
|
||||
add_library(terminal-client-api SHARED
|
||||
add_library(terminal-client-api STATIC
|
||||
client/main.cpp
|
||||
client/sock_client.cpp
|
||||
client/system_client.cpp
|
||||
|
75
front-generator/render-params.json
Normal file
75
front-generator/render-params.json
Normal 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
39
front-generator/render.py
Normal 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])
|
||||
|
139
front-generator/template/default-js.js
Normal file
139
front-generator/template/default-js.js
Normal 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 %}
|
@@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<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="/fields.css">
|
||||
<style>
|
||||
<style>{% raw %}
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@@ -31,27 +31,27 @@
|
||||
.l3-proto-label input[type=checkbox] {
|
||||
width: auto;
|
||||
}
|
||||
{% endraw %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<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_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>
|
||||
<!-- Последнее обновление: {{ lastUpdateTime }}-->
|
||||
<span :class="{ value_bad: initState !== 'Успешная инициализация системы' }">{{ initState }}</span>
|
||||
|
||||
{% endraw %}
|
||||
<div class="tabs-header">
|
||||
<span style="font-weight:bold">RSCM-101</span>
|
||||
<a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a>
|
||||
<a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a>
|
||||
<a href="#qos" class="tabs-btn" @click="activeTab = 'qos'" :class="{ active: activeTab === 'qos' }">QoS</a>
|
||||
<a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
|
||||
<a href="/logout" class="tabs-btn">Выход</a>
|
||||
<span style="font-weight:bold">{{ modem_name }}</span>
|
||||
{% for tab in header_tabs %}
|
||||
<a href="#{{ tab.name }}" class="tabs-btn" @click="activeTab = '{{ tab.name }}'" :class="{{ '{' }} active: activeTab === '{{ tab.name }}' {{ '}' }}">{{ tab.description }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</header>
|
||||
{% raw %}
|
||||
<div id="content">
|
||||
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
|
||||
<div class="settings-set-container">
|
||||
@@ -116,12 +116,13 @@
|
||||
<table>
|
||||
<tbody>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete">
|
||||
<h2>Настройки приема/передачи</h2>
|
||||
<div class="settings-set-container">
|
||||
@@ -167,7 +168,7 @@
|
||||
<h3>Параметры передачи</h3>
|
||||
<label>
|
||||
<span>Центральная частота, КГц</span>
|
||||
<input v-model="param.tx.centerFreq" type="number"/>
|
||||
<input v-model="param.tx.centerFreq" type="number" step="0.01"/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Символьная скорость, Бод</span>
|
||||
@@ -219,7 +220,7 @@
|
||||
|
||||
<label v-show="param.dvbs2.mode === 'ccm'">
|
||||
<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="8psk">8PSK</option>
|
||||
<option value="16apsk">16APSK</option>
|
||||
@@ -240,7 +241,7 @@
|
||||
|
||||
<label v-show="param.dvbs2.mode === 'acm'">
|
||||
<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="8psk">8PSK</option>
|
||||
<option value="16apsk">16APSK</option>
|
||||
@@ -255,7 +256,7 @@
|
||||
</label>
|
||||
<label v-show="param.dvbs2.mode === 'acm'">
|
||||
<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="8psk">8PSK</option>
|
||||
<option value="16apsk">16APSK</option>
|
||||
@@ -316,7 +317,7 @@
|
||||
</label>
|
||||
<label>
|
||||
<span>Центральная частота, кГц</span>
|
||||
<input v-model="param.rx.centerFreq" type="number"/>
|
||||
<input v-model="param.rx.centerFreq" type="number" step="0.01"/>
|
||||
</label>
|
||||
<label>
|
||||
<span>Символьная скорость, Бод</span>
|
||||
@@ -441,8 +442,7 @@
|
||||
</div>
|
||||
<template>
|
||||
<div v-for="classesGroup in ['rt1', 'rt2', 'rt3', 'cd']">
|
||||
<h3>Классы {{ classesGroup.toUpperCase() }}</h3>
|
||||
<button class="action-button" @click="qosAddClass(classesGroup)">Добавить класс {{ classesGroup.toUpperCase() }}</button>
|
||||
<h3>Классы {{ classesGroup.toUpperCase() }} <button class="action-button" @click="qosAddClass(classesGroup)"> + </button></h3>
|
||||
<details v-for="(qosClass, index) in param.qos[classesGroup]" :key="index" class="settings-set-container">
|
||||
<summary>
|
||||
<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}">
|
||||
</label>
|
||||
<label>
|
||||
<span>Порт для CinC</span>
|
||||
<span>Порт для данных</span>
|
||||
<input v-model="param.debugSend.portCinC" type="number" min="0" max="65535">
|
||||
</label>
|
||||
<label>
|
||||
@@ -617,7 +617,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<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>
|
||||
<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="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО <span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button>
|
||||
</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>
|
||||
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
|
||||
</div>
|
||||
{% endraw %}
|
||||
</div>
|
||||
|
||||
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
|
||||
<script src="/js/vue.js"></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 availableTabs = ['monitoring', 'setup', 'qos', 'admin']
|
||||
const availableTabs = {{ js_tabs_array }}
|
||||
const defaultTab = availableTabs[0]
|
||||
|
||||
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/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' }
|
||||
}
|
||||
|
||||
{% include 'default-js.js' %}
|
||||
{% raw %}
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
isCinC: false,
|
||||
|
||||
// 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
|
||||
{% endraw %}{% include 'vue-data.js' %}{% raw %}
|
||||
},
|
||||
methods: {
|
||||
getAvailableModcods(modulation) {
|
||||
@@ -1092,7 +766,7 @@
|
||||
body: JSON.stringify(query)
|
||||
}).then(async (resp) => {
|
||||
this.submitStatus.rxTx = false
|
||||
this.updateCincSettings(await resp.json())
|
||||
this.updateRxTxSettings(await resp.json())
|
||||
}).catch((reason) => {
|
||||
this.submitStatus.rxTx = false
|
||||
alert(`Ошибка при применении настроек: ${reason}`)
|
||||
@@ -1611,17 +1285,49 @@
|
||||
}
|
||||
|
||||
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() {
|
||||
const doFetchStatistics = async () => {
|
||||
try {
|
||||
let d = await fetch("/api/get/statistics")
|
||||
this.updateStatistics(await d.json())
|
||||
} catch (e) {
|
||||
this.initState = "Ошибка обновления статистики"
|
||||
if (this.submitStatus.modemReboot !== null) {
|
||||
this.initState = `Перезагрузка модема... Осталось ${this.submitStatus.modemReboot} сек`
|
||||
this.submitStatus.modemReboot--
|
||||
if (this.submitStatus.modemReboot <= 0) {
|
||||
window.location.reload()
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
let d = await fetch("/api/get/statistics")
|
||||
this.updateStatistics(await d.json())
|
||||
} catch (e) {
|
||||
this.initState = "Ошибка обновления статистики"
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
doFetchStatistics()
|
||||
}, 1000)
|
||||
@@ -1629,7 +1335,8 @@
|
||||
|
||||
const doFetchAbout = async () => {
|
||||
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.modemUid = d["fw.modemId"]
|
||||
this.about.modemSn = d["fw.modemSn"]
|
||||
@@ -1648,6 +1355,7 @@
|
||||
document.getElementById("app").removeAttribute("hidden")
|
||||
}
|
||||
})
|
||||
//{% endraw %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
181
front-generator/template/vue-data.js
Normal file
181
front-generator/template/vue-data.js
Normal 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,
|
88
src/main.cpp
88
src/main.cpp
@@ -84,6 +84,8 @@ class ServerResources {
|
||||
std::unique_ptr<api_driver::ApiDriver> api;
|
||||
http::auth::AuthProvider auth{};
|
||||
|
||||
bool upgradeOrRebootRunning = false;
|
||||
|
||||
static void onUploadFirmware(const http::server::Request& req) {
|
||||
std::ofstream f("/tmp/firmware.zip", std::ios::binary);
|
||||
|
||||
@@ -104,35 +106,37 @@ class ServerResources {
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr const char* INDEX_HTML = "static/main.html";
|
||||
static constexpr const char* LOGIN_HTML = "static/login.html";
|
||||
#if defined(MODEM_IS_TDMA)
|
||||
static constexpr const char* INDEX_HTML = "/main-tdma.html";
|
||||
#elif defined(MODEM_IS_SCPC)
|
||||
static constexpr const char* INDEX_HTML = "/main-scpc.html";
|
||||
#else
|
||||
#error "Modem type not defined!"
|
||||
#endif
|
||||
static constexpr const char* LOGIN_HTML = "/login.html";
|
||||
|
||||
// картинки, их даже можно кешировать
|
||||
static constexpr const char* FAVICON_ICO = "static/favicon.png";
|
||||
static constexpr const char* KROKODIL_GIF = "static/krokodil.gif";
|
||||
static constexpr const char* VUE_JS = "static/js/vue.js"; // это тоже можно кешировать
|
||||
static constexpr const char* FAVICON_ICO = "/favicon.ico";
|
||||
static constexpr const char* VUE_JS = "/js/vue.js"; // это тоже можно кешировать
|
||||
|
||||
// а эти стили нельзя кешировать в отладочной версии
|
||||
static constexpr const char* STYLE_CSS = "static/style.css";
|
||||
static constexpr const char* FIELDS_CSS = "static/fields.css";
|
||||
static constexpr const char* KB_MP4 = "static/video_2024-11-06_15-49-35.mp4";
|
||||
static constexpr const char* STYLE_CSS = "/style.css";
|
||||
static constexpr const char* FIELDS_CSS = "/fields.css";
|
||||
static constexpr const char* INTERNET_JPG = "/internet.jpg";
|
||||
|
||||
ServerResources(const ServerResources&) = delete;
|
||||
|
||||
ServerResources(): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
|
||||
explicit ServerResources(const std::string& staticFilesPath): sf(std::make_unique<http::resource::StaticFileFactory>()), api(std::make_unique<api_driver::ApiDriver>()) {
|
||||
api->startDaemon();
|
||||
auth.users.emplace_back(std::make_shared<http::auth::User>("admin", "", http::auth::User::SUPERUSER));
|
||||
|
||||
sf->registerFile(FAVICON_ICO, mime_types::image_png, true);
|
||||
sf->registerFile(KROKODIL_GIF, mime_types::image_gif, true);
|
||||
sf->registerFile(VUE_JS, mime_types::javascript, true);
|
||||
sf->registerFile(KB_MP4, mime_types::video_mp4, true);
|
||||
|
||||
sf->registerFile(STYLE_CSS, mime_types::text_css, true);
|
||||
sf->registerFile(FIELDS_CSS, mime_types::text_css, true);
|
||||
|
||||
sf->registerFile(INDEX_HTML, mime_types::text_html, false);
|
||||
sf->registerFile(LOGIN_HTML, mime_types::text_html, true);
|
||||
sf->registerFile(staticFilesPath + "/favicon.png", FAVICON_ICO, mime_types::image_png, true);
|
||||
sf->registerFile(staticFilesPath + VUE_JS, VUE_JS, mime_types::javascript, true);
|
||||
sf->registerFile(staticFilesPath + STYLE_CSS, STYLE_CSS, mime_types::text_css, true);
|
||||
sf->registerFile(staticFilesPath + FIELDS_CSS, FIELDS_CSS, mime_types::text_css, true);
|
||||
sf->registerFile(staticFilesPath + INDEX_HTML, INDEX_HTML, mime_types::text_html, false);
|
||||
sf->registerFile(staticFilesPath + LOGIN_HTML, LOGIN_HTML, mime_types::text_html, true);
|
||||
sf->registerFile(staticFilesPath + INTERNET_JPG, INTERNET_JPG, mime_types::image_jpeg, true);
|
||||
}
|
||||
|
||||
void registerResources(http::server::Server& s) {
|
||||
@@ -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>("/images/krokodil_vzryvaetsya_hd.gif", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(KROKODIL_GIF, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/style.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/fields.css", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/js/vue.js", [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/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::resource::GenericResource>(FAVICON_ICO, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FAVICON_ICO, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(STYLE_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(STYLE_CSS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(FIELDS_CSS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(FIELDS_CSS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(VUE_JS, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(VUE_JS, rep); }));
|
||||
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>(INTERNET_JPG, [this](const auto& req, auto& rep) { boost::ignore_unused(req); sf->serve(INTERNET_JPG, rep); }));
|
||||
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/get/statistics", this->auth, http::auth::User::WATCH_STATISTICS, [this](const auto& req, auto& rep) {
|
||||
if (req.method != "GET") {
|
||||
@@ -203,6 +206,8 @@ public:
|
||||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||||
std::string result = R"({"mainState":)";
|
||||
result += api->loadTerminalState();
|
||||
result += R"(,"sysinfo":)";
|
||||
result += api->loadSysInfo();
|
||||
result += "}";
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
}));
|
||||
@@ -301,7 +306,7 @@ public:
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
}
|
||||
}));
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/cinc", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||||
if (req.method != "POST") {
|
||||
http::server::stockReply(http::server::bad_request, rep);
|
||||
@@ -329,7 +334,7 @@ public:
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
}
|
||||
}));
|
||||
|
||||
#endif
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/set/rxtx", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) {
|
||||
if (req.method != "POST") {
|
||||
http::server::stockReply(http::server::bad_request, rep);
|
||||
@@ -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") {
|
||||
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)});
|
||||
const std::string result = R"({"status":"ok"})";
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
this->upgradeOrRebootRunning = true;
|
||||
system(REBOOT_COMMAND);
|
||||
}));
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/resetSettings", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||||
@@ -438,10 +444,11 @@ public:
|
||||
system(REBOOT_COMMAND);
|
||||
}));
|
||||
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [](const auto& req, auto& rep) {
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
|
||||
if (req.method != "PUT") {
|
||||
http::server::stockReply(http::server::bad_request, rep);
|
||||
}
|
||||
this->upgradeOrRebootRunning = true;
|
||||
onUploadFirmware(req);
|
||||
|
||||
rep.status = http::server::ok;
|
||||
@@ -453,12 +460,14 @@ public:
|
||||
result += http::utils::sha256(req.payload.data(), req.payload.size());
|
||||
result += "\"}";
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
this->upgradeOrRebootRunning = false;
|
||||
}));
|
||||
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/api/doFirmwareUpgrade", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) {
|
||||
if (req.method != "POST") {
|
||||
http::server::stockReply(http::server::bad_request, rep);
|
||||
}
|
||||
this->upgradeOrRebootRunning = true;
|
||||
doTerminalUpgrade();
|
||||
rep.status = http::server::ok;
|
||||
rep.headers.clear();
|
||||
@@ -466,6 +475,19 @@ public:
|
||||
const auto result = api->loadFirmwareVersion();
|
||||
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||
}));
|
||||
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||||
boost::ignore_unused(req);
|
||||
sf->serve(INTERNET_JPG, rep);
|
||||
}));
|
||||
|
||||
s.resources.emplace_back(std::make_unique<http::auth::AuthRequiredResource>("/dev/fetchParams", this->auth, http::auth::User::SUPERUSER, [this](const auto& req, auto& rep) {
|
||||
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
|
||||
std::string result = R"({"status":"ok","fwsize":)";
|
||||
result += std::to_string(req.payload.size());
|
||||
result += R"(,"sha256":")";
|
||||
result += "\"}";
|
||||
}));
|
||||
}
|
||||
|
||||
~ServerResources() = default;
|
||||
@@ -479,9 +501,9 @@ int main(int argc, char *argv[]) {
|
||||
if (argc != 4 && argc != 5) {
|
||||
std::cerr << "Usage: http_server <ssl|nossl> <address> <port> [static files directory]\n";
|
||||
std::cerr << " For IPv4, try:\n";
|
||||
std::cerr << " receiver nossl 0.0.0.0 80\n";
|
||||
std::cerr << " receiver nossl 0.0.0.0 80 .\n";
|
||||
std::cerr << " For IPv6, try:\n";
|
||||
std::cerr << " receiver nossl 0::0 80\n";
|
||||
std::cerr << " receiver nossl 0::0 80 .\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -502,7 +524,9 @@ int main(int argc, char *argv[]) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Generated new secret key " << http::auth::jwt::secretKey;
|
||||
#endif
|
||||
|
||||
ServerResources resources;
|
||||
const std::string staticFilesPath = (argc == 5 ? argv[4]: ".");
|
||||
BOOST_LOG_TRIVIAL(info) << "Use static files path: " << staticFilesPath << "/";
|
||||
ServerResources resources(staticFilesPath);
|
||||
|
||||
// Initialise the server.
|
||||
std::unique_ptr<http::server::Server> s;
|
||||
|
@@ -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::StaticFileFactory::StaticFileDef::StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache): path(std::move(path)), type(type), allowCache(allowCache) {
|
||||
http::resource::StaticFileFactory::StaticFileDef::StaticFileDef(const std::string& path, std::string webPath, server::mime_types::Mime type, bool allowCache):
|
||||
webPath(std::move(webPath)),
|
||||
#ifdef USE_DEBUG
|
||||
fsPath(path),
|
||||
#endif
|
||||
type(type), allowCache(allowCache) {
|
||||
#ifdef USE_DEBUG
|
||||
if (allowCache) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path;
|
||||
loadFile(this->path, this->content);
|
||||
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->webPath;
|
||||
loadFile(path, this->content);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->path;
|
||||
BOOST_LOG_TRIVIAL(info) << "Skip loading static file " << this->webPath;
|
||||
}
|
||||
#else
|
||||
BOOST_LOG_TRIVIAL(info) << "Load static file " << this->path;
|
||||
loadFile(this->path, this->content);
|
||||
BOOST_LOG_TRIVIAL(info) << "Load static file " << path;
|
||||
loadFile(path, this->content);
|
||||
#endif
|
||||
}
|
||||
http::resource::StaticFileFactory::StaticFileDef::~StaticFileDef() = default;
|
||||
|
||||
http::resource::StaticFileFactory::StaticFileFactory() = default;
|
||||
|
||||
void http::resource::StaticFileFactory::registerFile(const std::string &path, server::mime_types::Mime type, bool allowCache) {
|
||||
this->files.emplace_back(path, type, allowCache);
|
||||
void http::resource::StaticFileFactory::registerFile(const std::string &path, const std::string &webPath, server::mime_types::Mime type, bool allowCache) {
|
||||
this->files.emplace_back(path, webPath, type, allowCache);
|
||||
}
|
||||
|
||||
void http::resource::StaticFileFactory::serve(const std::string &path, server::Reply &rep) {
|
||||
for (auto& f: this->files) {
|
||||
if (f.path == path) {
|
||||
if (f.webPath == path) {
|
||||
#ifdef USE_DEBUG
|
||||
if (f.allowCache) {
|
||||
rep.content.clear();
|
||||
rep.content.insert(rep.content.end(), f.content.begin(), f.content.end());
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Reload file " << path << " (http path: " << path << ")";
|
||||
loadFile(f.path, rep.content);
|
||||
loadFile(f.fsPath, rep.content);
|
||||
}
|
||||
#else
|
||||
rep.content.clear();
|
||||
|
@@ -23,9 +23,12 @@ namespace http::resource {
|
||||
class StaticFileFactory {
|
||||
class StaticFileDef {
|
||||
public:
|
||||
StaticFileDef(std::string path, server::mime_types::Mime type, bool allowCache = true);
|
||||
StaticFileDef(const std::string& path, std::string webPath, server::mime_types::Mime type, bool allowCache = true);
|
||||
|
||||
std::string path;
|
||||
std::string webPath;
|
||||
#ifdef USE_DEBUG
|
||||
std::string fsPath;
|
||||
#endif
|
||||
server::mime_types::Mime type;
|
||||
bool allowCache;
|
||||
std::vector<char> content;
|
||||
@@ -36,7 +39,7 @@ namespace http::resource {
|
||||
public:
|
||||
StaticFileFactory();
|
||||
|
||||
void registerFile(const std::string& path, server::mime_types::Mime type, bool allowCache = true);
|
||||
void registerFile(const std::string& path, const std::string &webPath, server::mime_types::Mime type, bool allowCache = true);
|
||||
|
||||
void serve(const std::string& path, server::Reply& rep);
|
||||
|
||||
|
@@ -8,8 +8,9 @@
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/log/trivial.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;
|
||||
|
||||
@@ -69,13 +70,13 @@ public:
|
||||
std::string managementIp, managementGateway, mode, dataIp;
|
||||
unsigned int dataMtu = 1500;
|
||||
|
||||
TerminalNetworkSettings() { this->reset(); }
|
||||
TerminalNetworkSettings() = default;
|
||||
TerminalNetworkSettings(const TerminalNetworkSettings& src) = default;
|
||||
~TerminalNetworkSettings() = default;
|
||||
|
||||
TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default;
|
||||
|
||||
void reset() {
|
||||
void loadDefaults() {
|
||||
managementIp = "0.0.0.0/0";
|
||||
managementGateway = "";
|
||||
mode = "l2";
|
||||
@@ -95,30 +96,29 @@ public:
|
||||
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, а так же корректно сохранять настройки
|
||||
*/
|
||||
class api_driver::TerminalApiDaemon {
|
||||
private:
|
||||
|
||||
void updateFirmwareSettings() {
|
||||
std::string version, chip_id, sn, mac0, mac1;
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
|
||||
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;
|
||||
CP_Result lastCpError = OK;
|
||||
void logCpApiError(const char* desc, CP_Result err) {
|
||||
if (err != OK) {
|
||||
BOOST_LOG_TRIVIAL(error) << "CP API error in " << desc << ": " << err;
|
||||
this->lastCpError = err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,23 +126,36 @@ private:
|
||||
modulator_state modulator{};
|
||||
demodulator_state demodulator{};
|
||||
device_state device{};
|
||||
#ifdef MODEM_IS_SCPC
|
||||
CinC_state cinc{};
|
||||
#endif
|
||||
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_GetModulatorState(sid, modulator);
|
||||
CP_GetDemodulatorState(sid, demodulator);
|
||||
CP_GetDeviceState(sid, device);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetModulatorState()", CP_GetModulatorState(sid, modulator));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetDemodulatorState()", CP_GetDemodulatorState(sid, demodulator));
|
||||
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) {
|
||||
CP_GetCinCState(sid, cinc);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateState()->CP_GetCinCState()", CP_GetCinCState(sid, cinc));
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
std::lock_guard lock2(this->stateMutex);
|
||||
this->modState = modulator;
|
||||
this->demodState = demodulator;
|
||||
this->devState = device;
|
||||
#ifdef MODEM_IS_TDMA
|
||||
this->deviceInitState = tmpDevState;
|
||||
#endif
|
||||
#ifdef MODEM_IS_SCPC
|
||||
this->cincState = cinc;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,23 +164,29 @@ private:
|
||||
// uint32_t modulatorModcod;
|
||||
// CP_GetModulatorParams(sid, "modcod", &modulatorModcod);
|
||||
demodulator_settings demod{};
|
||||
#ifdef MODEM_IS_SCPC
|
||||
ACM_parameters_serv_ acm{};
|
||||
DPDI_parmeters dpdi{};
|
||||
#endif
|
||||
buc_lnb_settings bucLnb{};
|
||||
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_GetModulatorSettings(sid, mod);
|
||||
CP_GetDemodulatorSettings(sid, demod);
|
||||
CP_GetAcmParams(sid, &acm);
|
||||
CP_GetDpdiParams(sid, &dpdi);
|
||||
CP_GetBUC_LNB_settings(sid, bucLnb);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetModulatorSettings()", CP_GetModulatorSettings(sid, mod));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetDemodulatorSettings()", CP_GetDemodulatorSettings(sid, demod));
|
||||
#ifdef MODEM_IS_SCPC
|
||||
logCpApiError("api_driver::TerminalApiDaemon::updateSettings()->CP_GetAcmParams()", CP_GetAcmParams(sid, &acm));
|
||||
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);
|
||||
this->modSettings = mod;
|
||||
this->demodSettings = demod;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
this->acmSettings = acm;
|
||||
this->dpdiSettings = dpdi;
|
||||
#endif
|
||||
this->bucLnbSettings = bucLnb;
|
||||
}
|
||||
}
|
||||
@@ -176,15 +195,16 @@ private:
|
||||
TerminalNetworkSettings s;
|
||||
std::string tmp;
|
||||
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 + "/";
|
||||
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));
|
||||
tmp.clear(); 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(gateway)", CP_GetNetwork(sid, "gateway", &s.managementGateway)); s.managementGateway = tmp;
|
||||
tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::updateNetworkSettings()->CP_GetNetwork(mode)", CP_GetNetwork(sid, "mode", &tmp));
|
||||
if (tmp == "tun") {
|
||||
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 {
|
||||
s.mode = "l2";
|
||||
s.dataIp = "0.0.0.0/24";
|
||||
@@ -200,7 +220,7 @@ private:
|
||||
void updateQos() {
|
||||
bool tmp1; std::string tmp2;
|
||||
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);
|
||||
this->qosEnabled = tmp1;
|
||||
@@ -208,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() {
|
||||
// это демон, который в бесконечном цикле опрашивает API
|
||||
updateFirmwareSettings();
|
||||
this->connectToApi();
|
||||
|
||||
struct IntervalUpdate_t {
|
||||
int64_t lastUpdate;
|
||||
@@ -272,6 +335,12 @@ private:
|
||||
};
|
||||
|
||||
while (true) {
|
||||
if (this->lastCpError == ERROR || this->lastCpError == TIMEOUT) {
|
||||
BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::run(): close current daemon session caused error " << this->lastCpError;
|
||||
CP_Logout(this->sid);
|
||||
this->connectToApi();
|
||||
}
|
||||
|
||||
int64_t sleepTime = 60000; // минута по-умолчанию
|
||||
auto now = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
|
||||
for (auto& u: updaters) {
|
||||
@@ -290,20 +359,22 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_mutex firmwareMutex;
|
||||
TerminalFirmwareVersion firmware;
|
||||
|
||||
std::shared_mutex stateMutex;
|
||||
modulator_state modState{};
|
||||
demodulator_state demodState{};
|
||||
device_state devState{};
|
||||
std::string deviceInitState;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
CinC_state cincState{};
|
||||
#endif
|
||||
|
||||
std::shared_mutex settingsMutex;
|
||||
modulator_settings modSettings{};
|
||||
demodulator_settings demodSettings{};
|
||||
#ifdef MODEM_IS_SCPC
|
||||
ACM_parameters_serv_ acmSettings{};
|
||||
DPDI_parmeters dpdiSettings{};
|
||||
#endif
|
||||
buc_lnb_settings bucLnbSettings{};
|
||||
|
||||
std::shared_mutex networkSettingsMutex;
|
||||
@@ -313,15 +384,24 @@ private:
|
||||
bool qosEnabled;
|
||||
std::string qosClassesJson;
|
||||
|
||||
std::shared_mutex firmwareMutex;
|
||||
TerminalFirmwareVersion firmware;
|
||||
|
||||
public:
|
||||
std::mutex cpApiMutex;
|
||||
TSID sid;
|
||||
boost::thread daemon;
|
||||
|
||||
explicit TerminalApiDaemon(TSID sid): qosEnabled(false), sid(sid), daemon([this]() { this->run(); }) {
|
||||
explicit TerminalApiDaemon(): deviceInitState("Not connected to API"), qosEnabled(false), sid(0), daemon([this]() { this->run(); }) {
|
||||
this->qosClassesJson = DEFAULT_QOS_CLASSES;
|
||||
}
|
||||
|
||||
std::string getDeviceInitState() {
|
||||
std::shared_lock lock(this->stateMutex);
|
||||
return this->deviceInitState;
|
||||
}
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
/**
|
||||
* Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
|
||||
* @param mod статистика модулятра
|
||||
@@ -338,10 +418,27 @@ public:
|
||||
if (cinc) { *cinc = this->cincState; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
|
||||
* @param mod статистика модулятра
|
||||
* @param demod статистика демодулятора
|
||||
* @param dev статистика устройства (температуры)
|
||||
*/
|
||||
void getState(modulator_state* mod, demodulator_state* demod, device_state* dev) {
|
||||
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; }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Получение настроек, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет.
|
||||
*/
|
||||
#ifdef MODEM_IS_SCPC
|
||||
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) {
|
||||
std::shared_lock lock(this->settingsMutex);
|
||||
@@ -352,11 +449,23 @@ public:
|
||||
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) {
|
||||
std::shared_lock lock(this->networkSettingsMutex);
|
||||
@@ -369,21 +478,17 @@ public:
|
||||
json = this->qosClassesJson;
|
||||
}
|
||||
|
||||
void getFirmwareVersion(TerminalFirmwareVersion& fw) {
|
||||
std::lock_guard lock2(this->firmwareMutex);
|
||||
fw = this->firmware;
|
||||
}
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
void setSettingsRxTx(modulator_settings& mod, demodulator_settings& demod, ACM_parameters_serv_& acm, bool readback = true) {
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||||
CP_SetModulatorSettings(this->sid, mod);
|
||||
CP_SetDemodulatorSettings(this->sid, demod);
|
||||
CP_SetAcmParams(this->sid, acm);
|
||||
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));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_SetAcmParams()", CP_SetAcmParams(this->sid, acm));
|
||||
if (readback) {
|
||||
CP_GetModulatorSettings(this->sid, mod);
|
||||
CP_GetDemodulatorSettings(this->sid, demod);
|
||||
CP_GetAcmParams(this->sid, &acm);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetModulatorSettings()", CP_GetModulatorSettings(this->sid, mod));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetDemodulatorSettings()", CP_GetDemodulatorSettings(this->sid, demod));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsRxTx()->CP_GetAcmParams()", CP_GetAcmParams(this->sid, &acm));
|
||||
{
|
||||
std::lock_guard lock2{this->settingsMutex};
|
||||
this->modSettings = mod;
|
||||
@@ -391,51 +496,71 @@ public:
|
||||
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) {
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||||
CP_SetDpdiParams(sid, s);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsCinc()->CP_SetDpdiParams()", CP_SetDpdiParams(sid, s));
|
||||
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};
|
||||
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) {
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||||
CP_SetBUC_LNB_settings(this->sid, bucLnb);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setSettingsBucLnb()->CP_SetBUC_LNB_settings()", CP_SetBUC_LNB_settings(this->sid, bucLnb));
|
||||
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};
|
||||
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) {
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||||
CP_SetQoSSettings(this->sid, str, enabled);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setQosSettings()->CP_SetQoSSettings()", CP_SetQoSSettings(this->sid, str, enabled));
|
||||
if (readback) {
|
||||
bool tmp1; std::string tmp2;
|
||||
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);
|
||||
this->qosEnabled = tmp1;
|
||||
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) {
|
||||
@@ -447,30 +572,30 @@ public:
|
||||
else { throw std::runtime_error("invalid mode"); }
|
||||
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", "");
|
||||
CP_SetNetwork(sid, "mode", isL2 ? "tap" : "tun");
|
||||
CP_SetNetwork(sid, "addr", mang.first.c_str());
|
||||
CP_SetNetwork(sid, "mask", mang.second.c_str());
|
||||
CP_SetNetwork(sid, "gateway", s.managementGateway.c_str());
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(mode)", CP_SetNetwork(sid, "mode", isL2 ? "tap" : "tun"));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(addr)", CP_SetNetwork(sid, "addr", mang.first.c_str()));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(mask)", CP_SetNetwork(sid, "mask", mang.second.c_str()));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_SetNetwork(gateway)", CP_SetNetwork(sid, "gateway", s.managementGateway.c_str()));
|
||||
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 MTU не устанавливается, потому что в API этого нет
|
||||
|
||||
if (readback) {
|
||||
std::string tmp;
|
||||
s.reset();
|
||||
s.loadDefaults();
|
||||
s.managementIp.clear();
|
||||
CP_GetNetwork(sid, "addr", &s.managementIp);
|
||||
CP_GetNetwork(sid, "mask", &tmp);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(addr)", CP_GetNetwork(sid, "addr", &s.managementIp));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(mask)", CP_GetNetwork(sid, "mask", &tmp));
|
||||
s.managementIp += "/";
|
||||
s.managementIp += std::to_string(calculateSubnetMask(tmp));
|
||||
CP_GetNetwork(sid, "gateway", &s.managementGateway);
|
||||
tmp.clear(); CP_GetNetwork(sid, "mode", &tmp);
|
||||
logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(gateway)", CP_GetNetwork(sid, "gateway", &s.managementGateway));
|
||||
tmp.clear(); logCpApiError("api_driver::TerminalApiDaemon::setNetworkSettings()->CP_GetNetwork(mode)", CP_GetNetwork(sid, "mode", &tmp));
|
||||
if (tmp == "tun") {
|
||||
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 {
|
||||
s.mode = "l2";
|
||||
s.dataIp = "0.0.0.0/24";
|
||||
@@ -481,20 +606,25 @@ public:
|
||||
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() {
|
||||
std::string tmp;
|
||||
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() {
|
||||
std::lock_guard lock(this->cpApiMutex);
|
||||
CP_SetDmaDebug(sid, "begin_save_config", " ");
|
||||
CP_SetDmaDebug(sid, "default_params", "");
|
||||
CP_SetDmaDebug(sid, "save_config", " ");
|
||||
logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(begin_save_config)", CP_SetDmaDebug(sid, "begin_save_config", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(default_params)", CP_SetDmaDebug(sid, "default_params", ""));
|
||||
logCpApiError("api_driver::TerminalApiDaemon::resetDefaultSettings()->CP_SetDmaDebug(save_config)", CP_SetDmaDebug(sid, "save_config", ""));
|
||||
}
|
||||
|
||||
~TerminalApiDaemon() {
|
||||
@@ -508,17 +638,11 @@ public:
|
||||
};
|
||||
|
||||
|
||||
api_driver::ApiDriver::ApiDriver() {
|
||||
}
|
||||
api_driver::ApiDriver::ApiDriver() = default;
|
||||
|
||||
void api_driver::ApiDriver::startDaemon() {
|
||||
if (daemon == nullptr) {
|
||||
TSID sid{};
|
||||
unsigned int access{};
|
||||
CP_Login("admin", "pass", &sid, &access);
|
||||
CP_GetDmaDebug(sid, "status_init", &deviceInitState);
|
||||
|
||||
daemon = std::make_unique<TerminalApiDaemon>(sid);
|
||||
daemon = std::make_unique<TerminalApiDaemon>();
|
||||
BOOST_LOG_TRIVIAL(info) << "api_driver::ApiDriver::startDaemon(): API daemon succes started!";
|
||||
}
|
||||
}
|
||||
@@ -544,7 +668,7 @@ void writeDouble(std::ostream& out, double value, int prec = 2) {
|
||||
out << std::fixed << std::setprecision(prec) << value;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
double translateCoordinates(uint8_t deg, uint8_t min) {
|
||||
return static_cast<double>(deg) + static_cast<double>(min) / 60;
|
||||
}
|
||||
@@ -555,6 +679,7 @@ std::tuple<uint8_t, uint8_t> translateCoordinates(double abs) {
|
||||
auto min = static_cast<uint8_t>(min_double);
|
||||
return std::make_tuple(deg, min);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
std::string api_driver::ApiDriver::loadTerminalState() const {
|
||||
@@ -563,34 +688,42 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
|
||||
}
|
||||
std::stringstream result;
|
||||
|
||||
result << "{\n\"initState\":" << buildEscapedString(this->deviceInitState);
|
||||
result << "{\n\"initState\":" << buildEscapedString(daemon->getDeviceInitState());
|
||||
|
||||
modulator_state modulator{};
|
||||
demodulator_state demodulator{};
|
||||
device_state device{};
|
||||
#ifdef MODEM_IS_SCPC
|
||||
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);
|
||||
#endif
|
||||
|
||||
// формируем структуру для TX
|
||||
result << ",\n\"tx.state\":" << boolAsStr(modulator.is_tx_on);
|
||||
result << ",\"tx.modcod\":" << modulator.modcod;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
result << ",\"tx.snr\":"; writeDouble(result, modulator.snr_remote);
|
||||
|
||||
if (modulator.is_short) {
|
||||
result << R"(,"tx.frameSizeNormal":false)";
|
||||
} else {
|
||||
result << R"(,"tx.frameSizeNormal":true)";
|
||||
}
|
||||
if (modulator.is_short) { result << R"(,"tx.frameSizeNormal":false)"; }
|
||||
else { result << R"(,"tx.frameSizeNormal":true)"; }
|
||||
|
||||
if (modulator.is_pilots) {
|
||||
result << R"(,"tx.isPilots":true)";
|
||||
} else {
|
||||
result << R"(,"tx.isPilots":false)";
|
||||
if (modulator.is_pilots) { result << R"(,"tx.isPilots":true)"; }
|
||||
else { result << R"(,"tx.isPilots":false)"; }
|
||||
#else
|
||||
{
|
||||
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.speedOnIifKbit\":"; writeDouble(result, (static_cast<double>(modulator.speed_in_bytes_tx_iface) / 128.0));
|
||||
|
||||
@@ -628,6 +761,7 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
|
||||
result << ",\"rx.packetsBad\":" << demodulator.packet_bad_cnt;
|
||||
result << ",\"rx.packetsDummy\":" << demodulator.dummy_cnt;
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
// формируем структуру для CinC
|
||||
if (isCinC) {
|
||||
if (modulator.is_tx_on) {
|
||||
@@ -648,6 +782,7 @@ std::string api_driver::ApiDriver::loadTerminalState() const {
|
||||
} else {
|
||||
result << R"(,"cinc.correlator":null)";
|
||||
}
|
||||
#endif
|
||||
|
||||
// структура температур девайса
|
||||
result << ",\n\"device.adrv\":"; writeDouble(result, device.adrv_temp, 1);
|
||||
@@ -671,14 +806,19 @@ std::string api_driver::ApiDriver::loadSettings() const {
|
||||
|
||||
modulator_settings modSettings{};
|
||||
demodulator_settings demodSettings{};
|
||||
ACM_parameters_serv_ acmSettings{};
|
||||
DPDI_parmeters dpdiSettings{};
|
||||
buc_lnb_settings bucLnb{};
|
||||
TerminalNetworkSettings network;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
ACM_parameters_serv_ acmSettings{};
|
||||
DPDI_parmeters dpdiSettings{};
|
||||
daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb);
|
||||
#else
|
||||
daemon->getSettings(&modSettings, &demodSettings, &bucLnb);
|
||||
#endif
|
||||
daemon->getNetworkSettings(network);
|
||||
|
||||
std::stringstream result;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
result << "{\n\"general.isCinC\":" << boolAsStr(modSettings.is_cinc);
|
||||
result << ",\"general.txEn\":" << boolAsStr(modSettings.tx_is_on);
|
||||
result << ",\"general.modulatorMode\":" << (modSettings.is_carrier ? "\"normal\"" : "\"test\"");
|
||||
@@ -689,7 +829,7 @@ std::string api_driver::ApiDriver::loadSettings() const {
|
||||
result << ",\"tx.cymRate\":" << modSettings.baudrate;
|
||||
result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3);
|
||||
result << ",\"dvbs2.frameSizeNormal\":" << boolAsStr(!(modSettings.modcod_tx & 2));
|
||||
result << ",\"dvbs2.ccm_modcod\":" << (modSettings.modcod_tx >> 4);
|
||||
result << ",\"dvbs2.ccm_modcod\":" << (modSettings.modcod_tx >> 2);
|
||||
|
||||
// result << ",\"dvbs2.isPilots\":" << "null";
|
||||
result << ",\n\"dvbs2.isAcm\":" << boolAsStr(acmSettings.enable);
|
||||
@@ -717,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.delayMin\":" << dpdiSettings.min_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);
|
||||
switch (bucLnb.buc) {
|
||||
@@ -759,9 +913,8 @@ std::string api_driver::ApiDriver::loadFirmwareVersion() const {
|
||||
return R"({"error": "api daemon not started!"})";
|
||||
}
|
||||
|
||||
TerminalFirmwareVersion firmware;
|
||||
daemon->getFirmwareVersion(firmware);
|
||||
std::stringstream result;
|
||||
auto firmware = daemon->getFirmware();
|
||||
result << "{\n\"fw.version\":" << buildEscapedString(firmware.version);
|
||||
result << ",\"fw.modemId\":" << buildEscapedString(firmware.modemId);
|
||||
result << ",\"fw.modemSn\":" << buildEscapedString(firmware.modemSn);
|
||||
@@ -774,9 +927,12 @@ std::string api_driver::ApiDriver::loadFirmwareVersion() const {
|
||||
void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
|
||||
modulator_settings mod{};
|
||||
demodulator_settings demod{};
|
||||
#ifdef MODEM_IS_SCPC
|
||||
ACM_parameters_serv_ acm{};
|
||||
#endif
|
||||
|
||||
// для модулятора
|
||||
#ifdef MODEM_IS_SCPC
|
||||
mod.is_cinc = pt.get<bool>(json_path("general.isCinC", '/'));
|
||||
mod.tx_is_on = pt.get<bool>(json_path("general.txEn", '/'));
|
||||
auto tmp = pt.get<std::string>(json_path("general.modulatorMode", '/'));
|
||||
@@ -790,11 +946,22 @@ void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
|
||||
mod.baudrate = pt.get<uint32_t>(json_path("tx.cymRate", '/'));
|
||||
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);
|
||||
#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", '/'));
|
||||
#else
|
||||
auto tmp = pt.get<std::string>(json_path("rx.gainMode", '/'));
|
||||
#endif
|
||||
if (tmp == "auto") { demod.is_aru_on = true; }
|
||||
else if (tmp == "manual") { demod.is_aru_on = false; }
|
||||
else { throw std::runtime_error("api_driver::ApiDriver::setRxTxSettings(): Wrong gain mode: " + tmp); }
|
||||
@@ -804,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.central_freq_in_kGz = pt.get<double>(json_path("rx.centerFreq", '/'));
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
// ACM
|
||||
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);
|
||||
@@ -816,8 +984,12 @@ void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
|
||||
acm.snr_treashold = pt.get<double>(json_path("acm.requiredSnr", '/')); // требуемый ОСШ
|
||||
|
||||
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) {
|
||||
DPDI_parmeters s{};
|
||||
|
||||
@@ -844,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.min_delay = pt.get<uint32_t>(json_path("cinc.delayMin", '/'));
|
||||
|
||||
s.freq_offset = pt.get<uint32_t>(json_path("cinc.searchBandwidth", '/'));
|
||||
|
||||
this->daemon->setSettingsCinc(s);
|
||||
}
|
||||
#endif
|
||||
|
||||
void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) {
|
||||
buc_lnb_settings s{};
|
||||
@@ -864,7 +1039,9 @@ void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) {
|
||||
tmp = pt.get<int>(json_path("buc.powering", '/'));
|
||||
switch (tmp) {
|
||||
case 24: s.buc = voltage_buc::_24V; break;
|
||||
#ifdef MODEM_IS_SCPC
|
||||
case 48: s.buc = voltage_buc::_48V; break;
|
||||
#endif
|
||||
case 0:
|
||||
default:
|
||||
s.lnb = voltage_lnb::DISABLE;
|
||||
@@ -916,4 +1093,42 @@ void api_driver::ApiDriver::executeInApi(const std::function<void(TSID sid)>& ca
|
||||
}
|
||||
}
|
||||
|
||||
std::string api_driver::ApiDriver::loadSysInfo() {
|
||||
std::stringstream result;
|
||||
struct sysinfo info{};
|
||||
sysinfo(&info);
|
||||
|
||||
struct sysinfo {
|
||||
long uptime; /* Seconds since boot */
|
||||
unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
|
||||
unsigned long totalram; /* Total usable main memory size */
|
||||
unsigned long freeram; /* Available memory size */
|
||||
unsigned long sharedram; /* Amount of shared memory */
|
||||
unsigned long bufferram; /* Memory used by buffers */
|
||||
unsigned long totalswap; /* Total swap space size */
|
||||
unsigned long freeswap; /* Swap space still available */
|
||||
unsigned short procs; /* Number of current processes */
|
||||
unsigned long totalhigh; /* Total high memory size */
|
||||
unsigned long freehigh; /* Available high memory size */
|
||||
unsigned int mem_unit; /* Memory unit size in bytes */
|
||||
};
|
||||
|
||||
result << "{\n\"uptime\":" << info.uptime;
|
||||
result << ",\"load1min\":" << info.loads[0];
|
||||
result << ",\"load5min\":" << info.loads[1];
|
||||
result << ",\"load15min\":" << info.loads[2];
|
||||
result << ",\"totalram\":" << info.totalram;
|
||||
result << ",\"freeram\":" << info.freeram;
|
||||
result << ",\"sharedram\":" << info.sharedram;
|
||||
result << ",\"bufferram\":" << info.bufferram;
|
||||
result << ",\"totalswap\":" << info.totalswap;
|
||||
result << ",\"freeswap\":" << info.freeswap;
|
||||
result << ",\"procs\":" << static_cast<long>(info.procs);
|
||||
result << ",\"totalhigh\":" << info.totalhigh;
|
||||
result << ",\"freehigh\":" << info.freehigh;
|
||||
result << ",\"mem_unit\":" << info.mem_unit;
|
||||
result << "\n}";
|
||||
return result.str();
|
||||
}
|
||||
|
||||
api_driver::ApiDriver::~ApiDriver() = default;
|
||||
|
@@ -46,11 +46,12 @@ namespace api_driver {
|
||||
*/
|
||||
void setRxTxSettings(boost::property_tree::ptree &pt);
|
||||
|
||||
#ifdef MODEM_IS_SCPC
|
||||
/**
|
||||
* Установить настройки CinC, readback можно получить используя loadTerminalState.
|
||||
*/
|
||||
void setCincSettings(boost::property_tree::ptree &pt);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Установить настройки BUC и LNB, readback можно получить используя loadTerminalState.
|
||||
*/
|
||||
@@ -69,10 +70,11 @@ namespace api_driver {
|
||||
|
||||
void executeInApi(const std::function<void(TSID sid)>& callback);
|
||||
|
||||
static std::string loadSysInfo();
|
||||
|
||||
~ApiDriver();
|
||||
|
||||
private:
|
||||
std::string deviceInitState;
|
||||
std::unique_ptr<TerminalApiDaemon> daemon;
|
||||
};
|
||||
}
|
||||
|
16
static/dev-params.json
Normal file
16
static/dev-params.json
Normal 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
10
static/dev.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -1,5 +1,5 @@
|
||||
.tabs-header {
|
||||
margin: 0.5em 0;
|
||||
margin: 0.5em 0 3px;
|
||||
background: var(--brand-bg);
|
||||
}
|
||||
.tabs-header > * {
|
||||
@@ -7,7 +7,6 @@
|
||||
}
|
||||
.tabs-btn {
|
||||
text-decoration: none;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
padding: 10px 25px;
|
||||
text-align: center;
|
||||
@@ -17,7 +16,7 @@
|
||||
}
|
||||
.tabs-btn.active {
|
||||
color: var(--brand-text);
|
||||
border-bottom: 3px solid var(--brand-text);
|
||||
border-bottom: 3px solid var(--bg-action);
|
||||
}
|
||||
.tabs-body-item {
|
||||
padding: 20px 0;
|
||||
@@ -58,7 +57,7 @@
|
||||
}
|
||||
|
||||
.action-button {
|
||||
background: var(--brand-bg);
|
||||
background: var(--bg-action);
|
||||
}
|
||||
|
||||
.nav-bar-element {
|
||||
@@ -87,6 +86,7 @@
|
||||
|
||||
.settings-set-container th {
|
||||
text-align: left;
|
||||
font-weight: normal;
|
||||
padding-right: 1em;
|
||||
}
|
||||
.settings-set-container td {
|
||||
@@ -113,7 +113,7 @@ label {
|
||||
.settings-set-container input, .settings-set-container select {
|
||||
margin-top: 0.5em;
|
||||
border: none;
|
||||
border-bottom: solid 2px var(--text-color);
|
||||
border-bottom: solid 2px var(--text-color2);
|
||||
width: 20em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ label {
|
||||
.settings-set-container input:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
border-bottom: solid 2px var(--brand-text);
|
||||
border-bottom: solid 2px var(--bg-action);
|
||||
}
|
||||
|
||||
.settings-set-container input:invalid {
|
||||
@@ -193,7 +193,7 @@ details > summary {
|
||||
|
||||
.toggle-input input[type="checkbox"]:checked + .slider {
|
||||
left: 25px;
|
||||
background-color: var(--brand-text);
|
||||
background-color: var(--bg-action);
|
||||
}
|
||||
|
||||
.toggle-input input[type="checkbox"]:checked + .slider:before {
|
||||
|
BIN
static/internet.jpg
Normal file
BIN
static/internet.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
347
static/js/vue.js
347
static/js/vue.js
@@ -1,6 +1,6 @@
|
||||
/*!
|
||||
* Vue.js v2.7.16
|
||||
* (c) 2014-2023 Evan You
|
||||
* Vue.js v2.7.14
|
||||
* (c) 2014-2022 Evan You
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
(function (global, factory) {
|
||||
@@ -82,16 +82,9 @@
|
||||
return val == null
|
||||
? ''
|
||||
: Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
|
||||
? JSON.stringify(val, replacer, 2)
|
||||
? JSON.stringify(val, null, 2)
|
||||
: String(val);
|
||||
}
|
||||
function replacer(_key, val) {
|
||||
// avoid circular deps from v3
|
||||
if (val && val.__v_isRef) {
|
||||
return val.value;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
/**
|
||||
* Convert an input value to a number for persistence.
|
||||
* If the conversion fails, return original string.
|
||||
@@ -253,7 +246,9 @@
|
||||
*/
|
||||
function genStaticKeys$1(modules) {
|
||||
return modules
|
||||
.reduce(function (keys, m) { return keys.concat(m.staticKeys || []); }, [])
|
||||
.reduce(function (keys, m) {
|
||||
return keys.concat(m.staticKeys || []);
|
||||
}, [])
|
||||
.join(',');
|
||||
}
|
||||
/**
|
||||
@@ -730,35 +725,30 @@
|
||||
};
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
var __assign = function() {
|
||||
__assign = Object.assign || function __assign(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
|
||||
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
||||
var e = new Error(message);
|
||||
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
|
||||
var __assign = function() {
|
||||
__assign = Object.assign || function __assign(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
|
||||
var uid$2 = 0;
|
||||
@@ -892,7 +882,7 @@
|
||||
});
|
||||
|
||||
var arrayKeys = Object.getOwnPropertyNames(arrayMethods);
|
||||
var NO_INITIAL_VALUE = {};
|
||||
var NO_INIITIAL_VALUE = {};
|
||||
/**
|
||||
* In some cases we may want to disable observation inside a component's
|
||||
* update computation.
|
||||
@@ -951,7 +941,7 @@
|
||||
var keys = Object.keys(value);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var key = keys[i];
|
||||
defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock);
|
||||
defineReactive(value, key, NO_INIITIAL_VALUE, undefined, shallow, mock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -988,8 +978,7 @@
|
||||
/**
|
||||
* Define a reactive property on an Object.
|
||||
*/
|
||||
function defineReactive(obj, key, val, customSetter, shallow, mock, observeEvenIfShallow) {
|
||||
if (observeEvenIfShallow === void 0) { observeEvenIfShallow = false; }
|
||||
function defineReactive(obj, key, val, customSetter, shallow, mock) {
|
||||
var dep = new Dep();
|
||||
var property = Object.getOwnPropertyDescriptor(obj, key);
|
||||
if (property && property.configurable === false) {
|
||||
@@ -999,10 +988,10 @@
|
||||
var getter = property && property.get;
|
||||
var setter = property && property.set;
|
||||
if ((!getter || setter) &&
|
||||
(val === NO_INITIAL_VALUE || arguments.length === 2)) {
|
||||
(val === NO_INIITIAL_VALUE || arguments.length === 2)) {
|
||||
val = obj[key];
|
||||
}
|
||||
var childOb = shallow ? val && val.__ob__ : observe(val, false, mock);
|
||||
var childOb = !shallow && observe(val, false, mock);
|
||||
Object.defineProperty(obj, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@@ -1047,7 +1036,7 @@
|
||||
else {
|
||||
val = newVal;
|
||||
}
|
||||
childOb = shallow ? newVal && newVal.__ob__ : observe(newVal, false, mock);
|
||||
childOb = !shallow && observe(newVal, false, mock);
|
||||
{
|
||||
dep.notify({
|
||||
type: "set" /* TriggerOpTypes.SET */,
|
||||
@@ -2510,10 +2499,11 @@
|
||||
// to the data on the placeholder node.
|
||||
vm.$vnode = _parentVnode;
|
||||
// render self
|
||||
var prevInst = currentInstance;
|
||||
var prevRenderInst = currentRenderingInstance;
|
||||
var vnode;
|
||||
try {
|
||||
// There's no need to maintain a stack because all render fns are called
|
||||
// separately from one another. Nested component's render fns are called
|
||||
// when parent component is patched.
|
||||
setCurrentInstance(vm);
|
||||
currentRenderingInstance = vm;
|
||||
vnode = render.call(vm._renderProxy, vm.$createElement);
|
||||
@@ -2537,8 +2527,8 @@
|
||||
}
|
||||
}
|
||||
finally {
|
||||
currentRenderingInstance = prevRenderInst;
|
||||
setCurrentInstance(prevInst);
|
||||
currentRenderingInstance = null;
|
||||
setCurrentInstance();
|
||||
}
|
||||
// if the returned array contains only a single node, allow it
|
||||
if (isArray(vnode) && vnode.length === 1) {
|
||||
@@ -2803,112 +2793,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
var activeEffectScope;
|
||||
var EffectScope = /** @class */ (function () {
|
||||
function EffectScope(detached) {
|
||||
if (detached === void 0) { detached = false; }
|
||||
this.detached = detached;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.active = true;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.effects = [];
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.cleanups = [];
|
||||
this.parent = activeEffectScope;
|
||||
if (!detached && activeEffectScope) {
|
||||
this.index =
|
||||
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
|
||||
}
|
||||
}
|
||||
EffectScope.prototype.run = function (fn) {
|
||||
if (this.active) {
|
||||
var currentEffectScope = activeEffectScope;
|
||||
try {
|
||||
activeEffectScope = this;
|
||||
return fn();
|
||||
}
|
||||
finally {
|
||||
activeEffectScope = currentEffectScope;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warn$2("cannot run an inactive effect scope.");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This should only be called on non-detached scopes
|
||||
* @internal
|
||||
*/
|
||||
EffectScope.prototype.on = function () {
|
||||
activeEffectScope = this;
|
||||
};
|
||||
/**
|
||||
* This should only be called on non-detached scopes
|
||||
* @internal
|
||||
*/
|
||||
EffectScope.prototype.off = function () {
|
||||
activeEffectScope = this.parent;
|
||||
};
|
||||
EffectScope.prototype.stop = function (fromParent) {
|
||||
if (this.active) {
|
||||
var i = void 0, l = void 0;
|
||||
for (i = 0, l = this.effects.length; i < l; i++) {
|
||||
this.effects[i].teardown();
|
||||
}
|
||||
for (i = 0, l = this.cleanups.length; i < l; i++) {
|
||||
this.cleanups[i]();
|
||||
}
|
||||
if (this.scopes) {
|
||||
for (i = 0, l = this.scopes.length; i < l; i++) {
|
||||
this.scopes[i].stop(true);
|
||||
}
|
||||
}
|
||||
// nested scope, dereference from parent to avoid memory leaks
|
||||
if (!this.detached && this.parent && !fromParent) {
|
||||
// optimized O(1) removal
|
||||
var last = this.parent.scopes.pop();
|
||||
if (last && last !== this) {
|
||||
this.parent.scopes[this.index] = last;
|
||||
last.index = this.index;
|
||||
}
|
||||
}
|
||||
this.parent = undefined;
|
||||
this.active = false;
|
||||
}
|
||||
};
|
||||
return EffectScope;
|
||||
}());
|
||||
function effectScope(detached) {
|
||||
return new EffectScope(detached);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function recordEffectScope(effect, scope) {
|
||||
if (scope === void 0) { scope = activeEffectScope; }
|
||||
if (scope && scope.active) {
|
||||
scope.effects.push(effect);
|
||||
}
|
||||
}
|
||||
function getCurrentScope() {
|
||||
return activeEffectScope;
|
||||
}
|
||||
function onScopeDispose(fn) {
|
||||
if (activeEffectScope) {
|
||||
activeEffectScope.cleanups.push(fn);
|
||||
}
|
||||
else {
|
||||
warn$2("onScopeDispose() is called when there is no active effect scope" +
|
||||
" to be associated with.");
|
||||
}
|
||||
}
|
||||
|
||||
var activeInstance = null;
|
||||
var isUpdatingChildComponent = false;
|
||||
function setActiveInstance(vm) {
|
||||
@@ -3211,8 +3095,7 @@
|
||||
if (setContext === void 0) { setContext = true; }
|
||||
// #7573 disable dep collection when invoking lifecycle hooks
|
||||
pushTarget();
|
||||
var prevInst = currentInstance;
|
||||
var prevScope = getCurrentScope();
|
||||
var prev = currentInstance;
|
||||
setContext && setCurrentInstance(vm);
|
||||
var handlers = vm.$options[hook];
|
||||
var info = "".concat(hook, " hook");
|
||||
@@ -3224,10 +3107,7 @@
|
||||
if (vm._hasHookEvent) {
|
||||
vm.$emit('hook:' + hook);
|
||||
}
|
||||
if (setContext) {
|
||||
setCurrentInstance(prevInst);
|
||||
prevScope && prevScope.on();
|
||||
}
|
||||
setContext && setCurrentInstance(prev);
|
||||
popTarget();
|
||||
}
|
||||
|
||||
@@ -3445,10 +3325,7 @@
|
||||
var instance = currentInstance;
|
||||
var call = function (fn, type, args) {
|
||||
if (args === void 0) { args = null; }
|
||||
var res = invokeWithErrorHandling(fn, null, args, instance, type);
|
||||
if (deep && res && res.__ob__)
|
||||
res.__ob__.dep.depend();
|
||||
return res;
|
||||
return invokeWithErrorHandling(fn, null, args, instance, type);
|
||||
};
|
||||
var getter;
|
||||
var forceTrigger = false;
|
||||
@@ -3473,7 +3350,6 @@
|
||||
return s.value;
|
||||
}
|
||||
else if (isReactive(s)) {
|
||||
s.__ob__.dep.depend();
|
||||
return traverse(s);
|
||||
}
|
||||
else if (isFunction(s)) {
|
||||
@@ -3617,6 +3493,112 @@
|
||||
};
|
||||
}
|
||||
|
||||
var activeEffectScope;
|
||||
var EffectScope = /** @class */ (function () {
|
||||
function EffectScope(detached) {
|
||||
if (detached === void 0) { detached = false; }
|
||||
this.detached = detached;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.active = true;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.effects = [];
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
this.cleanups = [];
|
||||
this.parent = activeEffectScope;
|
||||
if (!detached && activeEffectScope) {
|
||||
this.index =
|
||||
(activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(this) - 1;
|
||||
}
|
||||
}
|
||||
EffectScope.prototype.run = function (fn) {
|
||||
if (this.active) {
|
||||
var currentEffectScope = activeEffectScope;
|
||||
try {
|
||||
activeEffectScope = this;
|
||||
return fn();
|
||||
}
|
||||
finally {
|
||||
activeEffectScope = currentEffectScope;
|
||||
}
|
||||
}
|
||||
else {
|
||||
warn$2("cannot run an inactive effect scope.");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* This should only be called on non-detached scopes
|
||||
* @internal
|
||||
*/
|
||||
EffectScope.prototype.on = function () {
|
||||
activeEffectScope = this;
|
||||
};
|
||||
/**
|
||||
* This should only be called on non-detached scopes
|
||||
* @internal
|
||||
*/
|
||||
EffectScope.prototype.off = function () {
|
||||
activeEffectScope = this.parent;
|
||||
};
|
||||
EffectScope.prototype.stop = function (fromParent) {
|
||||
if (this.active) {
|
||||
var i = void 0, l = void 0;
|
||||
for (i = 0, l = this.effects.length; i < l; i++) {
|
||||
this.effects[i].teardown();
|
||||
}
|
||||
for (i = 0, l = this.cleanups.length; i < l; i++) {
|
||||
this.cleanups[i]();
|
||||
}
|
||||
if (this.scopes) {
|
||||
for (i = 0, l = this.scopes.length; i < l; i++) {
|
||||
this.scopes[i].stop(true);
|
||||
}
|
||||
}
|
||||
// nested scope, dereference from parent to avoid memory leaks
|
||||
if (!this.detached && this.parent && !fromParent) {
|
||||
// optimized O(1) removal
|
||||
var last = this.parent.scopes.pop();
|
||||
if (last && last !== this) {
|
||||
this.parent.scopes[this.index] = last;
|
||||
last.index = this.index;
|
||||
}
|
||||
}
|
||||
this.parent = undefined;
|
||||
this.active = false;
|
||||
}
|
||||
};
|
||||
return EffectScope;
|
||||
}());
|
||||
function effectScope(detached) {
|
||||
return new EffectScope(detached);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function recordEffectScope(effect, scope) {
|
||||
if (scope === void 0) { scope = activeEffectScope; }
|
||||
if (scope && scope.active) {
|
||||
scope.effects.push(effect);
|
||||
}
|
||||
}
|
||||
function getCurrentScope() {
|
||||
return activeEffectScope;
|
||||
}
|
||||
function onScopeDispose(fn) {
|
||||
if (activeEffectScope) {
|
||||
activeEffectScope.cleanups.push(fn);
|
||||
}
|
||||
else {
|
||||
warn$2("onScopeDispose() is called when there is no active effect scope" +
|
||||
" to be associated with.");
|
||||
}
|
||||
}
|
||||
|
||||
function provide(key, value) {
|
||||
if (!currentInstance) {
|
||||
{
|
||||
@@ -3911,7 +3893,7 @@
|
||||
suspensible = _b === void 0 ? false : _b, // in Vue 3 default is true
|
||||
userOnError = source.onError;
|
||||
if (suspensible) {
|
||||
warn$2("The suspensible option for async components is not supported in Vue2. It is ignored.");
|
||||
warn$2("The suspensiblbe option for async components is not supported in Vue2. It is ignored.");
|
||||
}
|
||||
var pendingRequest = null;
|
||||
var retries = 0;
|
||||
@@ -4014,7 +3996,7 @@
|
||||
/**
|
||||
* Note: also update dist/vue.runtime.mjs when adding new exports to this file.
|
||||
*/
|
||||
var version = '2.7.16';
|
||||
var version = '2.7.14';
|
||||
/**
|
||||
* @internal type is manually declared in <root>/types/v3-define-component.d.ts
|
||||
*/
|
||||
@@ -4391,7 +4373,7 @@
|
||||
"Instead, use a data or computed property based on the prop's " +
|
||||
"value. Prop being mutated: \"".concat(key, "\""), vm);
|
||||
}
|
||||
}, true /* shallow */);
|
||||
});
|
||||
}
|
||||
// static props are already proxied on the component's prototype
|
||||
// during Vue.extend(). We only need to proxy props defined at
|
||||
@@ -4707,9 +4689,6 @@
|
||||
vm.__v_skip = true;
|
||||
// effect scope
|
||||
vm._scope = new EffectScope(true /* detached */);
|
||||
// #13134 edge case where a child component is manually created during the
|
||||
// render of a parent component
|
||||
vm._scope.parent = undefined;
|
||||
vm._scope._vm = true;
|
||||
// merge options
|
||||
if (options && options._isComponent) {
|
||||
@@ -5956,7 +5935,7 @@
|
||||
return false;
|
||||
}
|
||||
function pruneCache(keepAliveInstance, filter) {
|
||||
var cache = keepAliveInstance.cache, keys = keepAliveInstance.keys, _vnode = keepAliveInstance._vnode, $vnode = keepAliveInstance.$vnode;
|
||||
var cache = keepAliveInstance.cache, keys = keepAliveInstance.keys, _vnode = keepAliveInstance._vnode;
|
||||
for (var key in cache) {
|
||||
var entry = cache[key];
|
||||
if (entry) {
|
||||
@@ -5966,7 +5945,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
$vnode.componentOptions.children = undefined;
|
||||
}
|
||||
function pruneCacheEntry(cache, key, keys, current) {
|
||||
var entry = cache[key];
|
||||
@@ -6288,7 +6266,7 @@
|
||||
}
|
||||
var el = document.createElement(tag);
|
||||
if (tag.indexOf('-') > -1) {
|
||||
// https://stackoverflow.com/a/28210364/1070244
|
||||
// http://stackoverflow.com/a/28210364/1070244
|
||||
return (unknownElementCache[tag] =
|
||||
el.constructor === window.HTMLUnknownElement ||
|
||||
el.constructor === window.HTMLElement);
|
||||
@@ -7163,11 +7141,8 @@
|
||||
var insert_1 = ancestor.data.hook.insert;
|
||||
if (insert_1.merged) {
|
||||
// start at index 1 to avoid re-invoking component mounted hook
|
||||
// clone insert hooks to avoid being mutated during iteration.
|
||||
// e.g. for customed directives under transition group.
|
||||
var cloned = insert_1.fns.slice(1);
|
||||
for (var i_10 = 0; i_10 < cloned.length; i_10++) {
|
||||
cloned[i_10]();
|
||||
for (var i_10 = 1; i_10 < insert_1.fns.length; i_10++) {
|
||||
insert_1.fns[i_10]();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8306,8 +8281,10 @@
|
||||
}
|
||||
for (name in newStyle) {
|
||||
cur = newStyle[name];
|
||||
// ie9 setting to null has no effect, must use empty string
|
||||
setProp(el, name, cur == null ? '' : cur);
|
||||
if (cur !== oldStyle[name]) {
|
||||
// ie9 setting to null has no effect, must use empty string
|
||||
setProp(el, name, cur == null ? '' : cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
var style$1 = {
|
||||
@@ -9554,7 +9531,7 @@
|
||||
return "continue";
|
||||
}
|
||||
}
|
||||
// https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
||||
// http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
|
||||
if (conditionalComment.test(html)) {
|
||||
var conditionalEnd = html.indexOf(']>');
|
||||
if (conditionalEnd >= 0) {
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 MiB |
@@ -35,7 +35,7 @@
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
border: none;
|
||||
border-bottom: var(--brand-bg) 2px solid;
|
||||
border-bottom: var(--text-color2) 2px solid;
|
||||
background-color: var(--bg-color);
|
||||
text-overflow: ellipsis;
|
||||
min-height: 2em;
|
||||
@@ -44,7 +44,7 @@
|
||||
.form-row input:focus {
|
||||
outline: none;
|
||||
border: none;
|
||||
border-bottom: var(--brand-text) 2px solid;
|
||||
border-bottom: var(--bg-action) 2px solid;
|
||||
background-color: var(--bg-selected);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
border: none;
|
||||
font-weight: bolder;
|
||||
background: var(--bg-action);
|
||||
color: var(--text-color);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
1700
static/main-scpc.html
Normal file
1700
static/main-scpc.html
Normal file
File diff suppressed because it is too large
Load Diff
1351
static/main-tdma.html
Normal file
1351
static/main-tdma.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,17 +2,17 @@
|
||||
body {
|
||||
--text-color: #262626;
|
||||
--text-color2: #3d3d3d;
|
||||
--text-good: green;
|
||||
--text-bad: red;
|
||||
--text-good: #0CF500;
|
||||
--text-bad: #F5000C;
|
||||
|
||||
--brand-bg: #EDF3FE;
|
||||
--brand-text: #5488F7;
|
||||
--brand-bg: #B3C0D1;
|
||||
--brand-text: #0146f4;
|
||||
|
||||
--bg-color: #FEFEFE;
|
||||
--bg-selected: #F1F1F1;
|
||||
--bg-element: #a7a7a7;
|
||||
--bg-action: #5181fe;
|
||||
--bg-danger: #db2828;
|
||||
--bg-action: #81a7ff;
|
||||
--bg-danger: #ff6464;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -20,8 +20,8 @@ body {
|
||||
body {
|
||||
--text-color: #eee;
|
||||
--text-color2: #bbb;
|
||||
--text-good: greenyellow;
|
||||
--text-bad: orangered;
|
||||
--text-good: #91FF00;
|
||||
--text-bad: #FF1F2A;
|
||||
|
||||
--brand-bg: #393E50;
|
||||
--brand-text: #5F93F3;
|
||||
@@ -29,7 +29,8 @@ body {
|
||||
--bg-color: #2d2c33;
|
||||
--bg-selected: #424248;
|
||||
--bg-element: #626268;
|
||||
--bg-action: #4a70d5;
|
||||
--bg-action: #3a58af;
|
||||
--bg-danger: #ac1e1e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +53,13 @@ body {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
/* увеличение размера шрифтов */
|
||||
|
||||
* { font-size: large; }
|
||||
h1 { font-size: xxx-large; }
|
||||
h2 { font-size: xx-large; }
|
||||
h3 { font-size: larger; }
|
||||
|
||||
/* ========== MAIN STYLES ========== */
|
||||
|
||||
.value-good {
|
||||
|
Binary file not shown.
Reference in New Issue
Block a user