добавил кучу всего в вебе

This commit is contained in:
Vladislav Ostapov 2024-11-01 18:07:53 +03:00
parent 8cb2e88ba5
commit d9967b69e8
7 changed files with 534 additions and 119 deletions

15
docs.md Normal file
View File

@ -0,0 +1,15 @@
# Документация
## HTTP(s) Запросы
В этом разделе будет документация на запросы сервера и ответы сервера.
### Статические файлы
Такие расположены по следующим адресам (веб адрес - путь в файловой системе):
* `/favicon.ico` - `static/favicon.png`
* `/js/vue.js` - `static/vue.js`
* `/style.css` - `static/style.css`
На эти ресурсы можно ходить только запросом `GET`. Любой другой

View File

@ -80,6 +80,7 @@ static void initResources(http::server::Server& s, std::shared_ptr<api_driver::A
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/images/krokodil_vzryvaetsya_hd.gif", "static/krokodil.gif", mime_types::image_gif));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/style.css", "static/style.css", mime_types::text_css));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/fields.css", "static/fields.css", mime_types::text_css));
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/js/vue.js", "static/js/vue.js", mime_types::javascript));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/statistics", [](const auto& req, auto& rep) {
@ -94,7 +95,7 @@ static void initResources(http::server::Server& s, std::shared_ptr<api_driver::A
rep.content.insert(rep.content.end(), json, json + strlen(json));
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/mainStatistics", [api](const auto& req, auto& rep) {
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/statistics", [api](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
@ -107,6 +108,33 @@ static void initResources(http::server::Server& s, std::shared_ptr<api_driver::A
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/resetPacketStatistics", [api](const auto& req, auto& rep) {
if (req.method != "POST") {
http::server::stockReply(http::server::bad_request, rep);
}
api->resetPacketStatistics();
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
const std::string result = R"({"status":"ok")";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/get/settings", [api](const auto& req, auto& rep) {
if (req.method != "GET") {
http::server::stockReply(http::server::bad_request, rep);
}
rep.status = http::server::ok;
rep.headers.clear();
rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)});
std::string result = R"({"settings":)";
result += api->loadSettings();
result += "}";
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
}));
}
int main(int argc, char *argv[]) {

View File

@ -181,26 +181,8 @@ std::string api_driver::ApiDriver::loadTerminalState() {
CP_GetDemodulatorParams(sid, "dummy_cnt", &tmpu32);
result << ",\"rx.packetsDummy\":" << tmpu32;
// auto reset_btn = m_lay->addWidget(std::make_unique<Wt::WPushButton>("Обновить"), 3, 0);
// reset_btn->setMaximumSize(95, 50);
// reset_btn->clicked().connect([=]
// {
// std::string var = "";
// CP_GetDmaDebug(sid, "reset_cnt_rx", &var);
// });
}
/*
stat_cinc: {
occ: '?',
correlator: '?',
correlatorFails: '?',
freqErr: '?', freqErrAcc: '?',
channelDelay: '?'
},
*/
// формируем структуру для CinC
{
std::string tmps;
@ -256,4 +238,13 @@ std::string api_driver::ApiDriver::loadTerminalState() {
}
void api_driver::ApiDriver::resetPacketStatistics() const {
std::string tmp;
CP_GetDmaDebug(sid, "reset_cnt_rx", &tmp);
}
std::string api_driver::ApiDriver::loadSettings() {
return "{}";
}
api_driver::ApiDriver::~ApiDriver() = default;

View File

@ -20,10 +20,11 @@ namespace api_driver {
std::string loadTerminalState();
/**
* Запросить статистику модулятора, демодулятора, CicC и температурные датчики
* @return
* Сбросить статистику пакетов
*/
std::string loadDeviceStatistics();
void resetPacketStatistics() const;
std::string loadSettings();
~ApiDriver();

124
static/fields.css Normal file
View File

@ -0,0 +1,124 @@
.tabs-header {
margin: 0.5em 0;
background: var(--brand-bg);
}
.tabs-header > * {
display: inline-block;
}
.tabs-btn {
text-decoration: none;
font-size: 18px;
border: none;
padding: 10px 25px;
text-align: center;
cursor: pointer;
margin-bottom: -3px;
border-bottom: 3px solid #eee;
}
.tabs-btn.active {
color: var(--brand-text);
border-bottom: 3px solid var(--brand-text);
}
.tabs-body-item {
padding: 20px 0;
}
.indicator_bad {
background: var(--text-bad);
}
.indicator_good {
background: var(--text-good);
}
.indicator {
display: inline-block;
width: 1em;
height: 1em;
border: solid 1px var(--text-color2);
border-radius: 0.5em;
}
.nav-bar-element {
margin: 0.5em;
border-bottom: 2px solid var(--text-color2);
}
.tabs-item-flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.tabs-item-flex-container > *, .settings-set-container {
padding: 1em;
margin: 1em;
border: 1px solid var(--text-color2);
border-radius: 0.2em;
}
.tabs-item-flex-container th {
text-align: left;
}
.tabs-item-flex-container h2 {
margin-top: 0;
}
form label * {
display: block;
}
form label {
margin: 1em 0;
display: block;
background: var(--bg-selected);
color: var(--text-color2);
}
form input {
margin-top: 0.5em;
border: none;
border-bottom: solid 2px var(--text-color);
width: 100%;
box-sizing: border-box;
}
form input:focus {
outline: none;
border: none;
border-bottom: solid 2px var(--brand-text);
}
/*********************** Стили для красивых 'switch' ***********************/
.toggle-input {
display: inline-block;
position: relative;
margin: 5px 10px;
width: 50px;
height: 16px;
background-color: var(--bg-element);
border-radius: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.toggle-input input[type="checkbox"] {
display: none;
}
.toggle-input .slider {
position: absolute;
top: -4px;
left: 0;
width: 25px;
height: 25px;
background-color: var(--text-color);
border-radius: 50%;
transition: left 0.3s;
}
.toggle-input input[type="checkbox"]:checked + .slider {
left: 25px;
background-color: var(--brand-text);
}
.toggle-input input[type="checkbox"]:checked + .slider:before {
left: 10px;
}

View File

@ -5,79 +5,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RSCM-101</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<style>
.tabs-header {
margin: 0.5em 0;
background: var(--brand-bg);
}
.tabs-header > * {
display: inline-block;
}
.tabs-btn {
text-decoration: none;
font-size: 18px;
border: none;
padding: 10px 25px;
text-align: center;
cursor: pointer;
margin-bottom: -3px;
border-bottom: 3px solid #eee;
}
.tabs-btn.active {
color: var(--brand-text);
border-bottom: 3px solid var(--brand-text);
}
.tabs-body-item {
padding: 20px 0;
}
.indicator_bad {
background: var(--text-bad);
}
.indicator_good {
background: var(--text-good);
}
.indicator {
display: inline-block;
width: 1em;
height: 1em;
border: solid 1px var(--text-color2);
border-radius: 0.5em;
}
.state-bar-element {
margin: 0.5em;
border-bottom: 2px solid var(--text-color2);
}
.tabs-item-flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.tabs-item-flex-container > div {
padding: 1em;
margin: 1em;
border: 1px solid var(--text-color2);
border-radius: 0.2em;
}
.tabs-item-flex-container th {
text-align: left;
}
.tabs-item-flex-container h2 {
margin-top: 0;
}
</style>
<link rel="stylesheet" type="text/css" href="/fields.css">
</head>
<body>
<div id="app" hidden>
<div>
<span class="state-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === true, indicator_good: stat_rx.state === false, indicator: true }"></span></span>
<span class="state-bar-element">Передача: <span :class="{ indicator_bad: stat_tx.state === true, indicator_good: stat_tx.state === false, indicator: true }"></span></span>
<span class="state-bar-element">Тест: <span :class="{ indicator_bad: testState === true, indicator_good: testState === false, indicator: true }"></span></span>
<span class="nav-bar-element">Прием: <span :class="{ indicator_bad: stat_rx.state === true, indicator_good: stat_rx.state === false, indicator: true }"></span></span>
<span class="nav-bar-element">Передача: <span :class="{ indicator_bad: stat_tx.state === true, indicator_good: stat_tx.state === false, indicator: true }"></span></span>
<span class="nav-bar-element">Тест: <span :class="{ indicator_bad: testState === true, indicator_good: testState === false, indicator: true }"></span></span>
<!-- Последнее обновление: {{ lastUpdateTime }}-->
</div>
<div class="tabs">
@ -117,7 +53,7 @@
<tr><th>DUMMY</th><td>{{ stat_rx.packetsDummy }}</td></tr>
</tbody>
</table>
<button> Сброс статистики </button>
<button @click="resetPacketsStatistics()"> Сброс статистики </button>
</div>
<div>
<h2>Статистика передачи</h2>
@ -156,27 +92,260 @@
</table>
</div>
</div>
<div class="tabs-body-item tabs-item-flex-container" v-show="activeTab === 'setup'">
<div>
<h2>Настройка передатчика</h2>
<div>
<div>
...
</div>
<h3>Параметры передачи</h3>
<div>
...
</div>
<h3>Режим работы DVB-S2</h3>
<div>
...
</div>
</div>
<div class="tabs-body-item" v-show="activeTab === 'setup' && settingFetchComplete">
<h2>Настройки приема/передачи</h2>
<form method="POST" onsubmit="" class="settings-set-container">
<label>
<span>Режим работы</span>
<select v-model="param.general.mode">
<option value="scpc">SCPC</option>
<option value="cinc">CinC</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<div class="tabs-item-flex-container">
<form>
<h2>Настройки передатчика</h2>
<label>
<span>Включить передатчик</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.general.txEn" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Автоматический запуск передатчика</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.general.autoStartTx" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Режим работы модулятора</span>
<select v-model="param.general.modulatorMode">
<option value="normal">Нормальный</option>
<option value="test">Тест (CW)</option>
</select>
</label>
<label>
<span>Входные данные</span>
<select v-model="param.general.inputData">
<option value="eth">Ethernet</option>
<option value="test">Тест (CW)</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Параметры передачи</h2>
<label>
<span>Центральная частота, кГц</span>
<input v-model="param.tx.centerFreq"/>
</label>
<label>
<span>Символьная скорость, Бод</span>
<input v-model="param.tx.cymRate"/>
</label>
<label>
<span>Roll-off</span>
<select v-model="param.tx.rolloff">
<option value="5">0.05</option>
<option value="10">0.10</option>
<option value="15">0.15</option>
<option value="20">0.20</option>
<option value="25">0.25</option>
</select>
</label>
<label>
<span>Номер послед-ти Голда</span>
<select v-model="param.tx.goldan">
<option value="0">0</option>
<option value="1">1</option>
</select>
</label>
<label>
<span>Ослабление, dB</span>
<input v-model="param.tx.attenuation"/>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Режим работы DVB-S2</h2>
<label>
<span>Режим</span>
<select v-model="param.dvbs2.mode">
<option value="ccm">CCM</option>
<option value="acm">ACM</option>
</select>
</label>
<label>
<span>Размер кадра</span>
<select v-model="param.dvbs2.frameSize">
<option value="normal">normal</option>
<option value="short">short</option>
</select>
</label>
<label>
<span>Пилот-символы</span>
<select v-model="param.dvbs2.pilots">
<option value="true">pilots</option>
<option value="false">no pilots</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'ccm'">
<span>Модуляция</span>
<select v-model="param.dvbs2.ccm_modulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'ccm'">
<span>Скорость кода</span>
<select v-model="param.dvbs2.ccm_speed">
<option value="1/4">1/4</option>
<option value="1/3">1/3</option>
<option value="2/5">2/5</option>
<option value="1/2">1/2</option>
<option value="2/3">2/3</option>
<option value="3/4">3/4</option>
<option value="4/5">4/5</option>
<option value="5/6">5/6</option>
<option value="8/9">8/9</option>
<option value="9/10">9/10</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (макс. режим)</span>
<select v-model="param.dvbs2.acm_maxModulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Скорость кода (макс. режим)</span>
<select v-model="param.dvbs2.acm_maxSpeed">
<option value="1/4">1/4</option>
<option value="1/3">1/3</option>
<option value="2/5">2/5</option>
<option value="1/2">1/2</option>
<option value="2/3">2/3</option>
<option value="3/4">3/4</option>
<option value="4/5">4/5</option>
<option value="5/6">5/6</option>
<option value="8/9">8/9</option>
<option value="9/10">9/10</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Модуляция (мин. режим)</span>
<select v-model="param.dvbs2.acm_minModulation">
<option value="qpsk">QPSK</option>
<option value="8psk">8PSK</option>
<option value="16apsk">16APSK</option>
<option value="32apsk">32APSK</option>
</select>
</label>
<label v-show="param.dvbs2.mode === 'acm'">
<span>Скорость кода (мин. режим)</span>
<select v-model="param.dvbs2.acm_minSpeed">
<option value="'1/4'">1/4</option>
<option value="'1/3'">1/3</option>
<option value="'2/5'">2/5</option>
<option value="'1/2'">1/2</option>
<option value="'2/3'">2/3</option>
<option value="'3/4'">3/4</option>
<option value="'4/5'">4/5</option>
<option value="'5/6'">5/6</option>
<option value="'8/9'">8/9</option>
<option value="'9/10'">9/10</option>
</select>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Настройки авто-регулировки мощности</h2>
<label>
<span>Авто-регулировка мощности</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.acp.en" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Максимальное ослабление</span>
<input v-model="param.acp.maxAttenuation"/>
</label>
<label>
<span>Минимальное ослабление</span>
<input v-model="param.acp.minAttenuation"/>
</label>
<label>
<span>Требуемое ОСШ</span>
<input v-model="param.acp.requiredSnr"/>
</label>
<button type="submit">Сохранить</button>
</form>
<form>
<h2>Настройка приемника</h2>
<label>
<span>Режим управления усилением</span>
<select v-model="param.rx.gainMode">
<option value="auto">АРУ</option>
<option value="manual">РРУ</option>
</select>
</label>
<label v-show="param.rx.gainMode === 'manual'">
<span>Усиление, dB</span>
<input v-model="param.rx.manualGain"/>
</label>
<label>
<span>Инверсия спектра</span>
<span class="toggle-input">
<input type="checkbox" v-model="param.rx.spectrumInversion" />
<span class="slider"></span>
</span>
</label>
<label>
<span>Центральная частота, кГц</span>
<input v-model="param.rx.centerFreq"/>
</label>
<label>
<span>Символьная скорость, Бод</span>
<input v-model="param.rx.cymRate"/>
</label>
<label>
<span>Roll-off</span>
<select v-model="param.rx.rolloff">
<option value="5">0.05</option>
<option value="10">0.10</option>
<option value="15">0.15</option>
<option value="20">0.20</option>
<option value="25">0.25</option>
</select>
</label>
<label>
<span>Номер послед-ти Голда</span>
<select v-model="param.rx.goldan">
<option value="0">0</option>
<option value="1">1</option>
</select>
</label>
<label>
<span>Ослабление, dB</span>
<input v-model="param.rx.attenuation"/>
</label>
<button type="submit">Сохранить</button>
</form>
</div>
<div>
<h2>Настройка приемника</h2>
</div>
<div>
<div v-show="param.general.mode === 'cinc'">
<h2>Настройки режима CinC</h2>
<p>CinC пока нельзя настроить, но скоро разработчик это поправит)</p>
</div>
@ -189,7 +358,7 @@
<p>
Эти настройки пока недоступны, но скоро разработчик это поправит. А пока смотри на крокодила
</p>
<img src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil">
<img loading="lazy" src="/images/krokodil_vzryvaetsya_hd.gif" alt="krokodil">
</div>
</div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
@ -211,6 +380,9 @@
return defaultTab
}
// TODO: взять модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
// и прикрутить декодинг модкода
const app = new Vue({
el: '#app',
data: {
@ -256,12 +428,73 @@
adrv: 0, zync: 0, fpga: 0
},
param: {
general: {
mode: 'scpc',
txEn: false, // включен/выключен
modulatorMode: 'normal', // режим работы модулятора
autoStartTx: false, // было "режим работы передатчика"
inputData: 'eth', // входные данные: eth или test
},
tx: {
attenuation: -3.0, // ослабление
goldan: '0',
rolloff: 20,
cymRate: 100000,
centerFreq: 1200000.0,
},
dvbs2: {
mode: 'ccm',
frameSize: 'normal',
pilots: false,
// CCM
ccm_modulation: 'qpsk',
ccm_speed: '1/2',
// ACM
acm_maxModulation: 'qpsk',
acm_maxSpeed: '1/2',
acm_minModulation: 'qpsk',
acm_minSpeed: '1/2',
snrReserve: 0.5,
servicePacketPeriod: 15,
},
// авто-регулировка мощности
acp: {
en: false,
maxAttenuation: -2.0,
minAttenuation: -3.0,
requiredSnr: -10,
},
rx: {
gainMode: 'auto', // режим управления усилением
manualGain: 70, // усиление, только для ручного режима
spectrumInversion: false,
goldan: '0',
rolloff: 20,
cymRate: 100000,
centerFreq: 1200000.0,
},
buc: {
},
lnb: {},
serviceSettings: {},
},
message: "<err>",
testState: '?',
lastUpdateTime: new Date(),
activeTab: getCurrentTab()
activeTab: getCurrentTab(),
settingFetchComplete: false
},
methods: {
updateMainState(vals) {
updateStatistics(vals) {
this.lastUpdateTime = new Date();
this.isCinC = vals["mainState"]["isCinC"]
@ -306,19 +539,40 @@
this.stat_device.fpga = vals["mainState"]["device.fpga"]
this.testState = vals["mainState"]["testState"]
},
resetPacketsStatistics() {
fetch('/api/resetPacketStatistics', {
method: 'POST'
}).then(() => {
this.stat_rx.packetsOk = 0
this.stat_rx.packetsBad = 0
this.stat_rx.packetsDummy = 0
})
},
updateSettings(vals) {
this.settingFetchComplete = true
}
},
mounted() {
const doFetch = async () => {
let d = await fetch("/api/mainStatistics")
this.updateMainState(await d.json())
const doFetchStatistics = async () => {
let d = await fetch("/api/get/statistics")
this.updateStatistics(await d.json())
setTimeout(() => {
doFetch()
doFetchStatistics()
}, 1000)
}
doFetch().then(() => {})
doFetchStatistics().then(() => {})
const doFetchSettings = async () => {
let d = await fetch("/api/get/settings")
this.updateSettings(await d.json())
}
doFetchSettings().then(() => {})
document.getElementById("app").removeAttribute("hidden")
}

View File

@ -10,6 +10,7 @@ body {
--bg-color: #FEFEFE;
--bg-selected: #F1F1F1;
--bg-element: #a7a7a7;
--bg-action: #5181fe;
}
@ -26,6 +27,7 @@ body {
--bg-color: #2d2c33;
--bg-selected: #424248;
--bg-element: #626268;
--bg-action: #4a70d5;
}
}