Compare commits
2 Commits
815a081a4c
...
e8eeee3755
Author | SHA1 | Date | |
---|---|---|---|
e8eeee3755 | |||
9037c6b329 |
@ -34,10 +34,12 @@ add_executable(terminal-web-server
|
|||||||
src/server/server.hpp
|
src/server/server.hpp
|
||||||
src/server/resource.cpp
|
src/server/resource.cpp
|
||||||
src/server/resource.h
|
src/server/resource.h
|
||||||
|
src/terminal_api_driver.h
|
||||||
|
src/terminal_api_driver.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED)
|
find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES})
|
target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} terminal-client-api)
|
||||||
target_include_directories(terminal-web-server PRIVATE ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
|
target_include_directories(terminal-web-server PRIVATE ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
2
dependencies/control_system/CMakeLists.txt
vendored
2
dependencies/control_system/CMakeLists.txt
vendored
@ -28,6 +28,8 @@ add_library(terminal-client-api SHARED
|
|||||||
client/system_client.cpp
|
client/system_client.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_include_directories(terminal-client-api PUBLIC "include/")
|
||||||
|
|
||||||
find_package(Boost 1.53.0 COMPONENTS system log log_setup REQUIRED)
|
find_package(Boost 1.53.0 COMPONENTS system log log_setup REQUIRED)
|
||||||
find_package(cereal REQUIRED)
|
find_package(cereal REQUIRED)
|
||||||
target_link_libraries(terminal-client-api ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} cereal::cereal)
|
target_link_libraries(terminal-client-api ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} cereal::cereal)
|
||||||
|
2
dependencies/control_system/client/main.cpp
vendored
2
dependencies/control_system/client/main.cpp
vendored
@ -1,5 +1,5 @@
|
|||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
#include "ControlProtoCInterface.h"
|
#include "terminal_api/ControlProtoCInterface.h"
|
||||||
#include "system_client.h"
|
#include "system_client.h"
|
||||||
|
|
||||||
std::shared_mutex mtx;
|
std::shared_mutex mtx;
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
#ifndef __CONTROL_PROTO_COMMANDS__
|
#ifndef __CONTROL_PROTO_COMMANDS__
|
||||||
#define __CONTROL_PROTO_COMMANDS__
|
#define __CONTROL_PROTO_COMMANDS__
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
#include <cstdint>
|
||||||
#define EXTERNC extern "C"
|
#define EXTERNC extern "C"
|
||||||
#else
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
#define EXTERNC extern
|
#define EXTERNC extern
|
||||||
#endif
|
#endif
|
||||||
|
|
31
src/main.cpp
31
src/main.cpp
@ -15,6 +15,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "terminal_api_driver.h"
|
||||||
|
|
||||||
|
|
||||||
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
||||||
|
|
||||||
@ -70,9 +72,11 @@ void init_logging() {
|
|||||||
log::add_common_attributes();
|
log::add_common_attributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void initResources(http::server::Server& s) {
|
static void initResources(http::server::Server& s, std::shared_ptr<api_driver::ApiDriver>& api) {
|
||||||
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/", "static/login.html", mime_types::text_html));
|
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/", "static/main.html", mime_types::text_html));
|
||||||
|
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/login", "static/login.html", mime_types::text_html));
|
||||||
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/favicon.ico", "static/favicon.png", mime_types::image_png));
|
s.resources.emplace_back(std::make_unique<http::resource::StaticFileResource>("/favicon.ico", "static/favicon.png", mime_types::image_png));
|
||||||
|
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>("/js/vue.js", "static/js/vue.js", mime_types::javascript));
|
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) {
|
s.resources.emplace_back(std::make_unique<http::resource::GenericResource>("/api/statistics", [](const auto& req, auto& rep) {
|
||||||
@ -86,6 +90,20 @@ static void initResources(http::server::Server& s) {
|
|||||||
const char* json = R"({"key":"value"})";
|
const char* json = R"({"key":"value"})";
|
||||||
rep.content.insert(rep.content.end(), json, json + strlen(json));
|
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) {
|
||||||
|
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"({"mainState":)";
|
||||||
|
result += api->loadTerminalState();
|
||||||
|
result += "}";
|
||||||
|
rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size());
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
@ -110,12 +128,14 @@ int main(int argc, char *argv[]) {
|
|||||||
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
|
BOOST_LOG_TRIVIAL(info) << "Starting RELEASE build" << argv[0];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
auto api = std::make_shared<api_driver::ApiDriver>();
|
||||||
|
|
||||||
// Initialise the server.
|
// Initialise the server.
|
||||||
std::unique_ptr<http::server::Server> s;
|
std::unique_ptr<http::server::Server> s;
|
||||||
|
|
||||||
if (strcmp(argv[1], "nossl") == 0) {
|
if (strcmp(argv[1], "nossl") == 0) {
|
||||||
s = std::make_unique<http::server::Server>(argv[2], argv[3]);
|
s = std::make_unique<http::server::Server>(argv[2], argv[3]);
|
||||||
initResources(*s);
|
initResources(*s, api);
|
||||||
s->run();
|
s->run();
|
||||||
|
|
||||||
} else if (strcmp(argv[1], "ssl") == 0) {
|
} else if (strcmp(argv[1], "ssl") == 0) {
|
||||||
@ -131,15 +151,12 @@ int main(int argc, char *argv[]) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ctx->set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::single_dh_use);
|
ctx->set_options(ssl::context::default_workarounds | ssl::context::no_sslv2 | ssl::context::single_dh_use);
|
||||||
|
|
||||||
ctx->use_certificate_chain(boost::asio::buffer(cert));
|
ctx->use_certificate_chain(boost::asio::buffer(cert));
|
||||||
|
|
||||||
ctx->use_private_key(boost::asio::buffer(key), ssl::context::file_format::pem);
|
ctx->use_private_key(boost::asio::buffer(key), ssl::context::file_format::pem);
|
||||||
|
|
||||||
ctx->use_tmp_dh(boost::asio::buffer(dh));
|
ctx->use_tmp_dh(boost::asio::buffer(dh));
|
||||||
|
|
||||||
s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
|
s = std::make_unique<http::server::Server>(argv[2], argv[3], ctx);
|
||||||
initResources(*s);
|
initResources(*s, api);
|
||||||
s->run();
|
s->run();
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;
|
std::cerr << "Unsupported ssl mode: " << argv[1] << std::endl;
|
||||||
|
73
src/terminal_api_driver.cpp
Normal file
73
src/terminal_api_driver.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include "terminal_api_driver.h"
|
||||||
|
#include "terminal_api/ControlProtoCInterface.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
api_driver::ApiDriver::ApiDriver() {
|
||||||
|
CP_Login("admin", "pass", &sid, &access);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DriverCP_GetLevelDemod(TSID sid, const char* param) {
|
||||||
|
double variable_dbl = 0;
|
||||||
|
CP_GetLevelDemod(sid, param, &variable_dbl);
|
||||||
|
return variable_dbl == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DriverCP_GetGain(TSID sid, const char* param) {
|
||||||
|
double variable_dbl = 0;
|
||||||
|
CP_GetGain(sid, param, &variable_dbl);
|
||||||
|
return variable_dbl == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* boolAsStr(bool value) {
|
||||||
|
return value ? "true" : "false";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string api_driver::ApiDriver::loadTerminalState() {
|
||||||
|
std::stringstream result;
|
||||||
|
result << "{";
|
||||||
|
|
||||||
|
result << "\"txState\":" << boolAsStr(DriverCP_GetGain(sid, "TXPWD"));
|
||||||
|
|
||||||
|
const auto sym_sync_lock = DriverCP_GetLevelDemod(sid, "sym_sync_lock"); // захват символьной
|
||||||
|
const auto freq_search_lock = DriverCP_GetLevelDemod(sid, "freq_lock"); // Захват поиска по частоте
|
||||||
|
const auto afc_lock = DriverCP_GetLevelDemod(sid, "afc_lock"); // захват ФАПЧ
|
||||||
|
const auto pkt_sync = DriverCP_GetLevelDemod(sid, "pkt_sync"); // захват пакетной синхронизации
|
||||||
|
const auto receive_active = sym_sync_lock && freq_search_lock && afc_lock && pkt_sync;
|
||||||
|
|
||||||
|
result << ",\"rxState\":" << boolAsStr(receive_active);
|
||||||
|
result << ",\"rx.sym_sync_lock\":" << boolAsStr(sym_sync_lock);
|
||||||
|
result << ",\"rx.freq_search_lock\":" << boolAsStr(freq_search_lock);
|
||||||
|
result << ",\"rx.afc_lock\":" << boolAsStr(afc_lock);
|
||||||
|
result << ",\"rx.pkt_sync\":" << boolAsStr(pkt_sync);
|
||||||
|
|
||||||
|
result << "}";
|
||||||
|
|
||||||
|
// return R"({"rxState":0,"txState":0,"testState":0})";
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string api_driver::ApiDriver::loadDeviceStatistics() {
|
||||||
|
std::stringstream result;
|
||||||
|
result << "{";
|
||||||
|
|
||||||
|
result << "\"txState\":" << boolAsStr(DriverCP_GetGain(sid, "TXPWD"));
|
||||||
|
|
||||||
|
const auto sym_sync_lock = DriverCP_GetLevelDemod(sid, "sym_sync_lock"); // захват символьной
|
||||||
|
const auto freq_search_lock = DriverCP_GetLevelDemod(sid, "freq_lock"); // Захват поиска по частоте
|
||||||
|
const auto afc_lock = DriverCP_GetLevelDemod(sid, "afc_lock"); // захват ФАПЧ
|
||||||
|
const auto pkt_sync = DriverCP_GetLevelDemod(sid, "pkt_sync"); // захват пакетной синхронизации
|
||||||
|
const auto receive_active = sym_sync_lock && freq_search_lock && afc_lock && pkt_sync;
|
||||||
|
|
||||||
|
result << ",\"rxState\":" << boolAsStr(receive_active);
|
||||||
|
result << ",\"rx.sym_sync_lock\":" << boolAsStr(sym_sync_lock);
|
||||||
|
result << ",\"rx.freq_search_lock\":" << boolAsStr(freq_search_lock);
|
||||||
|
result << ",\"rx.afc_lock\":" << boolAsStr(afc_lock);
|
||||||
|
result << ",\"rx.pkt_sync\":" << boolAsStr(pkt_sync);
|
||||||
|
|
||||||
|
result << "}";
|
||||||
|
|
||||||
|
// return R"({"rxState":0,"txState":0,"testState":0})";
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
api_driver::ApiDriver::~ApiDriver() = default;
|
37
src/terminal_api_driver.h
Normal file
37
src/terminal_api_driver.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef TERMINAL_API_DRIVER_H
|
||||||
|
#define TERMINAL_API_DRIVER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <terminal_api/ControlProtoCInterface.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace api_driver {
|
||||||
|
/**
|
||||||
|
* Это ApiDriver. Все ответы он будет возвращать в виде json.
|
||||||
|
*/
|
||||||
|
class ApiDriver {
|
||||||
|
public:
|
||||||
|
explicit ApiDriver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запросить общее состояние терминала
|
||||||
|
* @return {"txState":false,"rxState":false,"rx.sym_sync_lock":false,"rx.freq_search_lock":false,"rx.afc_lock":false,"rx.pkt_sync":false}
|
||||||
|
*/
|
||||||
|
std::string loadTerminalState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запросить статистику модулятора, демодулятора, CicC и температурные датчики
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
std::string loadDeviceStatistics();
|
||||||
|
|
||||||
|
~ApiDriver();
|
||||||
|
|
||||||
|
private:
|
||||||
|
TSID sid{0};
|
||||||
|
unsigned int access{0};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //TERMINAL_API_DRIVER_H
|
@ -3,41 +3,101 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Login</title>
|
<title>Вход</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/style.css">
|
||||||
|
<style>
|
||||||
|
#form-wrapper {
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 27em;
|
||||||
|
margin: 5em auto;
|
||||||
|
height: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
padding: 4px 0;
|
||||||
|
margin: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row * {
|
||||||
|
font-size: 1em;
|
||||||
|
text-align: left;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row label {
|
||||||
|
line-height: 2em;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input {
|
||||||
|
padding: 8px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: none;
|
||||||
|
border-bottom: var(--brand-bg) 2px solid;
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row input:focus {
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
border-bottom: var(--brand-text) 2px solid;
|
||||||
|
background-color: var(--bg-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#submit {
|
||||||
|
border: none;
|
||||||
|
font-weight: bolder;
|
||||||
|
background: var(--bg-action);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
|
||||||
<div id="app">
|
|
||||||
<h1>{{ message }}</h1>
|
|
||||||
<p>{{ now }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="status-header"></div>
|
<div id="form-wrapper">
|
||||||
|
<h1> Вход </h1>
|
||||||
|
<form method="POST" id="login-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
|
{% if message %}
|
||||||
<script src="/js/vue.js"></script>
|
<div class="form-row value-bad">
|
||||||
<script>
|
{{ message }}
|
||||||
const app = new Vue({
|
</div>
|
||||||
el: '#app',
|
{% endif %}
|
||||||
data: {
|
|
||||||
message: 'Hello Vue!',
|
|
||||||
now: new Date()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
updateDate() {
|
|
||||||
this.now = new Date();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
setInterval(() => {
|
|
||||||
this.updateDate();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// import MyComponent from './modules/header'
|
<div class="form-row">
|
||||||
// const sh = new Vue(MyComponent)
|
<label for="username">Имя пользователя</label>
|
||||||
</script>
|
<input type="text" name="username" id="username" required/>
|
||||||
</header>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="password">Пароль</label>
|
||||||
|
<input type="password" name="password" id="password" required/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<input id="submit" type="submit" value="Войти">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("username").onkeydown = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
document.getElementById("password").focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("password").onkeydown = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
document.getElementById("login-form").submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
118
static/main.html
Normal file
118
static/main.html
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Главная</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 {
|
||||||
|
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: #5fa03a;
|
||||||
|
border-bottom: 3px solid #5fa03a;
|
||||||
|
}
|
||||||
|
.tabs-body-item {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="app" hidden>
|
||||||
|
<div>
|
||||||
|
<h1>Общее состояние</h1>
|
||||||
|
<ul>
|
||||||
|
<li>Прием: {{ rxState }}</li>
|
||||||
|
<li>Передача: {{ txState }}</li>
|
||||||
|
<li>Тест: {{ testState }}</li>
|
||||||
|
<li>Последнее обновление: {{ lastUpdateTime }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tabs">
|
||||||
|
<div class="tabs-header">
|
||||||
|
<span style="font-weight:bold">RSCM-101</span>
|
||||||
|
<button class="tabs-btn" @click="activeTab = 1" :class="{ active: activeTab === 1 }">Мониторинг</button>
|
||||||
|
<button class="tabs-btn" @click="activeTab = 2" :class="{ active: activeTab === 2 }">Прием/Передача</button>
|
||||||
|
<button class="tabs-btn" @click="activeTab = 3" :class="{ active: activeTab === 3 }">Настройки CinC</button>
|
||||||
|
<button class="tabs-btn" @click="activeTab = 4" :class="{ active: activeTab === 4 }">Настройки питания и опорного генератора</button>
|
||||||
|
<button class="tabs-btn" @click="activeTab = 5" :class="{ active: activeTab === 5 }">Администрирование</button>
|
||||||
|
</div>
|
||||||
|
<div class="tabs-body">
|
||||||
|
<div class="tabs-body-item" v-show="activeTab === 1">
|
||||||
|
<div>
|
||||||
|
<h2>Статистика передачи</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tabs-body-item" v-show="activeTab === 2">
|
||||||
|
Содержимое вкладки 2
|
||||||
|
</div>
|
||||||
|
<div class="tabs-body-item" v-show="activeTab === 3">
|
||||||
|
Содержимое вкладки 3
|
||||||
|
</div>
|
||||||
|
<div class="tabs-body-item" v-show="activeTab === 4">
|
||||||
|
Содержимое вкладки 4
|
||||||
|
</div>
|
||||||
|
<div class="tabs-body-item" v-show="activeTab === 5">
|
||||||
|
Содержимое вкладки 5
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="mainState" hidden>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="status-header"></div>
|
||||||
|
|
||||||
|
<!-- Версия для разработки включает в себя возможность вывода в консоль полезных уведомлений -->
|
||||||
|
<script src="/js/vue.js"></script>
|
||||||
|
<script>
|
||||||
|
const app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
activeTab: 1,
|
||||||
|
rxState: '?',
|
||||||
|
txState: '?',
|
||||||
|
testState: '?',
|
||||||
|
lastUpdateTime: new Date()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateMainState(vals) {
|
||||||
|
this.lastUpdateTime = new Date();
|
||||||
|
this.rxState = vals["mainState"]["rxState"]
|
||||||
|
this.txState = vals["mainState"]["txState"]
|
||||||
|
this.testState = vals["mainState"]["testState"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
setInterval(() => {
|
||||||
|
fetch("/api/mainStatistics").then(async (val) => {
|
||||||
|
this.updateMainState(await val.json())
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.getElementById("app").removeAttribute("hidden")
|
||||||
|
|
||||||
|
// import MyComponent from './modules/header'
|
||||||
|
// const sh = new Vue(MyComponent)
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
105
static/style.css
105
static/style.css
@ -1,75 +1,82 @@
|
|||||||
:root {
|
/* ========== THEME ========== */
|
||||||
--text: #000;
|
body {
|
||||||
--bg: #fff;
|
--text-color: #262626;
|
||||||
|
--text-color2: #3d3d3d;
|
||||||
|
--text-good: green;
|
||||||
|
--text-bad: red;
|
||||||
|
|
||||||
--item_bg: rgb(248, 246, 243);
|
--brand-bg: #EDF3FE;
|
||||||
--second_text: #6e6d6d;
|
--brand-text: #5488F7;
|
||||||
|
|
||||||
|
--bg-color: #FEFEFE;
|
||||||
|
--bg-selected: #F1F1F1;
|
||||||
|
--bg-action: #5181fe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
/* defaults to dark theme */
|
||||||
--text: #fff;
|
body {
|
||||||
--bg: #222226;
|
--text-color: #eee;
|
||||||
|
--text-color2: #bbb;
|
||||||
|
--text-good: greenyellow;
|
||||||
|
--text-bad: orangered;
|
||||||
|
|
||||||
--item_bg: #333336;
|
--brand-bg: #393E50;
|
||||||
--second_text: #828282;
|
--brand-text: #5F93F3;
|
||||||
|
|
||||||
|
--bg-color: #2d2c33;
|
||||||
|
--bg-selected: #424248;
|
||||||
|
--bg-action: #4a70d5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
* {
|
||||||
padding: 10px;
|
background: transparent;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
display: flex;
|
background: var(--bg-color);
|
||||||
flex-direction: column;
|
margin: 0; /* браузеры зачем-то ставят свое значение */
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
background-color: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
#content {
|
||||||
text-decoration: none;
|
margin: 0.5em;
|
||||||
background-color: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
/* ========== MAIN STYLES ========== */
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
header > h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
background-color: var(--brand-bg);
|
||||||
|
padding: 0.5em;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
header * {
|
||||||
padding: 20px;
|
color: var(--brand-text);
|
||||||
width: 400px;
|
|
||||||
color: var(--text);
|
|
||||||
background-color: var(--item_bg);
|
|
||||||
border-bottom: 1px solid var(--text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item_title {
|
header > nav {
|
||||||
font-weight: 600;
|
display: flex;
|
||||||
text-align: center;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item_status {
|
header > nav > a {
|
||||||
text-align: end;
|
margin: 0.5em;
|
||||||
font-weight: 300;
|
|
||||||
color: var(--second_text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.item_icon {
|
.value-good {
|
||||||
width: 25px;
|
color: var(--text-good);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-bad {
|
||||||
|
color: var(--text-bad);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user